Fragment的使用分析

来源:互联网 发布:绝世唐门进阶数据 编辑:程序博客网 时间:2024/06/05 09:27

当我们导入Fragment的时候,一般有两个包可以选择,一个是android.app.Fragment,一个是android.support.v4.app.Fragment。建议使用v4包中的Fragment,因为它可以在所有android版本中保持一致。比如Fragment嵌套Fragment这个功能在android4.2以后才开始支持,所以如果使用内置的FragmentAndroid 4.2之前的设备,会崩溃。


Fragment的生命周期


     

onAttach()。 当碎片和活动建立关联的时候调用

onCreateView()。  为碎片创建视图(加载布局)时调用

onActivityCreated()。  确保与碎片相关联的活动一定已经创建完毕的时候调用

onDestoryView()。  当与碎皮那关联的视图被移除的时候调用

onDetach()。  当碎片和活动解除关联的时候调用


添加FragmentActivity

有两种方法,一种是静态添加,直接将Fragment添加到Activity的布局中,一种是动态添加,添加到Activity的代码中。

新建MyFragment.javafragment_my.xml文件

MyFragment.java文件如下:

public class MyFragment extends Fragment {    @Nullable    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {        View view = inflater.inflate(R.layout.fragment_my, container, false);        return view;    }}
fragment_my.xml文件

<?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:gravity="center"              android:orientation="vertical">    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="这是fragment"/></LinearLayout>

1.静态添加

在Activity的布局中,添加如下标签:

    <fragment        android:id="@+id/fragment_my"        android:name="njj.fragment.MyFragment"        class="njj.fragment.MyFragment1"        android:layout_width="match_parent"        android:layout_height="match_parent"/>
有两种方式,指定name属性,或者class属性,存在一个就可以,将fragment的完整类名写进去。如果class和name属性同时存在,将会以class属性为准。

2.动态添加

首先在Activity布局中,添加一个按钮,一个LinearLayout布局,布局主要是用来存放Fragment的容器,给布局指定id

<?xml version="1.0" encoding="utf-8"?><RelativeLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context="njj.fragment.MainActivity">    <Button        android:id="@+id/addBtn"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="add Fragment"/>    <LinearLayout        android:id="@+id/fragment_rl"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:layout_below="@+id/addBtn"        android:orientation="vertical"/></RelativeLayout>
然后,点击按钮,调用如下:

        mAddBtn.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                // 第一步:通过FragmentManager获得FragmentTransaction实例                FragmentManager fragmentManager = getSupportFragmentManager();                FragmentTransaction transaction = fragmentManager.beginTransaction();                // 第二步:调用add()方法,将Fragment对象添加到指定的布局id上。                MyFragment myFragment = new MyFragment();                transaction.add(R.id.fragment_rl, myFragment);                // 第三步:调用commit()方法,提交,生效                transaction.commit();            }        });

理解了几个类的含义和使用步骤后,完善的写法如下:

                FragmentManager fragmentManager = getSupportFragmentManager();                myFragment1 = (MyFragment1) fragmentManager.findFragmentById(R.id.fragment_rl2);                if (myFragment1 == null) {                    myFragment1 = new MyFragment1();                    fragmentManager.beginTransaction().add(R.id.fragment_rl2, myFragment1).commit();                }
1.为什么需要判null?

是因为当Activity因为配置发生改变(屏幕旋转)或者内存不足被系统杀死,造成重新创建时,我们的fragment会被保存下来,但是会创建新的FragmentManager,新的FragmentManager会首先去获取保存下来的fragment队列,重建fragment队列,从而回复之前的状态。

2.add(id,fragment)中的id有何用

一方面是告诉FragmentManager,此fragment应该放的位置,其次,以后可以通过findFragmentById(id)来查找。 


删除、替换、隐藏、显示

