ViewPager+Fragment实现Tab动态添加和移除


一、需求

   在viewpager+fragment+tablayout中根据权限动态设置显示/隐藏某个tab。

二、背景

  一个问题断断续续解决了好几天,明明感觉很简单的需求,就是会遇到各种问题,而且错误都能在源码中看到,实在是解决太久了,人烦了,照搬网上的实现,但是每个人遇到的问题不一定一样,还是要根据实际情况来定。
这个问题过程中遇到很多错误。
  1. fragment布局不显示。

  可能原因:fragment被添加多次,但是已经在activity中绘制过了。

  解决方法:判断fragment是否被重复添加

  2. fragment移除时,发生下标越界异常。
  原因:因为在移除item后,执行了notifysetChanged方法。而在其中调用了tab的监听。我在监听事件中做了tab选中/不选中的图标设置。
  解决方法:
  在添加/移除前移除tab的监听,另外,在adapter刷新后再将tab添加回来。

三、需求

以下是最终实现的代码:
1. 代码初始化
override fun initView() { tabPagerAdapter = TabPagerAdapter(supportFragmentManager) mViewPager.adapter = tabPagerAdapter?.apply { //fragmentList
 setFragments(tabFragments) } mViewPager.offscreenPageLimit = MAX_FRAGMENT_SIZE mTabLayout.setupWithViewPager(mViewPager) updateTab(0) tabLister = object : TabLayout.OnTabSelectedListener { override fun onTabSelected(tab: TabLayout.Tab?) { setImageState(mTabLayout.selectedTabPosition) setDarkModeStatus(mTabLayout.selectedTabPosition) } override fun onTabUnselected(tab: TabLayout.Tab?) {} override fun onTabReselected(tab: TabLayout.Tab?) {} }?.apply { mTabLayout.addOnTabSelectedListener(this) } }

 

