Fragment专题

Fragment是什么?

Fragment是Android3.0后引入的一个新的API,他出现的初衷是为了适应大屏幕的平板电脑, 当然现在他仍然是平板APP UI设计的宠儿,而且我们普通手机开发也会加入这个Fragment, 我们可以把他看成一个小型的Activity,又称Activity片段!想想,如果一个很大的界面,我们 就一个布局,写起界面来会有多麻烦,而且如果组件多的话是管理起来也很麻烦!而使用Fragment 我们可以把屏幕划分成几块,然后进行分组,进行一个模块化的管理!从而可以更加方便的在 运行过程中动态地更新Activity的用户界面!另外Fragment并不能单独使用,他需要嵌套在Activity 中使用,尽管他拥有自己的生命周期,但是还是会受到宿主Activity的生命周期的影响,比如Activity 被destory销毁了,他也会跟着销毁!

静态使用Fragment

  1. 继承Fragmentt或者它的子类,重写onCreateView决定Fragemnt的布局。
public class FirstFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view=inflater.inflate(R.layout.fragment_first, container, false);
        TextView tv_hello= (TextView) view.findViewById(R.id.tv_hello);
        tv_hello.setText("MyFirstFragment");
        return view;
    }
}

布局代码:


  1. 在Activity中使用此Fragment,就当和使用普通的View一样。
    使用Fragment的Activity的布局代码:


    

  • Fragment是在android3.0版本引入的,如果你使用的是3.0之前的系统,需要先导入android-support-v4的jar包才能使用Fragment功能,所在的那个Activity就要继承FragmentActivity,且Fragment的方法都要使用带Support的那个(如getSupportFragmentManager()方法);本专题里面所有的Fragment都是没有兼容3.0之前版本的。

动态使用Fragment

  1. 将静态使用时的Activity的布局代码上的fragment删除。
  2. 动态加载流程图:


    Fragment专题_第1张图片
    29546434.jpg
  3. 关键代码:
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        FirstFragment fragment = new FirstFragment();
        getFragmentManager().beginTransaction().add(R.id.activity_main, fragment).commit();
    }
}

Fragment的生命周期图

Fragment专题_第2张图片
31722863.jpg
  • Fragment需要嵌套在Activity中使用,当然也可以嵌套到另外一个Fragment中,但这个被嵌套 的Fragment也是需要嵌套在Activity中的,间接地说,Fragment还是需要嵌套在Activity中! 受寄主Activity的生命周期影响,当然他也有自己的生命周期!另外不建议在Fragment里面 嵌套Fragment因为嵌套在里面的Fragment生命周期不可控!
  • 官方文档说创建Fragment时至少需要实现三个方法:onCreate( ),onCreateView( ),OnPause( ); 不过貌似只写一个onCreateView也是可以的;除了onCreateView方法,其他的所有方法如果你重写了,必须调用父类对于该方法的实现。
  • 停止状态的fragment仍然活着(所有状态和成员信息被系统保持着),然而,它对用户不再可见,并且如果activity被干掉,他也会被干掉.
  • 走一趟生命周期图:
    ①Activity加载Fragment的时候,依次调用下面的方法: onAttach -> onCreate -> onCreateView -> onActivityCreated -> onStart ->onResume
    ②当我们弄出一个悬浮的对话框风格的Activity,或者其他,就是让Fragment所在的Activity可见,但不获得焦点 onPause
    ③当对话框关闭,Activity又获得了焦点: onResume
    ④当我们替换Fragment,并调用addToBackStack()将他添加到Back栈中 onPause -> onStop -> onDestoryView !!注意,此时的Fragment还没有被销毁哦!!!
    ⑤当我们按下键盘的回退键,Fragment会再次显示出来: onCreateView -> onActivityCreated -> onStart -> onResume
    ⑥如果我们替换后,在事务commit之前没有调用addToBackStack()方法将 Fragment添加到back栈中的话;又或者退出了Activity的话,那么Fragment将会被完全结束, Fragment会进入销毁状态 onPause -> onStop -> onDestoryView -> onDestory -> onDetach

