Android基础之Fragment详解
一.定义
Fragment 表示 Activity中的行为或用户界面部分.
二.生命周期
1.onAttach
源码注释:
called once the fragment is associated with its activity.
当fragment第一次和它的activity开始关联时回调此方法.
2.onCreate
源码注释:
called to do initial creation of the fragment
系统在初始化创建fragment时回调的方法
3.onCreateView
源码注释:
creates and returns the view hierarchy associated with the fragment.
系统在首次绘制fragment的用户界面时调用的方法,
返回的view 必须是Fragment布局的根视图,也可以是null.
4.onActivityCreated
源码注释:
tells the fragment that its activity has
completed its own {@link Activity#onCreate Activity.onCreate()}
告诉Fragment它依赖的Activity已经完成了onCreate方法. 我们可以在这里调用在Activity的onCreate方法中初始化的一些属性.
5.onViewStateRestored
源码注释:
tells the fragment that all of the saved state of its view hierarchy has been restored
告诉Fragment View的一些状态已经被恢复(比如check box 哪条被选中),
6.onStart
源码注释:
Called when the Fragment is visible to the user.
当Fragment可以被用户看见时调用(此时还不能与用户交互)
7.onResume
源码注释:
Called when the fragment is visible to the user and actively running.
当Fragment可以被用户看见并可以交互时调用
8.onPause
源码注释:
Called when the Fragment is no longer resumed.
当Fragment不可以和用户进行交互时调用
9.onStop
源码注释:
fragment is no longer visible to the user either because its activity is being stopped or a fragment operation is modifying it in the activity.
当Framgment不可见时候调用,可能是所属的Acitivty不可见或者 Fragment的operation在Acitivty中被修改
10.onDestroyView
源码注释:
allows the fragment to clean up resources
associated with its View.
允许Fragment 清理相关View的资源
11.onDestroy
源码注释:
called to do final cleanup of the fragment's state
当Fragment不再被使用时调用,清理Fragment的状态
12.onDetach
源码注释:
called immediately prior to the fragment no longer being associated with its activity
当Fragment与Activity解除关系时调用.
三.创建Fragment
1.如何想Acitivty中添加Fragment
(1) 在Activity布局文件中声明Fragment
`<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:name="com.example.news.ArticleListFragment"
android:id="@+id/list"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>`
android:name 属性是 指定要在布局中实例化的Fragment类
(2)在代码中将Fragment添加在现有的某个ViewGroup
eg:
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); //创建事务
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit(); //提交事务
2.管理Fragment
调用Activity中的fragment,必须使用FragmentManager
常用API:
(1)通过findFragmentById()(Activtiy布局中提供UI的Fragment)或者findFragmentByTag()(提供或者不提供UI的fragment) 获取Activity中的Fragment
(2)通过 popBackStack()(模拟用户发出的返回命令)将fragment从返回栈中弹出
(3)通过addOnBackStackChangedListener()注册一个监听返回栈变化的监听器
3.执行Fragment事务
eg:
将一个fragment替换成另一个fragment,并在返回栈中保留先前状态
`// 创建新的fragment和事务
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// 替换掉原来的fragment(如果有)
// 添加事务到返回栈中
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// 提交事务
transaction.commit();`
注意事项:
(1) 必须最后调用commit()
(2) 如果想统一容器中添加多个fragment,那么添加fragment的顺序将决定它们在图层结构中的出现顺序
(3) 在每个fragment事务提交前,可以调用setTransition()来应用过渡动画
4.FragmentPagerAdapter和FragmentStatePagerAdapter的区别
-
FragmentPagerAdapter
- (1) 销毁视图,不销毁实例
- 在实例化fragment时,会优先从FragmentManager中取出缓存的fragment.
- 如果实例不存在,则会创建fragment,开始一个fragment的生命周期.
- 如果取到了缓存的fragment,则会将这个fragment重新attach,然后走fragment的onCreateView
- (2) 状态保存 空实现
- FragmentPagerAdapter 在saveState方法中空实现
- (1) 销毁视图,不销毁实例
-
总结: FragmentPagerAdapter,每次切换页面的时候会保留fragment在内存,只是detach调用,适合页面少的场景,常驻内存.
-
FragmentStatePagerAdapter
- (1) 销毁实例
- 在成员变量中设置两个集合,第一个集合用来保存fragment.destroyItem时候,会从集合中移除fragment,实例化fragment时,如果不存在那么走 getItem()方法
- 另一个集合保存的是fragment的state,destroyItem时会保存销毁fragment的状态,在实例化fragment时候取出缓存fragment的state,并未getItem返回的fragment赋值.
- (2) 状态保存 有代码逻辑实现
- FragmentStatePagerAdapter 在saveState方法中进行了状态保存
- (1) 销毁实例
-
总结: FragmentPagerAdapter,每次切换页面的时候不会保留fragment在内存,调用remove方法,适合页面多的场景,不断重建销毁.
四.无UI的Fragment
1.定义
就是没有视图布局的Fragment,可以用做后台工作线程.
2.步骤
(1)使用add(Fragment,String)从Activity中添加fragment. 这里的 String字符串是为fragment提供一个唯一的"标识"
(2)有了这个标识,我们想从Activity中获取fragment可以使用 findFragmentByTag()找到这个fragment
五.Fragment的通信
1.Fragment中调用Actiivty中的方法
(1) 如何获取到Activity的引用
- getActiivty()获取到对应Acitivity的引用
2.所在Activity嗲用Fragment的方法
(1) 如何获取Fragment的引用
- findFragmentByTag()或者findFragmentById()获取到对应Fragment的引用
3. 在Fragment中调用另外一个Fragment的方法
(1) getSupportFragmentManager()
- 首先拿到所属Activity的引用
- 然后获取getSupportFragmentManager()
- 最后根据findFragmentByTag()或者findFragmentById()获取到对应fragment的引用
(2) 回调的方式
- 创建一个接口,包含所需传递的数据类型的参数
- 定义接口实例,在事件中通过接口实例去调用接口方法
- 创建一个静态的方法提供接口的注册
- 调用函数注册实现方法,如果调用了接口方法,就会在调用方法出接受到数据.
(3) 使用本地广播
- LocalBroadcastManager
(4) 事件总线
- EventBus
六.Fragment 的切换方式
1.add()& remove() 或者replace()
- add()加入fragment时会触发 onAttach(),
- remove()fragment后,fragment状态不会被保存
- replace()替换后,会将之前的fragment的view从viewtree中删除
2.hide()&show()
- hide()只是隐藏了fragment中的view,并没有将view从viewtree中删除
- show() 将隐藏的fragment显示
3.detach()和attach()
- 使用attach()不会触发onAttach()
- detach()后,fragment的状态依然保持,再使用attach()时会再次调用onCreateView来重绘视图.
七.懒加载,参数的传递与保存
1.原因
-
一般用在ViewPager+Fragment的场景中, 因为
ViewPager有个预加载机制,会提前加载fragment两边的fragment. 如果两个fragment中加载数据操作比较耗时,那么会占用大量内存,影响性能. -
采用懒加载,就是当打开某个fragment的时候,才去加载这个fragment的数据,而不是提前加载.优点在于不仅初始化速度变快,也可以节约流量.
2.LazyLoadFragmentDemo
`
public abstract class BaseLazyFragment extends Fragment {
private static final String TAG = BaseLazyFragment.class.getSimpleName();
private boolean isPrepared;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initPrepare();
}
/**
* 第一次onResume中的调用onUserVisible避免操作与onFirstUserVisible操作重复
*/
private boolean isFirstResume = true;
@Override
public void onResume() {
super.onResume();
if (isFirstResume) {
isFirstResume = false;
return;
}
//切换界面回来,再次加载数据
if (getUserVisibleHint()) {
onUserVisible();
}
}
@Override
public void onPause() {
super.onPause();
if (getUserVisibleHint()) {
onUserInvisible();
}
}
private boolean isFirstVisible = true;
private boolean isFirstInvisible = true;
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
if (isFirstVisible) {
isFirstVisible = false;
initPrepare();
} else {
onUserVisible();
}
} else {
if (isFirstInvisible) {
isFirstInvisible = false;
onFirstUserInvisible();
} else {
onUserInvisible();
}
}
}
public synchronized void initPrepare() {
if (isPrepared) {
onFirstUserVisible();
} else {
isPrepared = true;
}
}
/**
* 第一次fragment可见(进行初始化工作)
*/
public abstract void onFirstUserVisible();
/**
* fragment可见(切换回来或者onResume)
*/
public abstract void onUserVisible();
/**
* 第一次fragment不可见(不建议在此处理事件)
*/
public abstract void onFirstUserInvisible();
/**
* fragment不可见(切换掉或者onPause)
*/
public abstract void onUserInvisible();
}
`