Fragment的添加、删除、替换、隐藏,显示,都是借助FragmentTransaction来完成的,所有对Fragment的改动,都需要调用commit()来提交。

                /**                 * 删除Fragment实例                 */                transaction.remove(myFragment);                /**                 * 替换Fragment实例。执行更换的布局id,指定将要更换的Fragment实例                 */                transaction.replace(R.id.fragment_rl, myFragment)                transaction.replace(R.id.fragment_rl1, myFragment1, "MF1");                // 当指明了fragment的tag时,可以通过findFragmentByTag来得到Fragment的实例                MyFragment1 fragment1 = (MyFragment1) fragmentManager.findFragmentByTag("MF1");                /**                 * 隐藏Fragment实例                 */                transaction.hide(myFragment);                /**                 * 显示Fragment实例                 */                transaction.show(myFragment);


BackStack(回退栈)

调用addToBackStack()保存当前事务,当用户按下返回键,如果回退栈中保存之前的事务,便会执行事务回退,而不是执行activity的finish()。

                transaction.addToBackStack(null);

在事务提交之前,调用addToBackStack()方法,就ok了。


获取FragmentManager的方法区别:

getFragmentManager(); // 3.0版本之前没有fragment的api所以要借用v4的getSupportFragmentManager()来使用。

getSupportFragmentManager(); // v4包的使用方式

getChildFragmentManager(); // 前面两个只使用Activity嵌套一级Fragment,如果Fragment里面嵌套二级甚至三级Fragment,就必须使用此方法。


Fragment通讯

1.Activity->Fragment

当我们需要通过Activity向Fragment传递数据时,可以使用Bundle和Arguments来实现,首先,在Activity中创建Fragment的时候,用Bundle绑定数据,

    public MyFragment getFragmentInstance() {        Bundle bundle = new Bundle();        bundle.putString("extra", "info");        if (myFragment == null) {            myFragment = new MyFragment();            myFragment.setArguments(bundle);        }        return myFragment;    }
然后在Fragment的onCreateView中将数据取出

    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {        Bundle bundle = getArguments();        if (bundle != null) {            Log.i("niejianjian", " -> extra -> " + bundle.getString("extra"));        }        View view = inflater.inflate(R.layout.fragment_my, container, false);        return view;    }


2.Fragment->Activity

Fragment的操作想要传递到Activity,一般是使用回调的方式。

首先在Fragment中定义一个接口,用于在Activity回调:

    // 存放该Fragment的Activity必须实现此接口,否则报错。    public interface OnFragmentBtnClickListener {        void showToast(String text);    }
在Fragment中创建该接口,并且将实现我们接口的Activity,也就是存放该Fragment的Activity,强转为Listener,进行实例化。

    OnFragmentBtnClickListener listener;    @Override    public void onAttach(Context context) {        Log.i("niejianjian", " -> onAttach -> ");        super.onAttach(context);        try {            listener = (OnFragmentBtnClickListener) getActivity();        } catch (ClassCastException e) {            Log.i("niejianjian", " -> activity must implement OnFragmentBtnClickListener -> ");        }    }
然后在需要执行操作的地方,调用接口的方法

listener.showToast("Fragment Btn Click!!!");
最后在Activity中实现我们的接口,以及方法就OK了

    @Override    public void showToast(String text) {        Log.i("niejianjian", "Activity -> showToast -> " + text);    }


3.Fragment  <->  Fragment

方法一:首先我们获得宿主Activity的FragmentManager,然后用findFragmentById来根据id获取到另外一Fragment对象,然后调用其方法

                MyFragment1 fragment1 = (MyFragment1) getActivity().getSupportFragmentManager().findFragmentById(R.id.fragment_rl2);                fragment1.setTextMethod();
方法二:还是通过以宿主Activity为媒介,通过回调的方法,先将Fragment的信息传入到Activity,在从Activity调用另外一个Fragment的方法。

方法三,通过广播或者事件总线来实现。


Fragment常遇到的坑

1.getActivity()空指针

当系统内存不足时,回收了当前的的宿主Activity。如果我们在Fragment中执行异步操作,当执行完毕后,调用getActivity(),必然是会返回null。