Fragment管理与Fragment事务

Fragment专题_第3张图片
97188171.jpg
  1. Fragment常用的三个类:
    android.app.Fragment 主要用于定义Fragment
    android.app.FragmentManager 主要用于在Activity中操作Fragment
    android.app.FragmentTransaction 保证一些列Fragment操作的原子性,熟悉事务这个词,一定能明白。
  2. 获取FragmentManage的方式:
    getFragmentManager() ,v4中,getSupportFragmentManager
  3. 主要的操作:都是FragmentTransaction的方法
    FragmentTransaction transaction = fm.benginTransatcion();//开启一个事务
  • transaction.add()
    往Activity中添加一个Fragment
  • transaction.remove()
    从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈(回退栈后面会详细说),这个Fragment实例将会被销毁。
  • transaction.replace()
    使用另一个Fragment替换当前的,实际上就是remove(),然后add()的合体
  • transaction.hide()
    隐藏当前的Fragment,仅仅是设为不可见,并不会销毁
  • transaction.show()
    显示之前隐藏的Fragment
  • transaction.detach()
    会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。
  • transaction.attach()
    重建view视图,附加到UI上并显示。
    transatcion.commit()//提交一个事务,在一个事务开启到提交可以进行多个的添加、移除、替换等操作。
  1. 值得注意的是:如果你喜欢使用Fragment,一定要清楚这些方法,哪个会销毁视图,哪个会销毁实例,哪个仅仅只是隐藏,这样才能更好的使用它们。
    a、比如:我在FragmentA中的EditText填了一些数据,当切换到FragmentB时,如果希望会到A还能看到数据,则适合你的就是hide和show;也就是说,希望保留用户操作的面板,你可以使用hide和show,当然了不要使劲在那new实例,进行下非null判断。
    b、再比如:我不希望保留用户操作,你可以使用remove(),然后add();或者使用replace()这个和remove,add是相同的效果。
    c、remove和detach有一点细微的区别,在不考虑回退栈的情况下,remove会销毁整个Fragment实例,而detach则只是销毁其视图结构,实例并不会被销毁。那么二者怎么取舍使用呢?如果你的当前Activity一直存在,那么在不希望保留用户操作的时候,你可以优先使用detach。
    d、在Activity的onCreate方法里面调用transaction.add() 方法添加Fragment时进行如下处理:
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.activity_main);  
        if(savedInstanceState == null) {  
            //只有当savedInstanceState == null才调用transaction.add() 方法
            transaction.add(...) ;
        } 
    }  

这样做的目的是为了避免重复实例化一个相同的新的Fragment并重复添加到Activity上,因为当某些原因(屏幕旋转,长时间处于后台等)Activity重新调用onCreate恢复页面时,它会从savedInstanceState中取出原来保存的数据(包括Fragment的实例)进行恢复,这个时候就不必重新new了,Android系统会自动把保存的数据取出并还原的。

Fragment回退栈

类似与Android系统为Activity维护一个任务栈,我们也可以通过Activity维护一个回退栈来保存每次Fragment事务发生的变化。如果你将Fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次的保存的Fragment。一旦Fragment完全从后退栈中弹出,用户再次点击后退键,则退出当前Activity。

  • 添加到回退栈的方法:FragmentTransaction.addToBackStack(String);
    eg:FragmentTransaction.addToBackStack(null);
  • 根Fragment(第一个添加到Activity的Fragment)一般不添加到回退栈当中,因为如果根Fragment绑定的Activity上面没有任何UI控件的话,点击Back键将返回上一次的保存的Fragment,这时候由于这个Fragment已经是最根部Fragment了,就会返回到空白的Activity界面了。
  • 使用replace方法切换Fragment时(replace是remove和add的合体),如果不添加事务到回退栈,前一个Fragment实例会被销毁。调用FragmentTransaction.addToBackStack(null)将当前的事务添加到回退栈后,前一个Fragment实例不会被销毁,但是视图层次依然会被销毁,即会调用前一个Fragment的onDestoryView方法和替换Fragment的onCreateView方法。

