fragment

作者: 81bad73e9053 | 来源:发表于2016-09-20 08:22 被阅读65次

fragment
http://www.cnblogs.com/smyhvae/p/3978989.html
http://www.jianshu.com/p/ec0397cbca06
http://www.jianshu.com/p/d9143a92ad94
https://segmentfault.com/a/1190000006691830
http://www.jianshu.com/p/662c46cd3b5f
http://blog.csdn.net/shan_yao/article/details/52152804
http://blog.csdn.net/harvic880925/article/details/44917955
http://www.jianshu.com/users/2ce7b74b592b/latest_articles
http://www.bkjia.com/Androidjc/995847.html
http://android.jobbole.com/83606/
http://www.jianshu.com/p/1b824e26105b

http://www.jianshu.com/p/626229ca4dc2

http://www.jianshu.com/p/78ec81b42f92

懒加载
https://github.com/xmagicj/LazyFragment
http://www.jianshu.com/p/92befb8ab8c0
http://blog.csdn.net/feixiangdexin123087/article/details/42059975
http://www.jianshu.com/p/c5d29a0c3f4c/comments/1825997
http://blog.csdn.net/jdsjlzx/article/details/49970017

http://www.wangchenlong.org/2016/08/07/1608/076-bottom-dialog-fragment/
https://github.com/MoshDev/BackgroundViewPager


目录

