I have sample project with TabLayout
and PagerAdapter
.
Strange things happens with TabLayout when I call pagerAdapter.notifyDataSetChanged();
Every time setup view pager with the use of setupWithViewPager
might be costly. Because notifyDataSetChanged()
on your ViewPager Adapter causing TabLayout to redraw its all views. So for redrawing, TabLayout
remove all its associated Views
and re add them.
Please check below thread execution steps which happen after notifyDataSetChanged
on pager adapter.
at android.support.design.widget.TabLayout.removeAllTabs(TabLayout.java:654)
at android.support.design.widget.TabLayout.populateFromPagerAdapter(TabLayout.java:904)
at android.support.design.widget.TabLayout$PagerAdapterObserver.onChanged(TabLayout.java:2211)
at android.database.DataSetObservable.notifyChanged(DataSetObservable.java:37)
- locked <0x129f> (a java.util.ArrayList)
at android.support.v4.view.PagerAdapter.notifyDataSetChanged(PagerAdapter.java:287)
at com.sample.testtablayout.MainActivity$1.onClick(MainActivity.java:45)
According to thread execution -
Clicked on Action Button > Pager Adapter Notified for data set changed > TabLayout got notification of data change through observers > It try to populate new data from adapter > Removed all tabs.
Below is a code of TabLayout
class, from which you can check whenever all tabs are going to remove then last selected tab is lost(Intentionally marked null).
/**
* Remove all tabs from the action bar and deselect the current tab.
*/
public void removeAllTabs() {
// Remove all the views
for (int i = mTabStrip.getChildCount() - 1; i >= 0; i--) {
removeTabViewAt(i);
}
for (final Iterator i = mTabs.iterator(); i.hasNext();) {
final Tab tab = i.next();
i.remove();
tab.reset();
sTabPool.release(tab);
}
mSelectedTab = null; // Thats a cause for your issue.
}
To retain last selected tab I have created my own CustomTabLayout class and retained last selected position.
public class RetainableTabLayout extends TabLayout {
/*
* Variable to store invalid position.
*/
private static final int INVALID_TAB_POS = -1;
/*
* Variable to store last selected position, init it with invalid tab position.
*/
private int mLastSelectedTabPosition = INVALID_TAB_POS;
public RetainableTabLayout(Context context) {
super(context);
}
public RetainableTabLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RetainableTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public void removeAllTabs() {
// Retain last selected position before removing all tabs
mLastSelectedTabPosition = getSelectedTabPosition();
super.removeAllTabs();
}
@Override
public int getSelectedTabPosition() {
// Override selected tab position to return your last selected tab position
final int selectedTabPositionAtParent = super.getSelectedTabPosition();
return selectedTabPositionAtParent == INVALID_TAB_POS ?
mLastSelectedTabPosition : selectedTabPositionAtParent;
}
}
At the end make sure to reselect your tab after recreation of your TabLayout
.
findViewById(R.id.fab).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
pagerAdapter.notifyDataSetChanged();
// At the end make sure to reselect your last item.
new Handler().postDelayed(
new Runnable() {
@Override
public void run() {
final TabLayout.Tab selectedTab = tabLayout.getTabAt(
tabLayout.getSelectedTabPosition());
if (selectedTab != null) {
selectedTab.select();
}
}
}, 100);
}
});
This issue will resolve your problem but what I believe is TabLayout
must have to retain last selected position in case of data set change. This is what I understand, any comments or more understanding is welcome.