ViewPager+Fragment实现Tab动态添加和移除
一、需求
在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 = mutableListOf()
private var count = 10
private var mCurTransaction:FragmentTransaction? = null
private var tags= mutableListOf()
fun clearFragments(){
fragments.clear()
notifyDataSetChanged()
}
fun setFragments(frags: List) {
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) }
}