一、需求
在viewpager+fragment+tablayout中根据权限动态设置显示/隐藏某个tab。
二、背景
一个问题断断续续解决了好几天,明明感觉很简单的需求,就是会遇到各种问题,而且错误都能在源码中看到,实在是解决太久了,人烦了,照搬网上的实现,但是每个人遇到的问题不一定一样,还是要根据实际情况来定。
这个问题过程中遇到很多错误。
- 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) } }