Fragment详解

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vinson武

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值