概述
fragment加载到activity中
fragment的生命周期
fragment的返回栈
fragment的通信
fragment使用中会出现的问题(原文地址:http://www.jianshu.com/p/d9143a92ad94

0概述

一个新事物的产生总是为了解决旧事物存在的问题,Fragment是android3.0的产物,在android3.0之前解决手机、平板电脑的适配问题是很头疼的,对ActivityGroup有印象的朋友,应该能深深的体会到ActivityGroup包裹的多个Activity之间切换等一系列的性能问题。由此Fragment诞生了。个人总结的Fragment的使命:

解决手机、平板电脑等各种设备的适配问题
解决多个Activity之间切换性能问题
模块化,因为模块化导致复用的好处

1.Fragment加载到Activity中

        //步骤一:添加一个FragmentTransaction的实例
        FragmentManager fragmentManager =getFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();

        //步骤二:用add()方法加上Fragment的对象rightFragment 
        RightFragment rightFragment = new RightFragment();
        transaction.add(R.id.right, rightFragment); 

        //步骤三:调用commit()方法使得FragmentTransaction实例的改变生效
        transaction.commit();

代码

FragmentTransaction:
FragmentTransaction对象通过getFragmentManager().beginTransaction()获取。它将开启一个事务,用于操作一个ViewGroup中的Fragment。
FragmentTransaction的常用方法:
add():往Activity中添加一个Fragment。
remove():从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈,这个Fragment实例将会被销毁。
replace():使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体。
hide():隐藏当前的Fragment,仅仅是设为不可见,并不会销毁。
show():显示之前隐藏的Fragment
detach():将此Fragment从Activity中分离,会销毁其布局,但不会销毁该实例。
attach():将从Activity中分离的Fragment,重新关联到该Activity,重新创建其视图层次。
commit():提交一个事务。

2.Fragment的生命周期

代码

fragment的生命周期.png
进入页面
09-20 14:57:01.788: I/MainActivity(524): --MainActivity->>onCreate
09-20 14:57:01.788: I/MyFragment(524): --MyFragment->>onAttach
09-20 14:57:01.788: I/MyFragment(524): --MyFragment->>onCreate
09-20 14:57:01.788: I/MyFragment(524): --MyFragment->>onCreateView
09-20 14:57:02.018: I/MyFragment(524): --MyFragment->>onActivityCreated
09-20 14:57:02.018: I/MainActivity(524): --MainActivity->>onStart
09-20 14:57:02.018: I/MyFragment(524): --MyFragment->>onStart
09-20 14:57:02.018: I/MainActivity(524): --MainActivity->>onResume
09-20 14:57:02.018: I/MyFragment(524): --MyFragment->>onResume

按back键
09-20 14:58:25.128: I/MyFragment(524): --MyFragment->>onPause
09-20 14:58:25.128: I/MainActivity(524): --MainActivity->>onPause
09-20 14:58:25.978: I/MyFragment(524): --MyFragment->>onStop
09-20 14:58:25.978: I/MainActivity(524): --MainActivity->>onStop
09-20 14:58:25.978: I/MyFragment(524): --MyFragment->>onDestroyView
09-20 14:58:25.978: I/MyFragment(524): --MyFragment->>onDestroy
09-20 14:58:25.978: I/MyFragment(524): --MyFragment->>onDetach
09-20 14:58:25.978: I/MainActivity(524): --MainActivity->>onDestroy

3.backstack

代码

假设现在我们有两个Fragment:FragmentA和FragmentB,我们现在从FragmentA的界面跳到FragmentB,然后按Back键,发现程序是直接退出了,而不是返回到FragmentA。
如果现在想实现以下功能:从FragmentA的界面跳到FragmentB,然后按Back键,会返回到FragmentA。这个功能该怎么实现呢?这其实就利用到了返回栈的知识。

FragmentTransaction中提供了一个addToBackStack()方法,可以将一个事务添加到返回栈中

4.Fragment两种推荐的通信方案

4.1setArguments

调用方式
getFragmentManager().beginTransaction()        
.replace(android.R.id.content, ExampleFragment.newInstance(data))        
.addToBackStack(null) 
.commitAllowingStateLoss();
public class ExampleFragment extends Fragment {

    private String type;

    public static ExampleFragment newInstance(String type) {
        ExampleFragment myFragment = new ExampleFragment();
        Bundle args = new Bundle();
        args.putString("type", type);
        myFragment.setArguments(args);
        return myFragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        type=getArguments().getString("type");
        ...相应的处理代码
    }
}

4.2接口回调方式

//MainActivity实现MainFragment开放的接口 
public class MainActivity extends FragmentActivity implements FragmentListener{ 
      @override
       public void func(){ }
       ...
 } 

  public class MainFragment extends Fragment{

       public FragmentListener mListener;  
      //MainFragment开放的接口 
      public static interface FragmentListener{ 
                void func();
       }

       @Override 
      public void onAttach(Activity activity) { 
            super.onAttach(activity); 
            //对传递进来的Activity进行接口转换
             if(activity instance FragmentListener){
                 mListener = ((FragmentListener)activity); 
            }
       }
       ...
}

5.Fragment的坑

5.1内存重启:

安卓app有一种特殊情况,就是 app运行在后台的时候,系统资源紧张的时候导致把app的资源全部回收(杀死app的进程),这时把app再从后台返回到前台时,app会重启。这种情况下文简称为:“内存重启”。(屏幕旋转等配置变化也会造成当前Activity重启,本质与“内存重启”类似)
在系统要把app回收之前,系统会把Activity的状态保存下来,Activity的FragmentManager负责把Activity中的Fragment保存起来。在“内存重启”后,Activity的恢复是从栈顶逐步恢复,Fragment会在宿主Activity的onCreate
方法调用后紧接着恢复(从onAttach生命周期开始)。

5.2getActivity空指针

大多数情况下的原因:你在调用了getActivity()时,当前的Fragment已经onDetach()了宿主Activity。
比如:你在pop了Fragment之后,该Fragment的异步任务仍然在执行,并且在执行完成后调用了getActivity()方法,这样就会空指针。

解决方法:
设置一个Activity mActivity的全局变量,在onAttach(Activity activity)里赋值,使用mActivity代替getActivity(),保证Fragment即使在onDetach后,仍持有Activity的引用(有引起内存泄露的风险,但是相比空指针闪退,这种做法“安全”些),即:

protected Activity mActivity;
@Overridepublic void onAttach(Activity activity) {
   super.onAttach(activity);
   this.mActivity = activity;
}

5.3重叠

如果你add()了几个Fragment,使用show()、hide()方法控制,比如微信、QQ的底部tab等情景,如果你什么都不做的话,在“内存重启”后回到前台,app的这几个Fragment界面会重叠。

原因是FragmentManager帮我们管理Fragment,当发生“内存重启”,他会从栈底向栈顶的顺序一次性恢复Fragment;
但是因为没有保存Fragment的mHidden属性,默认为false,即show状态,所以所有Fragment都是以show的形式恢复,我们看到了界面重叠。
(如果是replace,恢复形式和Activity一致,只有当你pop之后上一个Fragment才开始重新恢复,所有使用replace不会造成重叠现象)

5.3.1第一个解决方案 findFragmentByTag

即在add()或者replace()时绑定一个tag,一般我们是用fragment的类名作为tag,然后在发生“内存重启”时,通过findFragmentByTag找到对应的Fragment,并hide()需要隐藏的fragment。

 @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity);

    TargetFragment targetFragment;
    HideFragment hideFragment;

    if (savedInstanceState != null) {  // “内存重启”时调用
        targetFragment = getSupportFragmentManager().findFragmentByTag(TargetFragment.class.getName);
        hideFragment = getSupportFragmentManager().findFragmentByTag(HideFragment.class.getName);
        // 解决重叠问题
        getFragmentManager().beginTransaction()
                .show(targetFragment)
                .hide(hideFragment)
                .commit();
    }else{  // 正常时
        targetFragment = TargetFragment.newInstance();
        hideFragment = HideFragment.newInstance();

        getFragmentManager().beginTransaction()
                .add(R.id.container, targetFragment, targetFragment.getClass().getName())
                .add(R.id,container,hideFragment,hideFragment.getClass().getName())
                .hide(hideFragment)
                .commit();
    }
}