解决方法就是在Fragment中定义一个全局变量Activity,然后使用mActivity代替getActivity(),这样就可以保证Fragment即使在onDetach后,还持有Activity的引用,不过这样会造成内存泄漏。

protected Activity mActivity;@Overridepublic void onAttach(Activity activity) {    super.onAttach(activity);    this.mActivity = activity;}/***  如果你用了support 23的库,上面的方法会提示过时,有强迫症的小伙伴,可以用下面的方法代替*/@Overridepublic void onAttach(Context context) {    super.onAttach(context);    this.mActivity = (Activity)context;


ViewPager + Fragment使用

任意创建一个Fragment

public class MyFragment0 extends Fragment {    @Nullable    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {        View view = inflater.inflate(R.layout.fragment_my0, container, false);        return view;    }}
然后将其多复制几个,用于添加到ViewPager上。

在Activity的布局中添加Viewpager控件

<?xml version="1.0" encoding="utf-8"?><RelativeLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="njj.fragment.MainActivity">    <android.support.v4.view.ViewPager        android:id="@+id/viewpager"        android:layout_width="match_parent"        android:layout_height="match_parent"/></RelativeLayout>
最后在Activity中实现,将Fragment添加到ViewPager中

package njj.fragment;import android.os.Bundle;import android.support.v4.app.Fragment;import android.support.v4.app.FragmentManager;import android.support.v4.app.FragmentPagerAdapter;import android.support.v4.view.ViewPager;import android.support.v7.app.AppCompatActivity;import android.view.ViewGroup;import android.view.Window;import java.util.ArrayList;import java.util.List;public class MainActivity extends AppCompatActivity {    private ViewPager mViewPager;    private List<Fragment> mFragmentList;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        requestWindowFeature(Window.FEATURE_NO_TITLE);        setContentView(R.layout.activity_main);        mFragmentList = new ArrayList<>();        mFragmentList.add(new MyFragment0());        mFragmentList.add(new MyFragment1());        mFragmentList.add(new MyFragment2());        mFragmentList.add(new MyFragment3());        mViewPager = (ViewPager) findViewById(R.id.viewpager);        mViewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager()));        // 预加载的页面数,(前后)默认是1,最少也是1,设置0,也是1        mViewPager.setOffscreenPageLimit(1);    }    class MyPagerAdapter extends FragmentPagerAdapter {        public MyPagerAdapter(FragmentManager fm) {            super(fm);        }        @Override        public Fragment getItem(int position) {            return mFragmentList.get(position);        }        @Override        public int getCount() {            return mFragmentList.size();        }        /**         * 页面更新后调用此方法。可以用于处理tab视图的切换         * http://blog.csdn.net/harvic880925/article/details/38487149         */        @Override        public void finishUpdate(ViewGroup container) {            super.finishUpdate(container);//            Log.i("niejianjian"," ->  ->" + mViewPager.getCurrentItem());        }    }}
此时,ViewPager + Fragment的简单实用就大功告成了。

ViewPager设置的Adapter有两种,分别是FragmentPagerAdapter和FragmentStatePagerAdapter,这两个的区别如下。

FragmentPagerAdapter:对于不再需要的Fragment,仅仅只会调用onDestoryView方法,也就是至销毁视图不销毁Fragment,所以不会执行onDetach方法。

FragmentStatePagerAdapter:会销毁不再需要的Fragment,一直调用到onDetach方法,将Activity和Fragment解绑。销毁时,会调用onSaveInstanceState(Bundle bundle)方法通过bundle将信息保存下来,当用户切换回来,可以通过bundle恢复生成新的fragment,也就是说,我们可以在onSaveInstanceState方法中保存一些数据,在onCreate中进行恢复创建。

不再需要的Fragment并不是不可见的Fragment,是否需要,要根据ViewPager当前显示哪个Fragment和Viewpager的setOffscreenPageLimit()方法设置的limit来共同决定的。