Fragment与Activity的交互

Fragment专题_第4张图片
45971686.jpg
  1. 组件获取
  • Fragment获得Activity中的组件: getActivity().findViewById(R.id.list);
  • Fragment中获得Context的方法:getActivity();
  • Activity获得Fragment中的组件(根据id和tag都可以):
    getFragmentManager().findFragmentById(R.id.fragment1);
    getFragmentManager().findFragmentByTag("fragment1") ;
  1. 数据传递
    ①Activit传递数据给Fragment:
    在Activity中创建Bundle数据包,调用Fragment实例的setArguments(bundle) 从而将Bundle数据包传给Fragment,然后Fragment中调用getArguments获得 Bundle对象,然后进行解析就可以了。
    ②Fragment传递数据给Activity
    在Fragment中定义一个内部回调接口,再让包含该Fragment的Activity实现该回调接口, Fragment就可以通过回调接口传数据了,其实就是接口回调。
    Step 1:定义一个回调接口:(Fragment中)
/*接口*/  
public interface CallBack{  
    /*定义一个获取信息的方法*/  
    public void getResult(String result);  
}  

Step 2:接口回调(Fragment中)

 /*本地私有回调接口,调用出通过回调接口方法传入*/
    private CallBack mCallback;
    /*设置回调接口*/
    public void getData(CallBack callBack){
        this.mCallback=callBack;
    }
    /*将需要传递给Activity数据的地方将数据msg传递过去*/
    mCallback.getResult(msg);

Step 3:使用接口回调方法读数据(Activity中)

/* 使用接口回调的方法获取数据 */  
fragment.getData(new FirstFragment.CallBack() {
            @Override
            public void getResult(String result) {
                //将回传数据进行处理
            }
});
  • 在Fragment定义接口的抽象方法时,你要传什么类型的数据参数就设置什么类型;
    ③Fragment与Fragment之间的数据互传
    其实这很简单,找到要接受数据的fragment对象,直接调用setArguments传数据进去就可以了 通常的话是replace时,即fragment跳转的时候传数据的,那么只需要在初始化要跳转的Fragment 后调用他的setArguments方法传入数据即可!
    如果是两个Fragment需要即时传数据,而非跳转的话,就需要先在Activity获得f1传过来的数据, 再传到f2了,就是以Activity为媒介。

使用Fragment.setArguments(Bundle bundle)来传递参数

首先我们来看下AndroidStudio提供的创建BlankFragment的工厂方法代码:

 /**
     * Use this factory method to create a new instance of
     * this fragment using the provided parameters.
     * @param param1 Parameter 1.
     * @param param2 Parameter 2.
     * @return A new instance of fragment BlankFragment.
     */
    // TODO: Rename and change types and number of parameters
    public static BlankFragment newInstance(String param1, String param2) {
        BlankFragment fragment = new BlankFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

从中我们可以看到官方建议我们用这种方法创建Fragment,这样可以传递参数给Fragment。但是为什么不推荐我们使用带参的构造方法传递参数呢?
原因就是因为当设备配置参数发生变化,如横竖屏切换时,系统会重新恢复(会从savedInstanceState中取出原来保存的数据,包括Activity、Fragment的实例进行恢复)创建Activity,同时也会重新构建它所管理的Fragment(默认调用无参的那个构造方法进行创建),原先的Fragment构造方法传递过来的参数将会全部丢失,但是通过Fragment.setArguments(Bundle bundle)方法设置的bundle会通过savedInstanceState保留下来。所以尽量使用Fragment.setArguments(Bundle bundle)方式来传递参数。

你可能感兴趣的:(Fragment专题)