美文网首页
Fragment详解

Fragment详解

作者: Vinson武 | 来源:发表于2020-01-09 17:42 被阅读0次

    Fragment生命周期

    Fragment必须是依存与Activity而存在的,因此Activity的生命周期会直接影响到Fragment的生命周期。官网这张图很好的说明了两者生命周期的关系:


    image.png
    • onAttach(Activity)
      当Fragment与Activity发生关联时调用。
    • onCreateView(LayoutInflater, ViewGroup,Bundle)
      创建该Fragment的视图
    • onActivityCreated(Bundle)
      当Activity的onCreate方法返回时调用
    • onDestoryView()
      与onCreateView想对应,当该Fragment的视图被移除时调用
    • onDetach()
      与onAttach相对应,当Fragment与Activity关联被取消时调用。

    Fragment使用

    静态使用

    把Fragment当成普通的控件,直接写在Activity的布局文件中. 一般比较少用,因为这样的fragment是静态的不能替换的。

    <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" >
     
        <fragment
            android:id="@+id/id_fragment_title"
            android:name="com.demo.wong.TitleFragment"
            android:layout_width="match_parent"
            android:layout_height="45dp" />
     
        <fragment
            android:layout_below="@id/id_fragment_title"
            android:id="@+id/id_fragment_content"
            android:name="com.demo.wong..ContentFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
     
    </RelativeLayout>
    
    动态使用

    为了动态使用Fragment,我们修改一下Actvity的布局文件,中间使用一个FrameLayout来放我们的动态添加的Fragment。

    <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" >
     
        <fragment
            android:id="@+id/id_fragment_title"
            android:name="com.demo.wong.TitleFragment"
            android:layout_width="match_parent"
            android:layout_height="45dp" />
    
        <FrameLayout
            android:id="@+id/id_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@id/id_fragment_title" />
     
    </RelativeLayout>
    
    @Override
        public void onClick(View v)
        {
            FragmentManager fm = getFragmentManager();
            // 开启Fragment事务
            FragmentTransaction transaction = fm.beginTransaction();
     
            switch (v.getId())
            {
            case R.id.tab_bottom_weixin:
                if (mWeixin == null)
                {
                    mWeixin = new ContentFragment();
                }
                // 使用当前Fragment的布局替代id_content的控件
                transaction.replace(R.id.id_content, mWeixin);
                break;
            case R.id.tab_bottom_friend:
                if (mFriend == null)
                {
                    mFriend = new FriendFragment();
                }
                transaction.replace(R.id.id_content, mFriend);
                break;
            }
            // transaction.addToBackStack();
            // 事务提交
            transaction.commit();
        }
    
    

    Fragment常用的三个类:

    1、android.app.Fragment 主要用于定义Fragment

    2、android.app.FragmentManager 主要用于在Activity中操作Fragment

    3、android.app.FragmentTransaction 保证一些列Fragment操作的原子性.

    • 获取FragmentManage的方式:getFragmentManager()
    • 主要的操作都是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维护。

    remove和detach有一点细微的区别,在不考虑回退栈的情况下,remove会销毁整个Fragment实例,而detach则只是销毁其视图结构,实例并不会被销毁。

    注意:使用Fragment的过程中可能会遇到Activity状态不一致:State loss这样的错误。主要是因为:commit方法一定要在Activity.onSaveInstance()之前调用。

    管理Fragment回退栈

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

    把一个Fragment事务添加到回退栈很简单,只需在事务提交前transaction.addToBackStack(String)就好。

    假如从FragmentOne跳到FragmentTwo时,addToBackStack,那么FragmentOne则会加入回退栈,当从FragmentTwo返回时会先回到FragmentOne

    FragmentTwo fTwo = new FragmentTwo();
            FragmentManager fm = getFragmentManager();
            FragmentTransaction transaction = fm.beginTransaction();
            transaction.replace(R.id.id_content, fTwo, "TWO");
            transaction.addToBackStack(null); //添加到当前回退栈
            transaction.commit();
    

    Fragment如何与Activity交互

    因为所有的Fragment都是依附于Activity的,所以通信起来并不复杂,大概归纳为:

    Activity调用Fragment

    1. 如果你Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法

    2. 如果Activity中未保存任何Fragment的引用,那么没关系,每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentByTag()或者findFragmentById()获得任何Fragment实例,然后进行操作。

    Fragment调用Activity

    1. 在Fragment中可以通过getActivity()得到当前绑定的Activity的实例,然后进行操作。
    2. 如果在Fragment中需要Context,可以通过调用getActivity(),如果该Context需要在Activity被销毁后还存在,则使用getActivity().getApplicationContext()。

    由于要考虑Fragment的复用性,必须降低Fragment与Activity的耦合。Fragment更不应该直接操作别的Fragment,毕竟Fragment操作应该由它的管理者Activity来决定。
    假如我们需要从一个fragment跳到另一个fragment,我们可以通过接口的方式来实现,通过activity去做跳转。

    public class FragmentOne extends Fragment implements OnClickListener
    {
        private FTwoBtnClickListener fTwoBtnClickListener ;
        
        public interface FTwoBtnClickListener
        {
            void onFTwoBtnClick();
        }
        //设置回调接口
        public void setfTwoBtnClickListener(FTwoBtnClickListener fTwoBtnClickListener)
        {
            this.fTwoBtnClickListener = fTwoBtnClickListener;
        }
        @Override
        public void onClick(View v)
        {
            if(fTwoBtnClickListener != null)
            {
                fTwoBtnClickListener.onFTwoBtnClick();
            }
        }
    
    }
    
    public class MainActivity extends Activity implements FTwoBtnClickListener
    {
    /**
         * FragmentOne 按钮点击时的回调
         */
        @Override
        public void onFTwoBtnClick()
        {
            if (mFTwo == null)
            {
                mFTwo = new FragmentTwo();
     
            }
            FragmentManager fm = getFragmentManager();
            FragmentTransaction tx = fm.beginTransaction();
            tx.hide(mFOne);
            tx.add(R.id.id_content, mFTwo, "Two");
            tx.addToBackStack(null);
            tx.commit();
        }
    }
    

    Fragment使用技巧

    1. 非空判断
    public class MainActivity extends FragmentActivity
    {
        
        private ContentFragment mContentFragment  ; 
     
        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        
            FragmentManager fm = getSupportFragmentManager();
            mContentFragment = (ContentFragment) fm.findFragmentById(R.id.id_fragment_container);
            
            if(mContentFragment == null )
            {
                mContentFragment = new ContentFragment();
                fm.beginTransaction().add(R.id.id_fragment_container,mContentFragment).commit();
            }
     
        }
     
    }
    
    

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

    1. 使用arguments来创建Fragment
    public class ContentFragment extends Fragment
    {
     
        private String mArgument;
        public static final String ARGUMENT = "argument";
     
        @Override
        public void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            Bundle bundle = getArguments();
            if (bundle != null)
                mArgument = bundle.getString(ARGUMENT);
     
        }
     
        /**
         * 传入需要的参数,设置给arguments
         * @param argument
         * @return
         */
        public static ContentFragment newInstance(String argument)
        {
            Bundle bundle = new Bundle();
            bundle.putString(ARGUMENT, argument);
            ContentFragment contentFragment = new ContentFragment();
            contentFragment.setArguments(bundle); //传参
            return contentFragment;
        }
    
    

    这种方式主要是为了Fragment和Activity间的解耦。

    1. Fragment的startActivityForResult
      考虑这种需求:从一个FragmentOne跳到另一个FragmentTwo,需要从FragmentTwo返回FragmentOne带回参数。
      在Fragment中存在startActivityForResult()以及onActivityResult()方法,但是呢,没有setResult()方法,用于设置返回的intent。这样我们就需要通过调用getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent)。也说明了一个问题:fragment能够从Activity中接收返回结果,但是其自设无法产生返回结果,只有Activity拥有返回结果。
    public class ListTitleFragment extends ListFragment
    {
     
        public static final int REQUEST_DETAIL = 0x110;
    
        @Override
        public void onActivityCreated(Bundle savedInstanceState)
        {
            super.onActivityCreated(savedInstanceState);
            setListAdapter(mAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, mTitles));
        }
        
        @Override
        public void onListItemClick(ListView l, View v, int position, long id)
        {
            mCurrentPos = position ; 
            Intent intent = new Intent(getActivity(),ContentActivity.class);
            intent.putExtra(ContentFragment.ARGUMENT, mTitles.get(position));
            startActivityForResult(intent, REQUEST_DETAIL);  //注意
        }
     
        //接收数据
        @Override
        public void onActivityResult(int requestCode, int resultCode, Intent data)
        {
            Log.e("TAG", "onActivityResult");
            super.onActivityResult(requestCode, resultCode, data);
            if(requestCode == REQUEST_DETAIL)
            {
                mTitles.set(mCurrentPos, mTitles.get(mCurrentPos)+" -- "+data.getStringExtra(ContentFragment.RESPONSE));
                mAdapter.notifyDataSetChanged();
            }
        }
    }
    
    public class ContentFragment extends Fragment
    {
     
        private String mArgument;
        public static final String ARGUMENT = "argument";
        public static final String RESPONSE = "response";
     
        @Override
        public void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            Bundle bundle = getArguments();
            if (bundle != null)
            {
                mArgument = bundle.getString(ARGUMENT);
                Intent intent = new Intent();
                intent.putExtra(RESPONSE, "good");
                getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent); //发送数据
            }
     
        }
    }
    

    Fragment中add与replace的区别

    • add不会重新初始化fragment,replace每次都会。所以如果在fragment生命周期内获取获取数据,使用replace会重复获取;
    • 添加相同的fragment时,replace不会有任何变化,add会报IllegalStateException异常;
    • replace先remove掉相同id的所有fragment,然后在add当前的这个fragment,而add是覆盖前一个fragment。所以如果使用add一般会伴随hide()和show(),避免布局重叠;
    • 使用add,如果应用放在后台,或以其他方式被系统销毁,再打开时,hide()中引用的fragment会销毁,所以依然会出现布局重叠bug,可以使用replace或使用add时,添加一个tag参数;(add前先判断是否已存在)

    getFragmentManager、getSupportFragmentManager 、getChildFragmentManager之间的区别?

    • getFragmentManager()所得到的是所在fragment 的父容器的管理器,
      getChildFragmentManager()所得到的是在fragment 里面子容器的管理器,
      如果是fragment嵌套fragment,那么就需要利用getChildFragmentManager();
    • 因为Fragment是3.0 Android系统API版本才出现的组件,所以3.0以上系统可以直接调用getFragmentManager()来获取FragmentManager()对象,而3.0以下则需要调用getSupportFragmentManager() 来间接获取;

    FragmentPagerAdapter与FragmentStatePagerAdapter的区别与使用场景

    • 相同点 :二者都继承PagerAdapter
    • 不同点 :FragmentPagerAdapter的每个Fragment会持久的保存在FragmentManager中,只要用户可以返回到页面中,它都不会被销毁。因此适用于那些数据相对静态的页,Fragment数量也比较少的那种;
      FragmentStatePagerAdapter只保留当前页面,当页面不可见时,该Fragment就会被消除,释放其资源。因此适用于那些数据动态性较大、占用内存较多,多Fragment的情况;

    参考链接:
    https://blog.csdn.net/lmj623565791/article/details/37970961

    https://blog.csdn.net/lmj623565791/article/details/37992017

    相关文章

      网友评论

          本文标题:Fragment详解

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