由于 Fragment 确实是一个大坑,所以在学习的过程中,记录一下 Fragment 的使用,以及抠一些细节的东西来强化自己。
1 - 兼顾平板与手机的编程。
推荐郭霖大神的博文:Android手机平板两不误,使用Fragment实现兼容手机和平板的程序
简单讲就是平板的屏幕相对手机来说是比较大的,在手机上很好看的页面也许到了平板就会因为过度拉伸而变得很奇怪,这时我们的想法就是将布局改变,可以是将页面扩展一下,可以将原本两页的内容合并到一起,这就是博文展示一种解决思路。
2 - 需要对某个页面进行反复替换——动态添加碎片
2.1 第一种方案:使用replace()
2.1.1 具体操作
- 主布局中嵌入 FrameLayout 作为容器,用于将承载 Fragment 。
- 使用 FragmentManager 开启事务。
- 使用事务的
replace()
方法替换 FrameLayout 。 - 提交事务。
2.1.2 优、缺点
replace()
被替换的 Fragment 将会被销毁,执行 onDestroy()
,故而内存开销会减少。相对应的如果频繁切换需要重复构建 Fragment 实例。
2.1.3 应用示例
将替换流程封装成一个方法,调用其替换指定的 FrameLayout。
- activity_main.xml 添加容器 FrameLayout
<!-- 省略 -->
<FrameLayout
android:id="@+id/frame_layout"
...>
<!-- 省略 -->
- MainActivity.java
...
private Fragment currentFragment = null; //记录当前Fragment
...
/*
* 调用该方法替换当前Fragment
* @param fragment 替换的Fragment
*/
public void replaceFragment(Fragment fragment){
if( (currentFragment!=null) && (fragment == currentFragment) )
return;
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transcation.replace(R.id.frame_layout, fragment); //替换容器中的Fragment
transcation.commit(); //提交事务
currentFragment = fragment; //更换当前Fragment标记
}
...
2.2 第二种方案(常用方案):使用add()
、 hide()
、show()
2.2.1 具体流程
- 主布局中嵌入 FrameLayout 作为容器,用于将承载 Fragment 。
- 使用 FragmentManager 开启事务。
- 使用
add()
向指定容器添加新的Fragment。 - 隐藏其他Fragment。
- 提交事务。
2.2.2 注意事项
- 不能重复
add()
同一个 Fragment 实例,否则会报错。 - Fragment 不会被销毁,会一直保存在内存中,相互之间的切换使用
hide()
、show()
不会调用到 Fragment 生命周期的方法。 - 因为我们一般会选择FrameLayout 作为容器,所以新添加的 Fragment 会在上层。默认所有的 Fragment 都是可见的,所以会配合
hide()
来隐藏其他 Fragment。
2.2.3 优、缺点
不会回收 Fragment ,适用于频繁切换页面的场景。
2.2.4 代码
xml布局和第一种方案一样,只修改我们的 MainActivity.java
...
private Fragment currentFragment = null; //记录当前显示的Fragment
...
public void replaceFragment(Fragment fragment){
if( (currentFragment!=null) && (fragment == currentFragment) ) //该页面已经显示,直接return
return;
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
if(fragment.isAdded()){ //已经添加就只需要show
transaction.show(fragment);
}else{
transaction.add(R.id.frame_layout,fragment); //没有添加就需要add
}
if(currentFragment!=null)
transaction.hide(currentFragment); //隐藏之前的 Fragment
transcation.commit(); //提交事务
currentFragment = fragment; //更换当前Fragment标记
}
2.2.5 补充
由于 Activity 有可能被意外重启,如手机内存不足,不可见的 Activity 有可能会被暂时销毁,再打开时,Fragment 将重新构建实例并添加到容器内,这样会造成内存泄漏。相关资料参考 Fragment 常用写法 ,里面提到 MainActivity的非正常退出的重启会造成 Fragment 重复添加到容器的漏洞以及如何解决的办法。
我这里的解决写法如下,使用 tag
标志,保证了始终只有 tag
关联的页面在容器里,即使意外重启也只能使用 tag
关联的 Fragment,不怕重复添加 :
...
private Fragment currentFragment = null;
...
public void replaceFragment(Fragment fragment, String tag){
if( (currentFragment!=null) && (fragment == currentFragment) )
return;
FragmentManager manager = getSupportFragmentManager();
if(manager.findFragmentByTag(tag)!=null){
fragment = manager.findFragmentByTag(tag); //通过tag获取实例
}
FragmentTransaction transaction = manager.beginTransaction();
if(fragment.isAdded()){
transaction.show(fragment);
}else{
transaction.add(R.id.frame_layout, fragment, tag); //第三个参数就是标志
}
if(currentFragment!=null)
transaction.hide(currentFragment);
transaction.commit();
currentFragment = fragment;
}
最后强调:Fragment 一般只会有一个实例,为了开发得顺心,大家千万不要偷懒,不然出现 bug 难受的是自己哦。即使重用也要考虑仔细,能不能只用一个 Fragment 的实例完成重用。
3 - 结合 ViewPager 作导航栏,实现导航切换功能
具体可以参考我的文章:底部导航栏的实现
结合 ViewPager 之后我们就要额外关注 Fragment 的生命周期了。
正文结束,欢迎留言。