• 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) }
    }

     

      

  • 相关阅读:
    文件的初级功能
    Scanner的用法
    界面制作小例
    初学Java感想
    el-table合计栏未显示的问题
    推荐一些团队博客和个人博客地址
    大数加法
    汇编语言画圆
    Java一个简单的文件工具集
    css选择器
  • 原文地址:https://www.cnblogs.com/fangg/p/15639127.html
Copyright © 2020-2023  润新知