Fragment的总结

2019-06-28  本文已影响0人  fastcv

前言

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

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

Fragment的生命周期

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

FragmentRecycle.png

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

注意:除了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常用的三个类:

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的,所以通信起来并不复杂,大概归纳为:

附加使用

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();
        }
    }
 
}
上一篇下一篇

猜你喜欢

热点阅读