美文网首页
Fragment的总结

Fragment的总结

作者: fastcv | 来源:发表于2019-06-28 16:31 被阅读0次

    前言

    Fragment(碎片)作为Android中大屏幕开发常用的UI片段,它让程序更加合理更加充分的利用大屏幕的空间。可以这么去理解,它可以将屏幕碎片化,每个碎片处理不同的事情,可以相互通信。(结合大佬们的博客总结的,如有侵权,麻烦联系我删除此文章)

    Fragment是依赖于Activity的,不能独立存在的。并且一个Activity里可以有多个Fragment,我们能在Activity运行时动态地添加或删除Fragment。。一个Fragment可以被多个Activity重用。另外,Fragment也有自己的生命周期。

    Fragment的生命周期

    Fragment必须是依存与Activity而存在的,因此Activity的生命周期会直接影响到Fragment的生命周期。所以,将Activity的生命周期和它的合在一起方便理解,如图:

    FragmentRecycle.png

    介绍下Fragment的生命周期的各个方法

    • onAttach(Activity):当Fragment与Activity发生关联时调用。
    • onCreate():初始化Fragment
    • onCreateView(LayoutInflater, ViewGroup,Bundle):创建该Fragment的视图
    • onActivityCreated(Bundle):当Activity的onCreate方法返回时调用
    • onStart():视图可见
    • onResume():可交互
    • onPause():不可交互
    • onStop():不可见
    • onDestoryView():与onCreateView想对应,当该Fragment的视图被移除时调用
    • onDestory():销毁Fragment
    • onDetach():与onAttach相对应,当Fragment与Activity关联被取消时调用

    注意:除了onCreateView,其他的所有方法如果你重写了,必须调用父类对于该方法的实现,

    基本使用

    用法有两种:

    • 静态使用
    • 动态使用

    静态使用

    把Fragment当成普通的控件,直接写在Activity的布局文件中。
    1、继承Fragment,重写onCreateView决定Fragemnt的布局,如下我们将界面分为两块(Title和Context):

    Title.java

    public class TitleFragment extends Fragment {
    
        @Override
        public View onCreateView(LayoutInflater inflater,ViewGroup container, Bundle savedInstanceState) {
            return inflater.inflate(R.layout.title_fragment,container,false);
        }
    }
    

    title.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:layout_width="match_parent"
        android:gravity="center"
        android:layout_height="45dp">
    
        <TextView
            android:layout_width="wrap_content"
            android:text="我是标题"
            android:id="@+id/frg_title"
            android:layout_height="wrap_content" />
    
    </LinearLayout>
    

    Context.java

    public class ContextFragment extends Fragment {
    
        @Override
        public View onCreateView( LayoutInflater inflater,ViewGroup container, Bundle savedInstanceState) {
            return inflater.inflate(R.layout.context_fragment,container,false);
        }
    }
    

    context.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:layout_width="match_parent"
        android:gravity="center"
        android:layout_height="match_parent">
    
        <TextView
            android:layout_width="wrap_content"
            android:text="我是上下文"
            android:layout_height="wrap_content" />
    
    </LinearLayout>
    

    2、在Activity中声明此Fragment,就当和普通的View一样
    FragmentsActivity.java

    public class FragmentsActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.study_fragments_activity);
        }
    
        public void intoActivity(Context context){
            Intent intent = new Intent(context,this.getClass());
            context.startActivity(intent);
        }
    }
    

    study_fragments_activity.xml

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <fragment
            android:layout_width="match_parent"
            android:layout_height="45dp"
            android:id="@+id/ac_title"
            android:name="sayhallo.cn.ilikeandroid.fragments.TitleFragment"/>
    
        <fragment
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/ac_context"
            android:name="sayhallo.cn.ilikeandroid.fragments.ContextFragment"/>
    
    </LinearLayout>
    

    看下效果图:

    静态Fragment.jpg

    这样子,一个Activity的工作就被两个Fragment给分了,结构就会清楚很多了。

    动态使用

    即动态的把Fragment加入(增删)到Activity中

    第一步:新建activity,放置我们fragment显示的布局

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_marginBottom="45dp"
            android:id="@+id/fragment_context"
            android:layout_height="match_parent"/>
    
        <LinearLayout
            android:layout_width="match_parent"
            android:id="@+id/fragment_select_tab"
            android:layout_alignParentBottom="true"
            android:orientation="horizontal"
            android:weightSum="4"
            android:layout_height="45dp">
    
            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:text="按钮1"
                android:id="@+id/bt_1"
                android:layout_height="match_parent" />
    
            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:text="按钮2"
                android:id="@+id/bt_2"
                android:layout_height="match_parent" />
    
            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:text="按钮3"
                android:id="@+id/bt_3"
                android:layout_height="match_parent" />
    
            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:id="@+id/bt_4"
                android:text="按钮4"
                android:layout_height="match_parent" />
    
        </LinearLayout>
    </RelativeLayout>
    

    第二步:在java文件设置显示的逻辑

     private void setDefaultFragment() {
            FragmentManager fm = getSupportFragmentManager();
            FragmentTransaction transaction = fm.beginTransaction();
            Fragment1 fragment1 = new Fragment1();
            transaction.replace(R.id.fragment_context, fragment1);
            transaction.commit();
        }
    
        @Override
        public void onClick(View v) {
            FragmentManager fm = getSupportFragmentManager();
            // 开启Fragment事务
            FragmentTransaction transaction = fm.beginTransaction();
    
            switch (v.getId())
            {
                case R.id.bt_1:
                    //添加、替换
                    break;
                case R.id.bt_2:
                    //添加、替换
                    break;
            }
            // transaction.addToBackStack();
            // 事务提交
            transaction.commit();
        }
    

    第三步:编写我们需要显示的fragment即可

    最后,点击相应的按钮即可显示对应的fragment了。

    Fragment常用的三个类:

    • androidx.fragment.app.Fragment 主要用于定义Fragment

    • androidx.fragment.app.FragmentManager 主要用于在Activity中操作Fragment,通过getSupportFragmentManager方法得到(必须要Activity继承AppCompatActivity才有这个方法)

    • androidx.fragment.app.FragmentTransaction 事务操作,通过FragmentManager.benginTran satcion()得到实例对象,如 FragmentTransaction transaction = fm.benginTransatcion();

      • transaction.add(Fragment fragment,String tag) :往Activity中添加一个Fragment
      • transaction.remove(Fragment fragment):从Activity中移除一个Fragment
      • transaction.replace(Fragment fragment):使用另一个Fragment替换当前的
      • transaction.hide(Fragment fragment):隐藏当前的Fragment,仅仅是设为不可见,并不会销毁
      • transaction.show(Fragment fragment):显示之前隐藏的Fragment
      • transaction.detach(Fragment fragment):会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。
      • transaction.attach(Fragment fragment):重建view视图,附加到UI上并显示。
      • transatcion.commit():提交一个事务

    Fragment回退栈

    和Activity的返回栈一样,如果你将Fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次的保存的Fragment。一旦Fragment完全从后退栈中弹出,用户再次点击后退键,则退出当前Activity。

    怎样添加Fragment到回退栈

    这里就会用到这样的一个方法

    FragmentTransaction.addToBackStack(String)
    

    如:

    Fragment3 fThree = new Fragment3();
                    FragmentManager fm = getActivity().getSupportFragmentManager();
                    FragmentTransaction tx = fm.beginTransaction();
                    tx.hide(Fragment2.this);
                    tx.add(R.id.fragment_context , fThree, "THREE");
             //     tx.replace(R.id.id_content, fThree, "THREE");
                    tx.addToBackStack(null);
                    tx.commit();
    

    这样子,就把当前这个fragment添加到回退栈了,并且进入了下一个Fragment

    Fragment与Activity通信

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

    • 如果你Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法
    • 如果Activity中未保存任何Fragment的引用,那么没关系,每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentByTag()或者findFragmentById()获得任何Fragment实例,然后进行操作
    • 在Fragment中可以通过getActivity得到当前绑定的Activity的实例,然后进行操作
      注:如果在Fragment中需要Context,可以通过调用getActivity(),如果该Context需要在Activity被销毁后还存在,则使用getActivity().getApplicationContext()。

    附加使用

    1、通常用法

    Activity中有个FragmentManager,其内部维护fragment队列,以及fragment事务的回退栈。

    一般情况下,我们在Activity里面会这么添加Fragment:

    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队列,从而恢复之前的状态。

    Fragment Arguments

    比如我们某个按钮触发Activity跳转,需要通过Intent传递参数到目标Activity的Fragment中,那么此Fragment如何获取当前的Intent的值呢?

            mArgument = getActivity().getIntent().getStringExtra(ARGUMENT);
    

    这么写,功能上是实现了,但是呢?存在一个大问题:我们用Fragment的一个很大的原因,就是为了复用。你这么写,相当于这个Fragment已经完全和当前这个宿主Activity绑定了,所有就完全不能复用了,所以,我们需要换种写法。

    public class Fragment1 extends Fragment
    {
        private String mArgument;
        @Override
        public void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            Bundle bundle = getArguments();
            if (bundle != null)
                mArgument = bundle.getString("argument");
     
        }
    
        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间的解耦。

    Fragment数据回传

    两个Fragment,一个展示文章列表的Fragment(Activity1),一个显示详细信息的Fragment(Activity2)。

    现在,我们点击列表Fragment中的列表项,传入相应的参数,去详细信息的Fragment展示详细的信息,在详细信息页面,用户可以进行点评,当用户点击back以后,我们以往点评结果显示在列表的Fragment对于的列表项中;

    也就是说,我们点击跳转到对应Activity的Fragment中,并且希望它能够返回参数,那么我们肯定是使用Fragment.startActivityForResult ;

    在Fragment中存在startActivityForResult()以及onActivityResult()方法,但是呢,没有setResult()方法,用于设置返回的intent,这样我们就需要通过调用

    getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent)
    

    如:点击跳转和监听回传的代码

    Fragment1

    @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();
            }
        }
    

    回传的代码
    Fragment2

                Intent intent = new Intent();
                intent.putExtra(RESPONSE, "good");
                getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent);
    

    但是,我们发现,刚才是在两个不同的Activity宿主上的Fragment数据回传,现在我们想在同一个Activity中数据回传。怎么实现呢?

    相同宿主上的Fragment数据回传

    虽然我们是在同一个Activity宿主,但是我们返回的数据,依然在onActivityResult中进行接收,但是,注意添加这么一句代码:

    dialog.setTargetFragment(ContentFragment.this, REQUEST_EVALUATE);
    

    设置回传的代码

    // 设置返回数据
        protected void setResult(int which)
        {
            // 判断是否设置了targetFragment
            if (getTargetFragment() == null)
                return;
     
            Intent intent = new Intent();
            intent.putExtra(RESPONSE_EVALUATE, mEvaluteVals[which]);
            getTargetFragment().onActivityResult(ContentFragment.REQUEST_EVALUATE,
                    Activity.RESULT_OK, intent);
     
        }
    

    监听的代码

    @Override
        public void onActivityResult(int requestCode, int resultCode, Intent data)
        {
            super.onActivityResult(requestCode, resultCode, data);
     
            if (requestCode == REQUEST_EVALUATE)
            {
                String evaluate = data
                        .getStringExtra(EvaluateDialog.RESPONSE_EVALUATE);
                Toast.makeText(getActivity(), evaluate, Toast.LENGTH_SHORT).show();
                Intent intent = new Intent();
                intent.putExtra(RESPONSE, evaluate);
                getActivity().setResult(Activity.REQUEST_OK, intent);
            }
     
        }
    

    通过回调回传数据

    如:

    public class FragmentOne extends Fragment implements OnClickListener
    {
        private Button mBtn;
     
        public interface FOneBtnClickListener
        {
            void onFOneBtnClick();
        }
     
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState)
        {
            View view = inflater.inflate(R.layout.fragment_one, container, false);
            mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);
            mBtn.setOnClickListener(this);
            return view;
        }
     
        /**
         * 交给宿主Activity处理,如果它希望处理
         */
        @Override
        public void onClick(View v)
        {
            if (getActivity() instanceof FOneBtnClickListener)
            {
                ((FOneBtnClickListener) getActivity()).onFOneBtnClick();
            }
        }
     
    }
    
    public class FragmentTwo extends Fragment implements OnClickListener
    {
     
        
        private Button mBtn ;
        
        private FTwoBtnClickListener fTwoBtnClickListener ;
        
        public interface FTwoBtnClickListener
        {
            void onFTwoBtnClick();
        }
        //设置回调接口
        public void setfTwoBtnClickListener(FTwoBtnClickListener fTwoBtnClickListener)
        {
            this.fTwoBtnClickListener = fTwoBtnClickListener;
        }
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState)
        {
            View view = inflater.inflate(R.layout.fragment_two, container, false);
            mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);
            mBtn.setOnClickListener(this);
            return view ; 
        }
        @Override
        public void onClick(View v)
        {
            if(fTwoBtnClickListener != null)
            {
                fTwoBtnClickListener.onFTwoBtnClick();
            }
        }
     
    }
    

    相关文章

      网友评论

          本文标题:Fragment的总结

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