Fragment相关
Android中展示界面一般是通过
Activity去实现的,当要实现类似商城类的首页时,就会拿出Fragment去实现对应的每个标签页,由Activity进行管理。
Fragment介绍
Fragment存在必须依附于FragmentActivity使用,并且与FragmentActivity一样,拥有自己独立生命周期,同时可以处理用户的交互动作。并且可以在一个Activity中动态的添加、替换,移除不同的Fragment,同样Fragment也可以拥有多个子Fragment并对他们进行控制,对于信息的显示有很大的便利性。
Fragment使用方式
Fragment初始化
默认提供两种初始化方式:
new XXFragment()1
DemoFragment fragment = new DemoFragment()xml 引入1
2
3
4<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.demo.fragment.DemoFragment"/>
主流使用的是第一种方法,但是并不是推荐的标准用法,如果需要有参数传入的情况下。
在Fragment中添加newInstance(),然后方法里面传入参数,以后获取Fragment就调用该方法,不要使用new方法。
1 | |
1 | |
Fragment会被重新销毁(可能因为内存不足、手机发生了配置变化),重新创建时会默认调用无参构造函数。
通过
setArguments()传递的Bundle也会被保留下来。
操作Fragment
1 | |
涉及到了以下类:
FragmentManager
在Activity通过getSupportManager获取该对象,在Fragment中通过getChildFragmentManager()获取
FragmentTransaction:Fragment操作事务
通过beginTransaction()开启事务,事务开启后就可以对Fragment进行操作
以下为几种常用的操作方法:
add():添加Fragment到Activity或Fragment中
hide()/show():隐藏和显示Fragment
remove():移除指定Fragment
replace():内部实质是调用remove()和add()完成Fragment修改过程
addToBackStack():添加当前Fragment到回退栈中,当按下返回键时可以根据回退栈进行操作
commit():提交事务,对Fragment进行操作后都需要commit()完成提交后可以生效
commitAllowingStateLoss():也是提交事务的一种,但是不会在其中抛出异常,只是跳过了检测mStateSaved是否进行了保存
确保
commit()在Activity.onPostResume()或者FragmentActivity.onResumeFragments()内调用,而且不要随意使用commitAllowingStateLoss()进行代替,不能滥用该方法。因为忽略状态丢失,Activity意外崩溃时就无法还原之前保存的数据。
添加Fragment有两种方法:
通过replace()
1 | |
通过add()配合show()、hide()
1 | |
replace()不会保留Fragment的状态,会销毁视图并重新加载,调用时保存的数据都会消失。
hide()/show()只是对Fragment进行隐藏/显示,不会影响存储的数据
Fragment生命周期
{% fullimage /images/Fragment生命周期.png,Fragment生命周期,Fragment生命周期%}onAttach():Fragment和Activity绑定时调用。Fragment附加到Activity之后,无法再次调用setArguments()
onCreate():此时可以获取到setArguments()传递过来的参数,通过Bundle获取
onCreateView():在Fragment加载布局时调用
1 | |
onActivityCreated():当Activity的onCreate()执行完成后调用
onDestoryView():Fragment中布局被移除时调用
onDetach():Fragmen和Activity解绑时调用
其中还有一个setRetainInstance()当调用到该方法时,在Actiivity重新创建时可以完全不销毁Fragment,以便Fragment中恢复数据。调用了
setRetainInstance(true)后,Fragment恢复时就会跳过onCreate()、onDestroy()生命周期回调,因此在使用该方法时,onCreate()中不要做初始化逻辑。
当因为设备配置发生变化时,
FragmentManager首先销毁队列中的fragment的视图,接着FragmentManager会检查Fragment中的retainInstance属性,如果为false,直接销毁Fragment实例;若设置为true,fragment的视图会被销毁,但fragment本身不会被销毁,处于短暂的保留状态。当Activity需要时会对其进行恢复。
Fragment通信
Fragment与Activity通信
如果Activity中包含自己管理的Fragment的引用,可以通过该引用直接访问所有public方法
Activity中未保存任何Fragment的引用,通过给每个Fragment设置
Tag或ID,后面通过调用FragmentManager.findFragmentByTag()或FragmentManager.findFragmentById()获取对应Fragment实例在Fragment通过
getActivity()获取Activity实例,然后执行操作。通过
getActivity()获取Activity实例,可能会返回null导致异常。该方法返回结果只会在onAttach()及onDetach()会非空,其他时候都有可能为空,所以可以先使用getContext()去进行替代,这样比较安全。还有一种就是定义一个全局变量,当触发
onAttach()进行赋值,后续调用直接使用该全局变量DemoFragment.java 1
2
3
4
5
6Context context ;
@Override
public void onAttach(Context context){
super.onAttach(context);
this.context = context;
}
使用接口方式(推荐做法)
DemoFragment.java 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//Fragment中定义接口
public interface ShowMsgListener{
void showMsg(String str);
}
private ShowMsgListener msgListener;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
if(activity!=null){
//获取Activity中实现的接口
msgListener=(DemoActivity)activity;
}
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement showMsgListener");
}
}
//调用接口
msgListener.showMsg("From fragment");DemoActivity.java 1
2
3
4
5
6public DemoActivity extends FragmentActivity implements ShowMsgListener{
@Override
public void showMsg(String str){
//拿来做事
}
}Fragment从Activity获取数据
Fragment与Fragment通信
Fragment之间的通信需要通过Activity进行关联,不应该是直接的进行通信。
实现通信步骤主要有以下三步:
- 定义接口
- Activity实现接口
- 在接口方法中Activity调用对应Fragment
Fragment常见问题
Fragment重叠
当宿主Activity因为被系统回收或者配置发生改变导致销毁重建时,会重新执行onCreate(),就有可能重新执行一次Fragment创建过程,然后又会新建一个Fragment。
1 | |
Fragment懒加载
懒加载:只在要使用时才去加载数据,而不是在初始化时就加载完毕。
在加载数据前需要先判断三种状态:
- 数据是否已经加载过
- Fragment是否已经调用到
onCreate() - 界面对于用户是否可见
Fragment.startActivityForResult()
Fragment启动,Activity获取结果
1 | |
1 | |
Fragment启动,Fragment获取结果
1 | |
1 | |
要求父Activity必须覆写了
onActivityResult()且调用了super.onActivityResult()。
Fragment配合ViewPager使用
一般类似资讯类、新闻类App首页都会分成多个标签,在不同的标签会有不同的内容,这个时候就需要配合ViewPager来实现内容展示,关键在于对应的fragment是否需要进行销毁。
可用的Adapter分为两种:
FragmentPagerAdapter:对于不再需要的fragment,选择调用detach(),仅销毁视图并不会销毁fragment实例。FragmentStatePagerAdapter:再切换不同fragment的时候,会把前面的fragment进行销毁,但是在系统销毁前,会存储其Fragment的Bundle,倒是需要重新创建Fragment时,可以从onSaveInstanceState()获取保存的数据。
使用FragmentStatePagerAdapter比较省内存,但是销毁重建的过程也是需要时间的,如果页面较少可以使用FragmentPageAdapter,很多的话还是推荐FragmentStatePagerAdapter。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!