5.3.2第二个解决方案 *****FragmentManager().getFragments()*****

getFragments可以获取到所有的fragments对象

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity);

    TargetFragment targetFragment;
    HideFragment hideFragment;

    if (savedInstanceState != null) {  // “内存重启”时调用
        List<Fragment> fragmentList = getSupportFragmentManager().getFragments();
        for (Fragment fragment : fragmentList) {
            if(fragment instanceof TartgetFragment){
               targetFragment = (TargetFragment)fragment; 
            }else if(fragment instanceof HideFragment){
               hideFragment = (HideFragment)fragment;
            }
        }
        // 解决重叠问题
        getFragmentManager().beginTransaction()
                .show(targetFragment)
                .hide(hideFragment)
                .commit();
    }else{  // 正常时
        targetFragment = TargetFragment.newInstance();
        hideFragment = HideFragment.newInstance();

        // 这里add时,tag可传可不传
        getFragmentManager().beginTransaction()
                .add(R.id.container)
                .add(R.id,container,hideFragment)
                .hide(hideFragment)
                .commit();
    }
}

5.3.3第三个解决方案:Fragment保存自己的Hidden状态

前两种方案的局限性
在Fragment嵌套的场景下,恢复会有问题,原因在于:页面重启后,在父Fragment没有初始化完成前,getChildFragmentManager()子栈内的子Fragment是空,只有父Fragment初始化完成后,子栈内的子Fragment才能正确获取到。

我们知道了发生Fragment重叠的根本原因在于FragmentState没有保存Fragment的显示状态,即mHidden,导致页面重启后,该值为默认的false,即show状态,所以导致了Fragment的重叠。

public class BaseFragment extends Fragment {
    private static final String STATE_SAVE_IS_HIDDEN = "STATE_SAVE_IS_HIDDEN";

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
    ...
    if (savedInstanceState != null) {
        boolean isSupportHidden = savedInstanceState.getBoolean(STATE_SAVE_IS_HIDDEN);

        FragmentTransaction ft = getFragmentManager().beginTransaction();
        if (isSupportHidden) {
            ft.hide(this);
        } else {
            ft.show(this);
        }
        ft.commit();
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        ...
        outState.putBoolean(STATE_SAVE_IS_HIDDEN, isHidden());
    }
}
public class MainActivity{
 @Override protected void onCreate(@Nullable Bundle savedInstanceState) {
 // 这里一定要在save为null时才加载Fragment,Fragment中onCreateView等生命周里加载根子Fragment同理
 // 因为在页面重启时,Fragment会被保存恢复,而此时再加载Fragment会重复加载,导致重叠 
   if(saveInstanceState == null){
       // 这里加载根Fragment 
    } 
  }
}