tab.setupWithViewpager就是和viewpager进行绑定。
private void setupWithViewPager( @Nullable final ViewPager viewPager, boolean autoRefresh, boolean implicitSetup) { if (this.viewPager != null) { // If we've already been setup with a ViewPager, remove us from it
    if (pageChangeListener != null) { this.viewPager.removeOnPageChangeListener(pageChangeListener); } if (adapterChangeListener != null) { this.viewPager.removeOnAdapterChangeListener(adapterChangeListener); } } if (currentVpSelectedListener != null) { // If we already have a tab selected listener for the ViewPager, remove it
 removeOnTabSelectedListener(currentVpSelectedListener); currentVpSelectedListener = null; } if (viewPager != null) { //与viewpager进行绑定
    this.viewPager = viewPager; // Add our custom OnPageChangeListener to the ViewPager
    if (pageChangeListener == null) { pageChangeListener = new TabLayoutOnPageChangeListener(this); } pageChangeListener.reset(); viewPager.addOnPageChangeListener(pageChangeListener); // Now we'll add a tab selected listener to set ViewPager's current item //添加tab选择默认监听
    currentVpSelectedListener = new ViewPagerOnTabSelectedListener(viewPager); addOnTabSelectedListener(currentVpSelectedListener); final PagerAdapter adapter = viewPager.getAdapter(); if (adapter != null) { //添加一个监听,adapter发生改变时,tab更新
 setPagerAdapter(adapter, autoRefresh); }
void setPagerAdapter(@Nullable final PagerAdapter adapter, final boolean addObserver) { if (pagerAdapter != null && pagerAdapterObserver != null) { // If we already have a PagerAdapter, unregister our observer
 pagerAdapter.unregisterDataSetObserver(pagerAdapterObserver); } pagerAdapter = adapter; if (addObserver && adapter != null) { // Register our observer on the new adapter
    if (pagerAdapterObserver == null) { pagerAdapterObserver = new PagerAdapterObserver(); } adapter.registerDataSetObserver(pagerAdapterObserver); } // Finally make sure we reflect the new adapter
 populateFromPagerAdapter(); }
void populateFromPagerAdapter() { removeAllTabs(); if (mPagerAdapter != null) { final int adapterCount = mPagerAdapter.getCount(); for (int i = 0; i < adapterCount; i++) { addTab(newTab().setText(mPagerAdapter.getPageTitle(i)), false); } // Make sure we reflect the currently set ViewPager item
        if (mViewPager != null && adapterCount > 0) { final int curItem = mViewPager.getCurrentItem(); if (curItem != getSelectedTabPosition() && curItem < getTabCount()) { selectTab(getTabAt(curItem)); } } } }
当执行pageAdapter的notifyDataSetchanged时
public void notifyDataSetChanged() { synchronized (this) { if (mViewPagerObserver != null) { mViewPagerObserver.onChanged(); } } //onChanged()方法会回调所有Observers者的onChanged方法,最终执行populateFromPagerAdapter方法进行tab的更新
 mObservable.notifyChanged(); }
错误2报错的原因
//tablayout.setUpWithViewpager()会设置pageSelect监听,设置viewPager的选择时回调监听,进而回调tab的onSelected方法,这时 //notify方法会有延迟,因此此时tab的count也没刷新,而走进循环后,更新导致的报错
public void onPageSelected(final int position) { final TabLayout tabLayout = tabLayoutRef.get(); if (tabLayout != null
      && tabLayout.getSelectedTabPosition() != position && position < tabLayout.getTabCount()) { // Select the tab, only updating the indicator if we're not being dragged/settled // (since onPageScrolled will handle that).
    final boolean updateIndicator = scrollState == SCROLL_STATE_IDLE || (scrollState == SCROLL_STATE_SETTLING && previousScrollState == SCROLL_STATE_IDLE); tabLayout.selectTab(tabLayout.getTabAt(position), updateIndicator); } }
2.adapter的实现
open class TabPagerAdapter(var fm: FragmentManager) : FragmentPagerAdapter(fm){ private var fragments: MutableList<BaseTabLazyFragment> = mutableListOf() private var count = 10
    private var mCurTransaction:FragmentTransaction? = null
    private var tags= mutableListOf<String>() fun clearFragments(){ fragments.clear() notifyDataSetChanged() } fun setFragments(frags: List<BaseTabLazyFragment>) { fragments.addAll(frags) notifyDataSetChanged() } fun addFragments(index:Int,frags: BaseTabLazyFragment) { fragments.add(index,frags) notifyDataSetChanged() } fun removeFragments(frags: BaseTabLazyFragment){ fragments.remove(frags) notifyDataSetChanged() } override fun getItemPosition(obj : Any): Int = POSITION_NONE override fun getItem(position: Int): Fragment = fragments[position] override fun getItemId(position: Int): Long { var fragment = fragments[position] return when(fragment.pageName){ "DeviceFragment"-> 1.toLong() "AuthorizationTabFragment" -> 2.toLong() "MonitorMessageTabFragment" -> 3.toLong() "MonitorSettingTabFragment" -> 4.toLong() else -> super.getItemId(position) } } override fun getCount(): Int = fragments.size
 
这里注意要重写getItemId,否则移除N遍也不会成功。
public Object instantiateItem(@NonNull 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); //优先获取tag中的fragment,没有的话才创建。而itemId默认是position,移除某个fragment是移除不成功的,会在 // fragmentManager中存储添加过的所有fragment
    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); if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED); } else { fragment.setUserVisibleHint(false); } } return fragment; }

四、使用

fun addFragment(index: Int, fragment: BaseTabLazyFragment) { if (tabFragments.size >= MAX_FRAGMENT_SIZE) return tabLister?.let { mTabLayout.removeOnTabSelectedListener(it) } var curItem = mViewPager.currentItem tabFragments.add(index, fragment) tabPagerAdapter?.addFragments(index, fragment) setCurTab(if (curItem >= index) curItem + index else curItem) } fun removeFragment(index: Int, fragment: BaseTabLazyFragment) { if (tabFragments.size < MAX_FRAGMENT_SIZE) return tabLister?.let { mTabLayout.removeOnTabSelectedListener(it) } var curItem = mViewPager.currentItem tabFragments.remove(fragment) tabPagerAdapter?.removeFragments(fragment) setCurTab(if (curItem >= index) curItem - index else curItem) } fun setCurTab(curItem: Int) { mViewPager.currentItem = curItem updateTab(curItem) tabLister?.let { mTabLayout.addOnTabSelectedListener(it) } }

 

 

  


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM