ViewPager2+Fragment 在不保留活动踩坑

ViewPager2+Fragment 在不保留活动踩坑

一. 目标

利用viewpager和 fragment实现滑动切换不同页面的效果

二.实现

1.ViewPager + FragmentPagerAdapter

xml:

 <androidx.viewpager.widget.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

activity:

 r.viewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
            @NonNull
            @Override
            public Fragment getItem(int position) {
                return fragmentList.get(position);
            }

            @Override
            public int getCount() {
                return fragmentList == null ? 0 : fragmentList.size();
            }
        });
        r.viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                
            }

            @Override
            public void onPageSelected(int position) {

            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });
        
2. ViewPager2 + FragmentStateAdapter

xml:

 <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

activity:

 r.viewPager.setAdapter(new FragmentStateAdapter(getSupportFragmentManager(), getLifecycle()) {
            @Override
            public int getItemCount() {
                return fragmentList == null ? 0 : fragmentList.size();
            }

            @NonNull
            @Override
            public Fragment createFragment(int position) {
                return fragmentList.get(position);
            }


        });
        r.viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
            @Override
            public void onPageSelected(int position) {
                super.onPageSelected(position);
              
            }
        });

三.区别

FragmentStateAdapter 和 FragmentPagerAdapterd区别
1. FragmentPagerAdapter区别会缓存fragment,非前台fragment依旧存在内存中。而FragmentStateAdapter则是非前台fragment会被销毁,在滑动变成前台时会重新创建,比较节约内存。举个例子,有个fragment在播放视频,在onPause()里面,我会释放播放器资源,但是在用FragmentPagerAdapter时,切换到其他fragment时,并没有调用onPause()导致资源没有释放.
2. 在不保留活动或者其他情况导致activity重建时,FragmentPagerAdapter没有实现restoreState,但实际上重新构建好的activity里面初始化viewpager时,FragmentPagerAdapter的getItem()方法并不会调用,原因是再加载时候它会从FragmentManager里面先读取,而FragmentManager里面是会重新构建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));
3. 在不保留活动或者其他情况导致activity重建时,FragmentStateAdapter有重写restoreState()方法,然后从savedState里面读取数据,包括Fragment的SavedState也会重建。
   for (String key : bundle.keySet()) {
            if (isValidKey(key, KEY_PREFIX_FRAGMENT)) {
                long itemId = parseIdFromKey(key, KEY_PREFIX_FRAGMENT);
                Fragment fragment = mFragmentManager.getFragment(bundle, key);
                mFragments.put(itemId, fragment);
                continue;
            }

            if (isValidKey(key, KEY_PREFIX_STATE)) {
                long itemId = parseIdFromKey(key, KEY_PREFIX_STATE);
                Fragment.SavedState state = bundle.getParcelable(key);
                if (containsItem(itemId)) {
                    mSavedStates.put(itemId, state);
                }
                continue;
            }

            throw new IllegalArgumentException("Unexpected key in savedState: " + key);
        }

四.问题

由于在不保留活动时,Fragment是重新构建的,并不是由初始化时从FragmentList里面读取的,所以在activity和fragment通信时,如果用了fragmentList里面的对象去通信,会发现对应fragment并没有被加载activity里面。

五。解决办法

先尝试暴力重写onSaveInstanceState()方法,使其在重新构建activity时不重构fragment。看起来似乎解决了问题,现在每次都会调用我们初始化方法,使用fragmentList里面的fragment对象,我们又能通过fragmentList的直接与fragment通信了。但是后面发现,如果在onActivityResult()里面需要跟fragment通信,又出现问题了。因为onActivityResult()方法执行是在onResume()之前的,此时你的fragment还没有加载到activity里面,所以还是不行.
转换思路,既然有缓存fragment时,都是从FragmentManager里面加载fragment的,那么在通信的时候我们也可以直接从FragmentManager里面找我们要的fragment即可,通过findFragmentByTag()方法即可。然后在Fragment的onSaveInstanceState()方法里面主动将我们需要恢复的数据存在Bundle里面。
至此,解决了Fragment的重建导致的缓存问题.

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在使用ViewPager2+Fragment时,每个Fragment的生命周期会在ViewPager2的切换过程中被调用,具体如下: 1. onAttach():当Fragment与Activity建立关联时调用。 2. onCreate():当Fragment被创建时调用。 3. onCreateView():当Fragment的布局被创建时调用。 4. onViewCreated():当Fragment的布局被创建后调用。 5. onStart():当Fragment可见时调用。 6. onResume():当Fragment与用户交互时调用。 7. onPause():当Fragment失去焦点时调用。 8. onStop():当Fragment不再可见时调用。 9. onDestroyView():当Fragment的布局被销毁时调用。 10. onDestroy():当Fragment被销毁时调用。 11. onDetach():当Fragment与Activity解除关联时调用。 在ViewPager2中,当用户滑动到一个新的Fragment时,会先调用新的Fragment的onAttach()、onCreate()、onCreateView()、onViewCreated()方法,然后再调用旧的Fragment的onPause()、onStop()、onDestroyView()、onDestroy()、onDetach()方法。当用户再次滑动回到旧的Fragment时,会先调用旧的Fragment的onAttach()、onCreate()、onCreateView()、onViewCreated()方法,然后再调用新的Fragment的onPause()、onStop()、onDestroyView()、onDestroy()、onDetach()方法。 需要注意的是,当ViewPager2中的Fragment被销毁后,会调用Fragment的onDestroy()方法,但Fragment的实例对象并没有被销毁,而是被保存在FragmentManager中,当用户再次滑动到该Fragment时,会重新调用Fragment的onCreate()方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值