我们用声明周期来说明这些。我们将四个Fragment都实现声明周期方法:

public class MyFragment0 extends Fragment {    @Nullable    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {        Log.i("niejianjian","MyFragment0 -> onCreateView ->");        View view = inflater.inflate(R.layout.fragment_my0, container, false);        return view;    }    @Override    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {        super.onViewCreated(view, savedInstanceState);        Log.i("niejianjian","MyFragment0 -> onViewCreated ->");    }    @Override    public void onAttach(Context context) {        super.onAttach(context);        Log.i("niejianjian","MyFragment0 -> onAttach ->");    }    @Override    public void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Log.i("niejianjian","MyFragment0 -> onCreate ->");    }    @Override    public void onActivityCreated(@Nullable Bundle savedInstanceState) {        super.onActivityCreated(savedInstanceState);        Log.i("niejianjian","MyFragment0 -> onActivityCreated ->");    }    @Override    public void onStart() {        super.onStart();        Log.i("niejianjian","MyFragment0 -> onStart ->");    }    @Override    public void onResume() {        super.onResume();        Log.i("niejianjian","MyFragment0 -> onResume ->");    }    @Override    public void onPause() {        super.onPause();        Log.i("niejianjian","MyFragment0 -> onPause ->");    }    @Override    public void onStop() {        super.onStop();        Log.i("niejianjian","MyFragment0 -> onStop ->");    }    @Override    public void onDestroyView() {        super.onDestroyView();        Log.i("niejianjian","MyFragment0 -> onDestroyView ->");    }    @Override    public void onDetach() {        super.onDetach();        Log.i("niejianjian","MyFragment0 -> onDetach ->");    }}

当我们设置setOffscreenPageLimit(1)的值为1的时候,当前页面的前后一个页面都是加载的。

启动应用,默认显示的是MyFragment0,生命周期显示如下:

12-29 10:38:46.130 25672-25672/njj.fragmeny I/niejianjian: MyFragment0 -> onAttach ->12-29 10:38:46.130 25672-25672/njj.fragmeny I/niejianjian: MyFragment0 -> onCreate ->12-29 10:38:46.130 25672-25672/njj.fragmeny I/niejianjian: MyFragment0 -> onCreateView ->12-29 10:38:46.130 25672-25672/njj.fragmeny I/niejianjian: MyFragment0 -> onViewCreated ->12-29 10:38:46.130 25672-25672/njj.fragmeny I/niejianjian: MyFragment0 -> onActivityCreated ->12-29 10:38:46.130 25672-25672/njj.fragmeny I/niejianjian: MyFragment0 -> onStart ->12-29 10:38:46.130 25672-25672/njj.fragmeny I/niejianjian: MyFragment0 -> onResume ->12-29 10:38:46.130 25672-25672/njj.fragmeny I/niejianjian: MyFragment1 -> onAttach ->12-29 10:38:46.130 25672-25672/njj.fragmeny I/niejianjian: MyFragment1 -> onCreate ->12-29 10:38:46.130 25672-25672/njj.fragmeny I/niejianjian: MyFragment1 -> onCreateView ->12-29 10:38:46.140 25672-25672/njj.fragmeny I/niejianjian: MyFragment1 -> onActivityCreated ->12-29 10:38:46.140 25672-25672/njj.fragmeny I/niejianjian: MyFragment1 -> onStart ->12-29 10:38:46.140 25672-25672/njj.fragmeny I/niejianjian: MyFragment1 -> onResume ->
我们看到,此时MyFragment1也加载完了,然后我们滑动到第二个页面,也就是显示MyFragment1页面,

