用Fragment的Hide()与Show()来代替Fragment的Replace()吧!
本博客由博主原创,未经允许,不得转载
fragment简介
Fragment说它是一种轻量级的Activity一点也不为过,安卓3.0以后,它迅速崛起,简易的操作与良好的可维护性很快受到了广大安卓爱好者的喜爱,笔者对于Fragment也有过一定时间的研究,最近开发的一些app也是基于Fragment搭建的整体框架。
Fragment的替换
笔者在决定写博客之前,上网查了一些有关Fragment切换的一些资料,大体对于网上针对于这个技术点的博客来说,大多说的还不够清晰,恰巧最近一个做平板开发的朋友问到这个问题,我整理了一下,希望以下资料可以对安卓学习的朋友有所帮助。
我们都知道,安卓在替换Fragment的时候,实际上做了两件事情:
1.将原有的Fragment移除。
getFragmentManager().beginTranstation().remove(fragment);
2.添加新的Fragment。
getFragmentManager().beginTransaction().add(containerViewId, fragment);
这两步操作已经可以完美的实现了Fragment的来回切换(注意不要忘了commit()).但是这里就出了一个问题:我如何保存Fragment的当前状态!
笔者先前写过一个小Demo,大致的内容是将QQ服务器返还的用户头像与姓名设置到Fragment的相应文本上。那么这里问题来了,每当我切换到其他的Fragment时,再切换回来,QQ的用户头像和姓名不见了!
那么此时肯定有人会说,Fragment依赖于Activity存在,我们直接保存当前界面的现场不就可以了。
不!这样不行!因为方法根本不会被调用!
原因很简单,因为onSaveInstanceState的调用遵循一个重要原则,即当系统“未经你许可”时销毁了你的activity,则 onSaveInstanceState会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据(当然你不保存那就随便你了)。
至于onRestoreInstanceState方法,需要注意的是,onSaveInstanceState方法和 onRestoreInstanceState方法“不一定”是成对的被调用的,onRestoreInstanceState被调用的前提 是,activity A“确实”被系统销毁了,而如果仅仅是停留在有这种可能性的情况下,则该方法不会被调用,例如,当正在显示activity A的时候,用户按下HOME键回到主界面,然后用户紧接着又返回到activity A,这种情况下activity A一般不会因为内存的原因被系统销毁,故activity A的onRestoreInstanceState方法不会被执行。
试想一下,实际开发中,我们获取到的第三方用户信息是要提交到服务器上的,难道我们每加载一次展现用户信息的Fragment就要请求一次服务器?
当然不可能这样,因为这样不仅会浪费用户大量的流量,还会出现加载延迟的一系列情况,甚至是用户的登录信息还未展现出来,用户的私人信息已经在其他界面展示了。
因此,我们迫切希望能有一个更好的方法,完美的解决这一类问题。
Fragment 的hide()和Show(),在进行Fragment的切换时,并不是Fragment的remove()和add(),而只是单纯的将Fragment显示和隐藏。
以下是笔者写的一个最简单易懂代码
这个是主界面的代码,我创建了一个RadioGruop,里面有三个RadioButton,点击不同的单选按钮跳转到不同的Fragment:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/ll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/rg"
android:orientation="vertical">
</LinearLayout>
<RadioGroup
android:id="@+id/rg"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_alignParentBottom="true"
android:background="#000000"
android:gravity="center"
android:orientation="horizontal" >
<RadioButton
android:id="@+id/rb1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1" />
<RadioButton
android:id="@+id/rb2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1" />
<RadioButton
android:id="@+id/rb3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1" />
</RadioGroup>
</RelativeLayout>
这是fragment01的代码,为了在最简单的情况下更直观的展示效果,我只是给每一Fragment设置不同的背景颜色。
<?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:background="#FFFF00"
android:orientation="vertical" >
</LinearLayout>
这是fragment02的代码
<?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:background="#FF0000"
android:orientation="vertical" >
</LinearLayout>
这是fragment03的代码
<?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:background="#FF00FF"
android:orientation="vertical" >
</LinearLayout>
fragment01.xml对应的实体类,里面不进行任何操作,只是最简单的加载xml布局文件,fragment02与fragment03也是一样
public class Fragment01 extends Fragment{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment01, container,false);
return view;
}
}
现在我们看主类中的操作,我在这里会把思路全部理一遍,最后把完整代码贴上去,作为一个安卓开发人员,笔者认为思路才是解决问题最重要的东西。
①:声明需要的对象,这一步不要多说了。
private Fragment01 fragment01 = new Fragment01();;
private Fragment02 fragment02;
private Fragment03 fragment03;
private FragmentManager manager;
private RadioGroup rg;
我在这里将fargment01创建出来的原因是为了让界面启动时加载某一个fragment,这点读者可以自行设定。当然如果不想在这里设置,在radioButton的onCheckListener()中设定也可以,如果在onCheckListener()设定这点读者不是很明白,接下来的代码会慢慢解释。
②:找到控件,初始化对象,这点也不必多言。
manager = getSupportFragmentManager();
rg = (RadioGroup) findViewById(R.id.rg);
rg.check(R.id.rb1);
rg.setOnCheckedChangeListener(this);
manager.beginTransaction().add(R.id.ll,fragment01).commit();
这里默认选中rb1,因此读者就可以在rb1的监听中设置加载fragment01
③:接下来便是重点,Fragment的hide()和show()的核心代码。
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
//隐藏Fragment
hideFragments();
switch (checkedId) {
case R.id.rb1:
if (fragment01 != null) { manager.beginTransaction().show(fragment01).commit();
return;
}创建
//我们只在第一次创建时将Fragmen添加进activity,接下来这步操作边不会执行,只是执行fragment的hide和show
manager.beginTransaction().add(R.id.ll, fragment01).commit();
break;
case R.id.rb2:
if (fragment02 != null) {
manager.beginTransaction().show(fragment02).commit();
return;
}
fragment02 = new Fragment02();
manager.beginTransaction().add(R.id.ll, fragment02).commit();
break;
case R.id.rb3:
if (fragment03 != null) {
manager.beginTransaction().show(fragment03).commit();
return;
}
fragment03 = new Fragment03();
manager.beginTransaction().add(R.id.ll, fragment03).commit();
break;
}
}
隐藏fragment的代码如下,在选中rb1时创建所有的fragment,只创建一个对象,以后Fragment全是复用,减小内存开销,然后将所有的Fragment隐藏,然后再onCreate方法内将默认展示的Fragment显示出来
private void hideFragments() {
if (fragment01 != null) {
manager.beginTransaction().hide(fragment01).commit();
}
if (fragment02 != null) {
manager.beginTransaction().hide(fragment02).commit();
}
if (fragment03 != null) {
manager.beginTransaction().hide(fragment03).commit();
}
}
这就是Fragment的hide()与show()的逻辑代码,笔者将其尽力简化,以达到最简单易懂且效果显著。
本博客由博主原创,未经允许,不得转载