5.4出栈方法

如果你想让某一个Fragment出栈,使用remove()在加入回退栈时并不靠谱。

如果你在add的同时将Fragment加入回退栈:addToBackStack(name)的情况下,它并不能真正将Fragment从栈内移除,如果你在2秒后(确保Fragment事务已经完成)打印getSupportFragmentManager().getFragments(),会发现该Fragment依然存在,并且依然可以返回到被remove的Fragment,而且是空白页面。

如果你没有将Fragment加入回退栈,remove方法可以正常出栈。

如果你加入了回退栈,popBackStack()系列方法才能真正出栈

        case R.id.button1:
            fragment01  = new Fragment01();
            transaction.replace(R.id.right, fragment01, "fragment01");
            transaction.addToBackStack("fragment01"); 
            transaction.commit();  
            break; 
        case R.id.button2:
             transaction.remove(fragment01);
             transaction.commit();
运行效果:
点击button1,fragment1加载进来
点击button2,移出
点击back,空白界面
点击back,退出
        case R.id.button1:
           fragment01  = new Fragment01();
            transaction.replace(R.id.right, fragment01, "fragment01");
            transaction.addToBackStack("fragment01");// 添加到Activity管理的回退栈中。
            transaction.commit();  
            break; 
        case R.id.button2:
             manager.popBackStack();
运行效果:
按顺序button1,button2,
点击back,退出

5.5 popBackStack的注意事项

        case R.id.button1:
            fragment01  = new Fragment01();
            transaction.replace(R.id.right, fragment01, "fragment01");
            transaction.addToBackStack("fragment01");// 添加到Activity管理的回退栈中。
            transaction.commit();  
            break; 
        case R.id.button2:
             manager.popBackStack(); 
            break; 
        case R.id.button3:
            Fragment03 fragment03 = new Fragment03();
            transaction.replace(R.id.right, fragment03, "fragment03");
            transaction.addToBackStack("fragment03");// 添加到Activity管理的回退栈中。
            transaction.commit();  
            break;
运行效果:
点击button1,button3
点击两次button2

popBackStack的

public class FragmentTransactionBugFixHack {

    public static void reorderIndices(FragmentManager fragmentManager) {
        if (!(fragmentManager instanceof FragmentManagerImpl))
            return;
        FragmentManagerImpl fragmentManagerImpl = (FragmentManagerImpl) fragmentManager;
        if (fragmentManagerImpl.mAvailIndices != null
                && fragmentManagerImpl.mAvailIndices.size() > 1) {
            Collections.sort(fragmentManagerImpl.mAvailIndices,
                    Collections.reverseOrder());
        }
    }
}

在调用popBackStack之后立马执行

        hanler.post(new Runnable(){
            @Override
             public void run() {
                 FragmentTransactionBugFixHack.reorderIndices(fragmentManager));
             }
        });

1.对Fragment传递数据,建议使用setArguments(Bundle args),而后在onCreate中使用getArguments()取出,在 “内存重启”前,系统会帮你保存数据,不会造成数据的丢失。和Activity的Intent原理一致。
2.使用newInstance(参数)创建Fragment对象,优点是调用者只需要关系传递的哪些数据,而无需关心传递数据的Key是什么。
3.如果你需要在Fragment中用到宿主Activity对象,建议在你的基类Fragment定义一个Activity的全局变量,在onAttach中初始化,这不是最好的解决办法,但这可以有效避免一些意外Crash。下面会说到此问题的解决方案。
原文链接:http://www.jianshu.com/p/662c46cd3b5f

相关文章

网友评论

      本文标题:fragment

      本文链接:https://www.haomeiwen.com/subject/xdanettx.html