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