Android-Fragments的使用
来源:互联网 发布:淘宝网美都袋鼠皮相 编辑:程序博客网 时间:2024/06/03 15:57
请转原文学习:Using Fragmenys in Android - Tutorial
where I found this resource:干干货分享——Android开发中学习资源大集合(译)
很素的翻译开始:
2. Fragments
2.1. 什么是fragments?
fragments是一个能够应用于Activity中的独立组件,他封装了功能,所以在activitys和layouts中更容易被复用。
fragment在activity的上下文环境中运行,但是他有自己的生命周期和自己的用户界面,定义一个fragmengts,我们也可以不需要用户界面,也就是无头fragments(headless fragments).
fragments可以被动态或者静态地添加到一个Activity中。
2.2. 使用fragments的好处
Fragments使得在不同的布局文件中复用组件变得更容易,你可以给手机构建单窗格布局,给平板构建多窗格布局。这并不仅限于用在平板上;例如,你也可以在智能手机上使用fragments去支持横屏和竖屏的布局。
一个典型的例子就是在Activity中项目(items)列表。在平板电脑上,如果你点某个列表项,就能立即在同屏幕右侧看到对应的详细内容(detail),而在智能手机上,你要跳转到一个新的详情页面(detail),下面是图形描述:
下面的讨论将假定你有两个fragments(main和detail),但是你也可以有更多个。我们也会有一个,main activity和一个detailed activity。在平板电脑上main activity包含了这两个Fragments,而在手机上只包含了main fragments。
下面的屏幕截图展示了这种用法:
2.3. 如何使用Fragments
用Fragments创建不同的布局,我们可以:
- 用一个activity,在平板上显示两个Fragments,在手机上显示一个fragemtns。在这种情况下,必要的时候要转换Fragments。这要求the fragment不能在布局文件layout file中被声明,同样地Fragments不能在运行时被移除。
- 在手机上使用不同的activities来 host每个fragment。例如,当平板上的UI实现是:在一个activity中使用了两个Fragments,在手机上使用同一个activity,但是提供另一种只包含一个fragment的布局。当你需要切换Fragments时, 需要调用另一个带有frgment的activity。(个人理解就是,在手机上一个activity用一个fragmen
第二种方法是最灵活,也是使用Fragments一般来说更可取的方法。在这种情况下,main activity检测如果detail fragment在布局文件可用。如果存在detailed fragment,the main activity通知fragment该更新自己的fragment。如果没有detail fragment, the main activity 启动 detailed activity。
3. Fragments 的生命周期
fragment的生命周期与持有他的activity的生命周期相关联
表一. Fragment生命周期
MethodDescriptiononAttach()fragemnt实例关联到Activity实例,此时这个activity还未完全初始化完onCreate()Fragment被创建onCreateView()fragment实例创建自己的view层级(fragment第一次话他自己的用户界面时调用)the inflated view成为activity view层级的一部分onActivityCreated()Activity和fragment的view层级创建完的同时他们的实例也已经创建完成。此时,view可以通过findviewById方法被找到onResume()Fragment可见并且是激活状态onPause()Fragment可见但非激活状态,例如,另一个activity覆盖在带有fragment的activity上面onStop()Fragment不可见
4. 定义和使用Fragments
4.1. 定义fragments
定义一个新的fragment,你可以继承android.app.Fragment 类或他的一个子类,例如 ListFragment, DialogFragment, PreferenceFragment或者WebViewFragment。下面的代码是一个实现的例子:
package com.example.android.rssfeed;import android.app.Fragment;import android.os.Bundle;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;public class DetailFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_rssitem_detail, container, false); return view; } public void setText(String item) { TextView view = (TextView) getView().findViewById(R.id.detailsText); view.setText(item); }}
4.2. 静态添加fragments
要使用一个新的fragment,可以静态地将他添加到一个XML布局文件中
你可以使用FragmentManager类来检查布局文件是否包含了这个fragment
DetailFragment fragment = (DetailFragment) getFragmentManager(). findFragmentById(R.id.detail_frag);if (fragment==null || ! fragment.isInLayout()) { // start new Activity }else { fragment.update(...);}如果一个fragment在XML布局文件里已经定义了, 他的android:name属性会指向相对应的类。
4.3 Fragment生命周期
fragment有他自己的生命周期。但总是和持有他的activity的生命周期相关联。
fragment的onCreate()方法在activity的onCreate()方法之后,该fragment的onCreateView()方法之前被调用。
当fragment开始创建他的用户界面时,系统就调用onCreateView(),在该方法里你可以通过Inflator类的对象调用inflate()方法来inflate一个布局文件,该布局文件作为inflate方法的一个参数。对于headless fragments没有必要实现该方法。
当持有该fragment的activity被创建后,onActivityCreated()方法在onCreateView()方法之后被调用。在这你可以初始化一个需要Context对象的对象。
Fragment并不是Context的子类,所以你必须通过getActivity()方法来获得父activity
一旦fragment可见,onStart()方法就会被调用
如果一个activity停止了,它的fragment也会停止;如果一个activity被销毁,它的fragment也会被销毁。
4.4 fragments之间的通讯
为了增加fragments的复用,fragment和fragment不应该直接和彼此沟通,fragemnts之间的每次沟通都应该通过持有他们的activity来完成。
为了达到该目的,fragment应该定义一个内部接口,然后要求activity必须实现该接口。该方法可以避免fragment对该activity的有任何了解(不懂怎么翻译,原文:This way you avoid that the fragment has any knowledge about the activity which uses it)。在fragment的onAttach()方法中,可以检查activity是否正确实现了该接口。
例如,假定你有一个fragment,该fragemnt要将一个值传递给他的父activity。可以通过下面的方法来实现。
package com.example.android.rssfeed;import android.app.Activity;import android.app.Fragment;import android.os.Bundle;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.Button;public class MyListFragment extends Fragment { private OnItemSelectedListener listener; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_rsslist_overview, container, false); Button button = (Button) view.findViewById(R.id.button1); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { updateDetail(); } }); return view; } public interface OnItemSelectedListener { public void onRssItemSelected(String link); } @Override public void onAttach(Activity activity) { super.onAttach(activity); if (activity instanceof OnItemSelectedListener) { listener = (OnItemSelectedListener) activity; } else { throw new ClassCastException(activity.toString() + " must implemenet MyListFragment.OnItemSelectedListener"); } } @Override public void onDetach() { super.onDetach(); listener = null; } // may also be triggered from the Activity public void updateDetail() { // create a string just for testing String newTime = String.valueOf(System.currentTimeMillis()); // inform the Activity about the change based // interface defintion listener.onRssItemSelected(newTime); }}
5. 在fragments中存储(持久化)数据
5.1 应用程序重启时保存数据
在fragments中你也需要存储你的应用数据。你可以把数据存储到一个中央地区。例如:
- SQLite database
- File
- 应用对象,在应用需要去处理存储的情况下
5.2配置变化时持久化数据
如果想要在配置变化时保持数据,你可以使用应用对象(application object)。
除此之外,还可以在fragments里调用setRetainState(true)方法,这种方法在配置变化时会保持fragment的实例,但是只有在fragment未加入到回退栈时才起作用。这种方式并不被Google提倡用在有用户界面的fragment上。在这种情况下,数据必须存为成员变量。
如果要被保存的数据由Bundle类支持,则可以使用onSaveInstanceState()方法将数据存放到Bundle,再在onActivityCreated()方法中找回数据。
6. 运行时修改Fragments
FragmentManager类和FragmentTransaction类允许你在activity的布局中添加,删除和替换fragments。
Fragments可以通过transaction来动态修改。要动态将fragments添加到现有的布局中,通常在要添加Fragment的XML布局文件中定义一个container(这个container就相当于在xml文件中的LinearLayout(or other layout)用来装载fragment),你可以使用一个FrameLayout 元素。
FragmentTransaction ft = getFragmentManager().beginTransaction();ft.replace(R.id.your_placehodler, new YourFragment());ft.commit();一个新的Fragment将会替换现有的Fragment,这个现有的Fragment就是先前被添加到container里的fragment。
如果你想要把transaction添加到Android的回退栈中,你可以使用addToBackStack()方法。这会使该操作添加到activity的历史栈中,通过返回按钮就可以恢复Fragment的变化。
7. Fragment transition的动画
在fragment的事物期间,可以定义动画,该动画基于Property Animation API来使用,通过调用setCustomAnimations()方法。
也可以通过调用setTransition()方法使用多种Android提供的基础动画。这些通过以FragmentTransaction为首的内容来定义。TRANSIT_FRAGMENT_*.
两种方式都允许你定义一个实体动画和一个现有动画。
8. 将Fragment transition添加到回退栈中
你可以添加一个FragmentTransition到回退栈中以便用户可以使用回退按钮倒退到这个转变。
使用FragmentTransition对象中的addToBackStack来实现。
9. Fragments的后台处理
9.1 无界面的Fragments
使用Fragments,可以不需要用户界面。
实现一个无界面的fragment,只需要在fragment里的onCreateView()方法中返回null即可。
Tip:建议在使用无界面的fragment来处理后台操作时结合setRetainInstance()方法来使用,通过这种方式,在异步操作的时候就无需处理配置变化了(configuration changes)
9.2 无界面fragments处理配置变化
无界面的fragment通常用于封装一些配置变化的状态或者用于一个后台处理任务。因此,你应该设置无界面的fragment处于被保持(retained)的状态。一个被保持的fragment在配置变化期间不会被销毁。
设置fragment为被保持,调用setRetainInsatance()方法即可。
你可以使用FragmentManager类的add()方法来添加这样的fragment到activity中。如果之后你还会查找到该Fragment,你需要给这个fragment添加一个标签tag,这样就可以使用FragmentManager中的findFragmentByTag()方法来查找到它。
Warging:onRetainNonConfigurationInstance()已废弃,should be replaced by retained headless fragments。
10. Fragments 教程
10.1 概览
接下来的教程说明了怎样使用fragment。应用将会根据横屏模式和竖屏模式分别使用带有fragment的不同布局。
在竖屏模式下,RssfeedActivity会显示一个Fragment。在这个fragment中用户可以跳转到另外一个带有fragment的activity。
在横屏模式下,RssfeedActivity会并列的显示两个fragments
1. 创建项目
按照下列内容创建一个新的Android Project。
Table 2. Android project
10.2 创建一个标准的layouts
在res/layout文件夹下创建或者改变以下布局文件。
创建一个新的布局文件:fragment_rssitem_detail.xml.该布局文件被DetailFragment使用。
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/detailsText" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="center_horizontal|center_vertical" android:layout_marginTop="20dip" android:text="Default Text" android:textAppearance="?android:attr/textAppearanceLarge" android:textSize="30dip" /></LinearLayout>
创建一个新的布局文件:fragment_rsslist_overview.xml,该布局文件用于MyListFragment类。
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Press to update" /></LinearLayout>修改现有的activity_rssfeed.xml文件。该布局是RssfeedActivity默认的布局文件,她展示两个fragments。
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal" > <fragment android:id="@+id/listFragment" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" android:layout_marginTop="?android:attr/actionBarSize" class="com.example.android.rssfeed.MyListFragment" ></fragment> <fragment android:id="@+id/detailFragment" android:layout_width="0dp" android:layout_weight="2" android:layout_height="match_parent" class="com.example.android.rssfeed.DetailFragment" > <!-- Preview: layout=@layout/details --> </fragment></LinearLayout>
10.3 创建Fragment类
接下来要创建Fragment类,由DetailFragment类开始
package com.example.android.rssfeed;import android.app.Fragment;import android.os.Bundle;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;public class DetailFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_rssitem_detail, container, false); return view; } public void setText(String item) { TextView view = (TextView) getView().findViewById(R.id.detailsText); view.setText(item); }}
创建MyListFragment类,先不管这个类的名字,他不会像他的命名一样展示一个列表,先用一个Button来代替,点击该按钮会将当前时间传递给details fragemnt。
package com.example.android.rssfeed;import android.app.Activity;import android.app.Fragment;import android.os.Bundle;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.Button;public class MyListFragment extends Fragment { private OnItemSelectedListener listener; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_rsslist_overview, container, false); Button button = (Button) view.findViewById(R.id.button1); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { updateDetail(); } }); return view; } public interface OnItemSelectedListener { public void onRssItemSelected(String link); } @Override public void onAttach(Activity activity) { super.onAttach(activity); if (activity instanceof OnItemSelectedListener) { listener = (OnItemSelectedListener) activity; } else { throw new ClassCastException(activity.toString() + " must implemenet MyListFragment.OnItemSelectedListener"); } } // May also be triggered from the Activity public void updateDetail() { // create fake data String newTime = String.valueOf(System.currentTimeMillis()); // Send data to Activity listener.onRssItemSelected(newTime); }}
10.4 RssfeedActivity
按照下面的代码来修改RssfeedActivity。
package com.example.android.rssfeed;import android.os.Bundle;import android.app.Activity;import android.view.Menu;public class RssfeedActivity extends Activity implements MyListFragment.OnItemSelectedListener{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_rssfeed); } // if the wizard generated an onCreateOptionsMenu you can delete // it, not needed for this tutorial @Override public void onRssItemSelected(String link) { DetailFragment fragment = (DetailFragment) getFragmentManager() .findFragmentById(R.id.detailFragment); if (fragment != null && fragment.isInLayout()) { fragment.setText(link); } } }
10.5 运行
运行该例子。在横屏模式或者竖屏模式都会显示两个fragments。如果点击了ListFragment中的按钮,DetailFragment上的当前时间会被更新。
11. Fragments 教程-竖屏模式的布局
11.1 为竖屏模式创建布局文件
RssfeedActivity在竖屏模式下应该使用特定的布局文件。在竖屏模式Android会检查有没有layout-port文件夹,该文件夹下是否有合适的布局文件。如果Android没有找到符合的布局文件,他就会去使用layout文件夹。
因此我们要创建res/layout-port文件夹。之后在res/layout-port文件夹下创建activity_rssfeed.xml布局文件。
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > <fragment android:id="@+id/listFragment" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="?android:attr/actionBarSize" class="com.example.android.rssfeed.MyListFragment" /></LinearLayout>
继续创建activity_detail.xml。该布局文件用户DetailActivity。注意我们本可以在res/layout文件夹下创建该文件,但是它只用于竖屏模式下,因此我们把它放到这个文件夹下(res/layout-port)。
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <fragment android:id="@+id/detailFragment" android:layout_width="match_parent" android:layout_height="match_parent" class="com.example.android.rssfeed.DetailFragment" /></LinearLayout>
11.2 DetailActivity
按照下面的类,重新创建DetailActivity
package com.example.android.rssfeed;import android.app.Activity;import android.content.res.Configuration;import android.os.Bundle;import android.widget.TextView;public class DetailActivity extends Activity { public static final String EXTRA_URL = "url"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Need to check if Activity has been switched to landscape mode // If yes, finished and go back to the start Activity if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { finish(); return; } setContentView(R.layout.activity_detail); Bundle extras = getIntent().getExtras(); if (extras != null) { String s = extras.getString(EXTRA_URL); TextView view = (TextView) findViewById(R.id.detailsText); view.setText(s); } }}
11.3 调整RssfeedActivity
调整RssfeedActivity类来显示DetailActivity以防另一个fragment不在该布局内
package com.example.android.rssfeed;import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.view.Menu;public class RssfeedActivity extends Activity implements MyListFragment.OnItemSelectedListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_rssfeed); } @Override public void onRssItemSelected(String link) { DetailFragment fragment = (DetailFragment) getFragmentManager() .findFragmentById(R.id.detailFragment); if (fragment != null && fragment.isInLayout()) { fragment.setText(link); } else { Intent intent = new Intent(getApplicationContext(), DetailActivity.class); intent.putExtra(DetailActivity.EXTRA_URL, link); startActivity(intent); } }}
11.4 运行
运行你的例子。如果在竖屏模式下运行应用,你应该只看到一个Fragment,使用Ctrl+F11快捷键来改变方向。在横向模式下你会看到两个fragments。如果你点击竖屏模式的按钮,新的DetailActivity会被重启,并且显示当前的时间。在横屏模式下两个fragment都会被看到。
- Android Fragments的使用
- Android-Fragments的使用
- Android Fragments 详细使用
- Android Fragments 详细使用
- Android Fragments 详细使用
- Android Fragments 详细使用
- Android Fragments 详细使用
- Android Fragments 详细使用
- Android Fragments 详细使用
- Android Fragments 详细使用
- Android Fragments 详细使用
- Android Fragments 详细使用
- Android Fragments 详细使用
- Android Fragments 详细使用
- Android Fragments 详细使用
- Android Fragments 详细使用
- Android Fragments 详细使用
- Android Fragments 详细使用
- 【Cracking the coding interview】Q1.8(旋转字符串)
- java基础(六)-----浅谈java中“==”和“equals”的区别
- Android禁止屏幕旋转和重启Activity
- 4种编程语言基本数据类型及其取值范围整理(C++,Java,Python,Go)
- boost::thread简介和gdb调试线程
- Android-Fragments的使用
- 学习C++ 1
- UVA - 12260 Free Goodies
- Linux命令:tar
- 同步化,同步,异步化,异步操作的区别
- Ubuntu上搭建Hadoop环境(单机模式+伪分布模式)
- C函数中,可变长参数的传递?
- stm32 串口发送数据第一字节丢失
- 数字图像处理之几何运算