Fragment总结

零、前言

今年可能要实习了,准备把之前学过的知识点重头开始回顾下,不学习新东西了,这篇讲Fragment。

一、生命周期

这里写图片描述

onAttach(Activity activity)

当Fragment第一次被添加到宿主Activity上时调用。

onCreate(@Nullable Bundle savedInstanceState)

初始化Fragment时调用,调用时期:在onAttach()方法之后,在onCreateView()方法之前。

onCreateView(inflater,container,savedInstanceState)

当已经有了一个fragment实例,一般我们在这里创建Fragment的视图。
在onCreate()之后,onActivityCreated(Bundle)之前调用。
如果在这里返回了一个View,那么当在释放时将会调用onDestroyView().

onActivityCreated(Bundle)

当Fragment的宿主Activity已经被create()了并且这个Fragment的视图也被实例化了的时候调用。在这个函数里,我们可以进行一些状态的恢复,加载数据等操作。当然,这里可以调用
setRetainInstance(boolean)来保存他们的实例。这个函数的调用时期:onCreateView()之前,
onViewStateRestored()之后。

接着几个生命周期方法对应着Activity的生命周期。就不叙述了。

onDestoryView()

上面提过,和onCreateView相对应,当onCreateView方法已经返回一个视图,那么在释放资源时,将调用该方法。

onDetach(Activity activity)

与onAttach相对应,当Fragment从宿主上面移除时调用。

注意:除了onCreateView方法外,其它方法重写的话必须调用父类对该方法的实现,也就是super()。

二、Fragment的使用

1、静态使用

  1. 继承Fragment,在onCreateView中初始化视图
  2. 在Activity的布局中使用fragment 布局,并且添加name属性,属性的值为第一步创建的Fragment类。
    就这样两步就实现了。

2、动态使用

  1. 获取FragmentManager对象
  2. 调用FragmentManager的beginTransaction()方法,获取FragmentTransaction 对象
  3. 创建一个继承了Fragment对象的类
  4. 调用FragmentTransaction 的replace()方法
  5. 调用 FragmentTransaction 的commit()

3、Fragment的回退栈

类似与Activity,Fragment也有自己的回退栈,当我们把一个Fragment放入栈中,这个Fragment实例并不会被销毁,但是界面会被销毁,也就会调用onDestoryView()方法,当我们从另一个Fragment回退到放入回退栈中的Fragment时,会调用onCreateView()方法。

三、常用API

在上面动态使用中涉及了几个常用的API,还有未介绍的API

FragmentTransaction 类

transaction.add() :往一个Activity中添加一个Fragment

transaction.remove() :如果这个Fragment没有被添加到回退站,则这个Fragment则会被销毁,也就是调用onDestoryView()方法。

transaction.replace():remove()和add()合体。

addToBackStack(null):将当前Fragment添加到回退栈

transaction.hide():隐藏当前的Fragment,仅仅是设为不可见,并不会销毁

transaction.show():显示之前隐藏的Fragment

transatcion.commit():提交一个事务,注意:一定要在Activity.onSaveInstance()之前调用,否则会报State loss这样的错误。

Fragment类

detach():会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。

attach():重建view视图,附加到UI上并显示。

四、和ViewPager结合使用

Fragment和ViewPager结合使用的情况还是比较多大。将每一个Fragment作为ViewPager的一页。我们不用操作事务,利用adapter帮助我们完成。

1、FragmentPagerAdapter

FragmentPagerAdapter 继承自 PagerAdapter。相比通用的 PagerAdapter,该类更专注于每一页均为 Fragment 的情况。这个adapter每生成一个Fragment都会报错在内存中,因此适合页面比较少的情况。使用该类需要重写两个方法

getItem()
这个函数在instantiateItem()函数中被调用。看看源码。


  @Override
    public Object instantiateItem(ViewGroup container, int position) {
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }

        final long itemId = getItemId(position);

        // Do we already have this fragment?
        String name = makeFragmentName(container.getId(), itemId);
        Fragment fragment = mFragmentManager.findFragmentByTag(name);
        if (fragment != null) {
            if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
            mCurTransaction.attach(fragment);
        } else {
            fragment = getItem(position);
            if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
            mCurTransaction.add(container.getId(), fragment,
                    makeFragmentName(container.getId(), itemId));
        }
        if (fragment != mCurrentPrimaryItem) {
            fragment.setMenuVisibility(false);
            fragment.setUserVisibleHint(false);
        }

        return fragment;
    }

首先判断事务是否为null,如果为空,则开启事务,接着调用getItemId(position)方法获取对应位置的itemid,通过itemid获取Fragment绑定的name,再获取Fragment。
然后判断Fragment是否为null,如果不为null,则调用事务的attach()方法,如果为null,则调用getItem()返回一个Fragment。从这里可以看出并不是每次创建都会创建Fragment,也就是说,不是每次都会调用getItem()方法,只有在第一次调用才会调用。这也是为什么有时候调用notifyDataSetChanged()不起作用的原因

getCount()
这个函数比较简单,只是单纯返回数量。

destroyItem() :该函数被调用后,会对 Fragment 进行 FragmentTransaction.detach()。Fragment 所占用的资源不会被释放。

@Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object
                + " v=" + ((Fragment)object).getView());
        mCurTransaction.detach((Fragment)object);
    }

