Fragment的详细解析
来源:互联网 发布:城乡统筹发展数据 编辑:程序博客网 时间:2024/05/18 01:09
介绍与应用
Fragment:可以理解为碎片,但也可以理解为片段。多个Fragment组合在一个 Activity 中来构建多窗格 UI,以及在多个 Activity 中重复使用某个Fragment。
Fragment有自己的生命周期,Fragment必须始终“嵌入”在 Activity 中使用,那么,它的生命周期受Activity的约束。只有在Activity处于活动状态才可以操作,如添加和移除。受约束的同时,fragment的生命的周期也是Activity同步的,当activity的 onPause() 方法被回调时,所在activity中的fragment也都会回调到 onPause().方法。如果该Activity 已停止,它内部没有碎片可以启动; 当活动被销毁,所有片段将被销毁。
Fragment是在Android 在 Android 3.0(API 级别 11)中引入的,主要是为了给大屏幕(如平板电脑、TV)上更加动态和灵活的 UI 设计提供支持。但目前来看,在开发过程中,其实也不只是应用在大屏幕中,手机也会经常应用,比如QQ、微信、新闻端、几乎多会用到,下面几个Tab用来切换Fragment;指导界面或者广告位的切换,也用到Fragment,已成为程序员必备技能。
关于导包:
Fragment是在3.0版本引入的,如果你使用的是3.0之前的系统,需要先导入android-support-v4的jar包才能使用Fragment功能:
import android.support.v4.app.Fragment;import android.support.v4.app.FragmentManager;
如果是在Android 3.0以上的版本使用,可以导入:
import android.app.Fragment;
但是,目前来看几乎很少使用那么低版本的系统,所以导包看项目而定吧。如果不会在Android 3.0以下使用,建议导入后者,对于初学者,避免不必要的麻烦,而且属性动画也是3.0才引用的,如果需要使用属性动画,更需要导入app下的包,(以下的测试导入的是:import android.app.Fragment;)
如何使用Fragment
开始创建Fragment
Fragment的使用与Activity非常相似,Activity需要继承的是Activity,而Fragment继承的是Fragment,而且多需要实现类似的回调方法 ,如 onCreate()、onStart()、onPause() 。其中一个不同的地方是必须使用 onCreateView() 的回调来定义Layout。
代码开始撸起来吧,如下:
public class FirstFragment extends Fragment{ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_first, container, false); } }
onCreateView( ) : 系统会在Fragment首次绘制其用户界面时调用此方法。 要Fragment绘制 UI,此方法中返回的 View 必须是Fragment布局的根视图。如果片段未提供 UI,可以返回 null。
container :是Fragment布局将插入到的父 容器(来自 Activity 的布局);
savedInstanceState: 是在恢复Fragment时,提供上一Fragment实例相关数据的 Bundle;
其中,R.layout.fragment_first 是Fragment对应的布局文件。
向Activity添加Fragment
Fragment作为 Activity 总体视图层次结构的一部分嵌入到 Activity 中。可以通过两种方式向 Activity 布局添加片段:
a. 使用XML添加fragment到activity(静态添加)
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.gotechcn.testfagment.MainActivity"> <fragment android:id="@+id/first_fragment" android:name="com.gotechcn.testfagment.FirstFragment" android:layout_width="match_parent" android:layout_height="match_parent"> </fragment></RelativeLayout>
其中fragment 标签中的 android:name 属性指定要在布局中实例化的 Fragment 类。
是的,在xml文件中静态加入,就是那么的简单。但是,虽然简单,在运行的过程中可能也有一些坑:
1.出现 ClassCastException 异常
fragment可以重用,是模块化的组件,每一个fragment的实例都与父类FragmentActivity有依赖,你可以通过定义每一个fragment的XML来实现这样的联系。 FragmentActivity 是一个在Support Library中的特殊的activity,使用它来向Level 11以下的系统进行兼容。所以,你要是导入是v4的包,那么你当前的FirstActivity必须继承FragmentActivity;如果是在app下的包,则可以继承通常的 Activity。同时,获取FragmentManager对象时,在v4包下是通过getSupportFragmentManager获取的。(这也是我上面说的,可以的话,建议不导入v4包)
2.出现IllegalArgumentException异常
每个fragment都需要一个唯一的标识符,重启 Activity 时,系统通过使用该标识符来恢复片段。所以,有可能在布局文件里面没有指定id, 可以通过三种方式为片段提供 ID:
为 android:id 属性提供唯一 ID。
为 android:tag 属性提供唯一字符串。
如果您未给以上两个属性提供值,系统会使用容器视图的 ID。
b. 运行时添加fragment到activity(动态添加)
在Activity运行时中执行片段事务(如添加、移除或替换片段),必须使用 FragmentManager 来创建一个FragmentTransaction,通过它来执行Fragment事务的操作,并且提交事务,MainActivity的代码如下:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); fragmentTransaction.add(R.id.layout, new FirstFragment()); fragmentTransaction.commit(); }
其中,add() 的第一个参数是 ViewGroup,即应该放置fragment的位置,由资源 ID 指定,第二个参数是要添加的fragment。
一旦您通过 FragmentTransaction 做出了更改,就必须调用 commit() 以使更改生效
对于实际的开发中,最为常用的是第二种方式。
Fragment的事务管理
前面也使用过,Fragment是通过FragmentManager来管理的,可以通过Activity 调用 getFragmentManager()获取。
有了事务管理,可以使用 add()、remove() 和 replace() 等方法为给定事务设置你想要执行的所有更改。最后记得必须调用 commit(),事务才会有效执行。
当你执行fragment的置换或者移除等切换动作时请注意:因为用户很可能想做后退与撤销的动作,为了让用户能够回退到之前的状态,必须在你commit FragmentTransaction 之前执行 addToBackStack(),如:
transaction.replace(R.id.fragment_container, newFragment);transaction.addToBackStack(null);transaction.commit();
其中,addToBackStack() 的方法会需要一个可选的string参数来指定这个特定的动作。除非你需要使用FragmentManager.BackStackEntry的API来做一些更加高级复杂的操作,一般是不需要传递的。
当你执行移除或者置换操作并且把这个动作添加到back stack的时候,被移除的fragment并没有被销毁而是stopped状态。如果用户执行回退的操作来恢复那个fragment,它会被restart。如果你没有把那个动作添加到back stack,那么fragment会被销毁。
通过事务,还可以执行以下操作:
- 对于每个片段事务,您都可以通过在提交前调用 setTransition() 来应用过渡动画。
- 通过findFragmentById()(对于在 Activity 布局中提供 UI 的片段)或findFragmentByTag()(对于提供或不提供 UI 的片段)获取 Activity 中存在的fragment;
- 通过 addOnBackStackChangedListener() 注册一个侦听返回栈变化的侦听器。
- 通过 popBackStack()(模拟用户发出的BACK命令)将fragment从返回栈中弹出。
Fragment与Activity的通信
在使用的过程中,Fragment与Activity、Fragment与Fragment多需要交互通信,那么他们是怎样交互的呢?
两个fragment之间没有办法直接交互,所有的Fragment-to-Fragment之间的交互都是基于activity进行操作的。
一般他们之间的通信可以使用这2种方式:
第一种:
当Fragment需要获取它所在的Activity,可以通过 getActivity() 访问 Activity 实例,并轻松地执行在 Activity 布局中查找视图等任务,如
View listView = getActivity().findViewById(R.id.list);
当 Activity 获取它包含的Fragment,调用Activity关联的FragmentManager ,使用 findFragmentById() 或 findFragmentByTag(),获取需要的Fragment之后,再执行其方法,例如:
FirstFragment fragment = (FirstFragment) getFragmentManager().findFragmentById(R.id.first_fragment);fragment.cleanFoucs();
第二种:
执行该操作的一个好方法是,在Fragment内定义一个回调接口,并要求宿主 Activity 实现它。这样Fragment可以调用改回调方法将数据传给Activity, 当 Activity 通过该接口收到回调时,可以根据需要与布局中的其他片段共享这些信息。以下是官方的一个例子:
public class HeadlinesFragment extends ListFragment { OnHeadlineSelectedListener mCallback; // Activity必须实现该接口 public interface OnHeadlineSelectedListener { public void onArticleSelected(int position); } @Override public void onAttach(Activity activity) { super.onAttach(activity); try { mCallback = (OnHeadlineSelectedListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement OnHeadlineSelectedListener"); } }}
为确保宿主 Activity 实现此接口,Fragment 的 onAttach() 回调方法(系统在向 Activity 添加片段时调用的方法)会通过转换传递到 onAttach() 中的 Activity 来实例化 OnHeadlineSelectedListener 的实例。
接口的方法会在用户点击listitem的时候被call到。Fragment使用这个callback接口来传递事件给父activity。
@Overridepublic void onListItemClick(ListView l, View v, int position, long id) { // 发送事件给宿主Activity mCallback.onArticleSelected(position);}
这样子,Fragment就完事了,Activity只要实现该接口就OK了:
public static class MainActivity extends Activity implements HeadlinesFragment.OnHeadlineSelectedListener{ public void onArticleSelected(int position) { // 做一些事... }}
Fragment的生命周期
Fragment的生命周期与Activity很相似(不熟悉Activity生命周期的可以参考这里),主要也是以这三种状态存在:
- Resumed:Fragment在运行中的 Activity 中可见;
- Paused:另一个 Activity 位于前台并具有焦点,但此Fragment所在的 Activity 仍然可见(前台 Activity 部分透明,或未覆盖整个屏幕)。
- Stopped: Fragment不可见。宿主 Activity 已停止,或Fragment已从Activity 中移除,但已添加到返回栈。 停止Fragment仍然处于活动状态(系统会保留所有状态和成员信息)。 不过,它对用户不再可见,如果Activity 被终止,它也会被终止。
前面说到,Activity的生命周期会影响Fragment的生命周期,而且具体流程又是非常相似的。有点抽象,那我们用实践来看看他们具体的生命流程。
实践中,最好是和Activity一起比较,这样能够清晰看到的区别:
Fragment的代码如下:
public class FirstFragment extends Fragment{ public static final String TAG = "Fragment Life"; @Override public void onAttach(Context context) { Log.e(TAG,"onAttach()"); super.onAttach(context); } @Override public void onCreate( Bundle savedInstanceState) { Log.e(TAG,"onCreate"); super.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Log.e(TAG,"onCreateView"); return inflater.inflate(R.layout.fragment_first, container, false); } @Override public void onActivityCreated( Bundle savedInstanceState) { Log.e(TAG,"onActivityCreated"); super.onActivityCreated(savedInstanceState); } @Override public void onStart() { Log.e(TAG,"onStart"); super.onStart(); } @Override public void onResume() { Log.e(TAG,"onResume"); super.onResume(); } @Override public void onPause() { Log.e(TAG,"onPause"); super.onPause(); } @Override public void onStop() { Log.e(TAG,"onStop"); super.onStop(); } @Override public void onDestroyView() { Log.e(TAG,"onDestroyView"); super.onDestroyView(); } @Override public void onDestroy() { Log.e(TAG,"onDestroy"); super.onDestroy(); } @Override public void onDetach() { Log.e(TAG,"onDetach"); super.onDetach(); }}
其布局很简单,如下:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:background="#66006600" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:textSize="30sp" android:text="First Fragment" android:gravity="center" android:textColor="@android:color/white" android:layout_width="match_parent" android:layout_height="match_parent"/></LinearLayout>
Activity的代码如下:
public class MainActivity extends Activity{ private static final String TAG = "Activity Life"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.e(TAG, "onCreate()..."); } @Override protected void onRestart() { super.onRestart(); Log.e(TAG, "onRestart()..."); } @Override protected void onStart() { // The activity is about to become visible. super.onStart(); Log.e(TAG, "onStart()..."); } @Override protected void onResume() { // The activity has become visible (it is now "resumed"). super.onResume(); Log.e(TAG, "onResume()..."); } @Override protected void onPause() { // Another activity is taking focus (this activity is about to be "paused"). super.onPause(); Log.e(TAG, "onPause()..."); } @Override protected void onStop() { // The activity is no longer visible (it is now "stopped") super.onStop(); Log.e(TAG, "onStop()..."); } @Override protected void onDestroy() { // The activity is about to be destroyed. super.onDestroy(); Log.e(TAG, "onDestroy()..."); }}
其布局是将Fragment添加进来,如下:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" tools:context="com.gotechcn.testfagment.MainActivity"> <fragment android:id="@+id/first_fragment" android:name="com.gotechcn.testfagment.FirstFragment" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1"> </fragment></LinearLayout>
好了,测试代码已经准备好了,我们运行起来看看log
首先,直接运行,其生命周期:
接着,按Home键,将应用暂停,其生命周期:
然后,再点击应用唤醒,其生命周期:
最后,按返回键,关闭应用,其生命周期:
如果了解Activity的生命周期,相信大家对于这个,一看也就明白了。是的,几乎是相同的流程,这不足为怪,Fragment需要嵌入在Activity才能运行,相同的生命周期更好处理他们 的关系。
官方也有形象的生命周期流程图,如图所示:
稍微总结一下他们的流程的区别:
相同点:
可以看出,Fragment所在的 Activity 的生命周期会直接影响Fragment的生命周期,其表现为,Activity 的每次生命周期回调都会引发每个Fragment的类似回调。 例如,当 Activity 收到 onPause() 时,Activity 中的每个Fragment也会收到 onPause()。
在其他方面,管理Fragment生命周期与管理 Activity 生命周期非常相似。 因此,管理 Activity 生命周期的做法同样适用于Fragment。
对于状态的保存也是一样的,假使 Activity 的进程被终止,而需要在重建 Activity 时恢复Fragment状态,也可以使用 Bundle 保留Fragment的状态。您可以在Fragment的 onSaveInstanceState() 回调期间保存状态,并可在 onCreate()、onCreateView() 或 onActivityCreated() 期间恢复状态。
不同点:
其中,以下生命周期是与Activity不同的:
onAttach(Content) :在Fragment已与 Activity 关联时调用(Content传递到此方法内)。
onCreateView(LayoutInflater, ViewGroup, Bundle)创建并返回与该Fragment相关联的视图。
onActivityCreated(Bundle):在 Activity 的 onCreate() 方法已返回时调用。
onDestroyView() 在移除与Fragment关联的视图层次结构时调用。
onDetach() 在取消Fragment与 Activity 的关联时调用。
onDestroy() 所谓做片段的状态的最后的清理工作。
Activity 生命周期与Fragment生命周期之间的最显著差异在于它们在其各自返回栈中的存储方式。 默认情况下,Activity 停止时会被放入由系统管理的 Activity 返回栈(以便用户通过返回按钮回退到 Activity)。而Fragment,仅当在移除Fragment的事务执行期间通过显式调用 addToBackStack() 保存实例时,系统才会将片段放入由宿主 Activity 管理的返回栈。
Fragment的特点大致如下:
Fragment可以被多个Activity重复的利用;
Fragment可以用通过FragmentManager管理,进行动态的添加、移除、替换等操作;
Fragment作为Activity的组成部分,其通信交互是通过调用getActivity()获取当前的Activity,Activity通过FragmentManager调用findFragmentById()来获取Fragment,或者通过回调的方式通信;
Fragment可以响应自己的事件,拥有自己的生命周期,但同时又被宿主Activity的生命周期影响;
好了,Fragment的介绍就到这里了。每次总结完,对于还是菜鸟的我来说,有那么一瞬间找到大牛的感觉,哈哈。
如有错误,欢迎指出!
- Fragment的详细解析
- Fragment详细学习解析
- Fragment的详细使用
- android的fragment解析
- Fragment的startActivityForResult详细解决方案
- Fragment的startActivityForResult详细解决方案
- Fragment的startActivityForResult详细解决方案
- Fragment的startActivityForResult详细解决方案
- Fragment的startActivityForResult详细解决方案
- Fragment的startActivityForResult详细解决方案
- Fragment的不够详细篇
- Fragment缓存问题的解析
- Fragment缓存问题的解析
- Android中Fragment的解析
- Android中Fragment的解析
- 句柄的详细解析
- URI的详细解析
- URI的详细解析
- 超大文件打开工具---10G的日志文件
- JAVA深入
- android-关于Button设置圆角之后不能点击变色的问题
- 【MySQL】MySQL数据导出(未完待续)
- QQ第三方登录
- Fragment的详细解析
- javaweb项目的jsp页面跳转问题
- border、margin、padding属性的区别
- Pandas模块2
- 程序员面试金典——矩阵元素查找
- [leetcode]ones and zeros
- C语言之位域
- 【Direct3D】之资源管理
- leetcode 566: Reshape the Matrix