12-29 10:41:36.970 25672-25672/njj.fragmeny I/niejianjian: MyFragment2 -> onAttach ->12-29 10:41:36.970 25672-25672/njj.fragmeny I/niejianjian: MyFragment2 -> onCreate ->12-29 10:41:36.970 25672-25672/njj.fragmeny I/niejianjian: MyFragment2 -> onCreateView ->12-29 10:41:36.980 25672-25672/njj.fragmeny I/niejianjian: MyFragment2 -> onActivityCreated ->12-29 10:41:36.980 25672-25672/njj.fragmeny I/niejianjian: MyFragment2 -> onStart ->12-29 10:41:36.980 25672-25672/njj.fragmeny I/niejianjian: MyFragment2 -> onResume ->
此时,第三个页面MyFragment2加载了。然后我们再滑动到第三个页面,也就是显示MyFragment2页面
12-29 10:42:42.470 25672-25672/njj.fragmeny I/niejianjian: MyFragment0 -> onPause ->12-29 10:42:42.470 25672-25672/njj.fragmeny I/niejianjian: MyFragment0 -> onStop ->12-29 10:42:42.470 25672-25672/njj.fragmeny I/niejianjian: MyFragment0 -> onDestroyView ->12-29 10:42:42.470 25672-25672/njj.fragmeny I/niejianjian: MyFragment3 -> onAttach ->12-29 10:42:42.470 25672-25672/njj.fragmeny I/niejianjian: MyFragment3 -> onCreate ->12-29 10:42:42.470 25672-25672/njj.fragmeny I/niejianjian: MyFragment3 -> onCreateView ->12-29 10:42:42.480 25672-25672/njj.fragmeny I/niejianjian: MyFragment3 -> onActivityCreated ->12-29 10:42:42.480 25672-25672/njj.fragmeny I/niejianjian: MyFragment3 -> onStart ->12-29 10:42:42.480 25672-25672/njj.fragmeny I/niejianjian: MyFragment3 -> onResume ->
然后我们发现MyFragment3加载完的同时,MyFragment0销毁了。

因为我们设置的setOffscreenPageLimit为1,也就是当前页面的前后1个页面是需要的,不需要的,会执行销毁视图。当我们滑动到第三个页面也就是MyFragment2显示的时候,它的前面是MyFragment1,后面是MyFragment3,此时,MyFragment0是不需要的,所以,它会销毁视图,当我们在滑动回第二个页面(MyFragment1显示)

12-29 10:45:46.970 25672-25672/njj.fragmeny I/niejianjian: MyFragment0 -> onCreateView ->12-29 10:45:46.970 25672-25672/njj.fragmeny I/niejianjian: MyFragment0 -> onViewCreated ->12-29 10:45:46.970 25672-25672/njj.fragmeny I/niejianjian: MyFragment0 -> onActivityCreated ->12-29 10:45:46.970 25672-25672/njj.fragmeny I/niejianjian: MyFragment3 -> onPause ->12-29 10:45:46.970 25672-25672/njj.fragmeny I/niejianjian: MyFragment3 -> onStop ->12-29 10:45:46.970 25672-25672/njj.fragmeny I/niejianjian: MyFragment3 -> onDestroyView ->12-29 10:45:46.970 25672-25672/njj.fragmeny I/niejianjian: MyFragment0 -> onStart ->12-29 10:45:46.970 25672-25672/njj.fragmeny I/niejianjian: MyFragment0 -> onResume ->
此时,又会重新绑定试图。

我们设置FragmentPageAdapter和FragmentStatePageAdapter的区别就是在于,会多执行onAttach和onDetach。使用哪个,根据自己的需求。

懒加载对setuserVisibleHint()进行处理

    @Override    public void setUserVisibleHint(boolean isVisibleToUser) {        super.setUserVisibleHint(isVisibleToUser);        if (getUserVisibleHint()){                    }        Log.i("niejianjian","MyFragment0 -> isVisibleToUser -> " + isVisibleToUser);    }

http://blog.csdn.net/linglongxin24/article/details/53205878




参考连接:

https://gold.xitu.io/entry/5858f7078e450a006c8008f4/view

http://yifeng.studio/2016/12/15/android-fragment-attentions/

http://www.jianshu.com/p/662c46cd3b5f













1 0
原创粉丝点击