2、FragmentPagerStateAdapter

FragmentStatePagerAdapter 和前面的 FragmentPagerAdapter 一样,是继承子 PagerAdapter。但是,和 FragmentPagerAdapter 不一样的是,该 PagerAdapter 的实现将只保留当前页面,当页面离开视线后,就会被消除,释放其资源;而在页面需要显示时,生成新的页面(就像 ListView 的实现一样)。这么实现的好处就是当拥有大量的页面时,不必在内存中占用大量的内存。

instantiateItem()源码:

 @Override
    public Object instantiateItem(ViewGroup container, int position) {
        // If we already have this item instantiated, there is nothing
        // to do.  This can happen when we are restoring the entire pager
        // from its saved state, where the fragment manager has already
        // taken care of restoring the fragments we previously had instantiated.
        if (mFragments.size() > position) {
            Fragment f = mFragments.get(position);
            if (f != null) {
                return f;
            }
        }

        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }

        Fragment fragment = getItem(position);
        if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
        if (mSavedState.size() > position) {
            Fragment.SavedState fss = mSavedState.get(position);
            if (fss != null) {
                fragment.setInitialSavedState(fss);
            }
        }
        while (mFragments.size() <= position) {
            mFragments.add(null);
        }
        fragment.setMenuVisibility(false);
        fragment.setUserVisibleHint(false);
        mFragments.set(position, fragment);
        mCurTransaction.add(container.getId(), fragment);

        return fragment;
    }

destroyItem() :将 Fragment 移除,即调用 FragmentTransaction.remove(),并释放其资源。

 @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment) object;

        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
                + " v=" + ((Fragment)object).getView());
        while (mSavedState.size() <= position) {
            mSavedState.add(null);
        }
        mSavedState.set(position, fragment.isAdded()
                ? mFragmentManager.saveFragmentInstanceState(fragment) : null);
        mFragments.set(position, null);

        mCurTransaction.remove(fragment);
    }

五、横竖屏切换时的问题

解决这个问题之前我们先了解下当手机横竖屏切换时,Activity的生命周期方法的调用情况。

进入Activity

02-16 18:28:50.341 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onCreate
02-16 18:28:50.345 4655-4655/com.lw.fragment I/System.out: ScreenFragment-----无参构造器
02-16 18:28:50.345 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onStart
02-16 18:28:50.345 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onResume

切换为横屏之后

02-16 18:28:50.341 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onCreate
02-16 18:28:50.345 4655-4655/com.lw.fragment I/System.out: ScreenFragment-----无参构造器
02-16 18:28:50.345 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onStart
02-16 18:28:50.345 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onResume
02-16 18:29:41.861 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onPause
02-16 18:29:41.861 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onStop
02-16 18:29:41.861 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onDestroy
02-16 18:29:41.873 4655-4655/com.lw.fragment I/System.out: ScreenFragment-----无参构造器
02-16 18:29:41.885 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onCreate
02-16 18:29:41.885 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onStart
02-16 18:29:41.885 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onResume

从上面可以知道,当从竖屏切到横屏时,依次调用了onPause、onStop、onDestroy、onCreate、onStart、onResume。这就相当于重新启动了一个Activity。

OK、结合Fragment来看。

这里我贴上我的代码

Activity

public class ScreenActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_screen);
        System.out.println("ScreenActivity:onCreate");
        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction().add(R.id.fl_content, new ScreenFragment("我是参数")).commit();
//            getSupportFragmentManager().beginTransaction().add(R.id.fl_content, ScreenFragment.newInstance("我是参数")).commit();
        }

    }

    @Override
    protected void onStart() {
        super.onStart();
        System.out.println("ScreenActivity:onStart");
    }

    @Override
    protected void onResume() {
        super.onResume();
        System.out.println("ScreenActivity:onResume");
    }

    @Override
    protected void onPause() {
        super.onPause();
        System.out.println("ScreenActivity:onPause");
    }

    @Override
    protected void onStop() {
        super.onStop();
        System.out.println("ScreenActivity:onStop");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        System.out.println("ScreenActivity:onDestroy");
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        System.out.println("ScreenActivity:onRestart");
    }
}

Fragment

public class ScreenFragment extends Fragment {

    private String arg = "无参构造器";

    public ScreenFragment() {
        System.out.println("ScreenFragment-----无参构造器");
    }

    public ScreenFragment(String arg) {
        this.arg = arg;
    }

    public static ScreenFragment newInstance(String arg) {
        Bundle bundle = new Bundle();
        bundle.putString("arg", arg);
        ScreenFragment fragment = new ScreenFragment();
        fragment.setArguments(bundle);
        return fragment;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        View view = inflater.inflate(R.layout.fragment_screen, container, false);
        TextView tv = (TextView) view.findViewById(R.id.tv);
        tv.setText(arg);
//        tv.setText(getArguments().getString("arg"));
        return view;
    }

}

如果安照上面的代码执行,则在从竖屏切换成为横屏时呈现无参构造器。解决方法:将Activity和Fragment两个地方的注释取消,并把注释上面的代码删除即可。

OK,关于Fragment就复习到这里了,以后要是又学到新东西再添加。

这篇博客所用到的代码: 源码下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值