Skip to content Skip to sidebar Skip to footer

Tablayout Scrolls To Unkown Position After Calling Notifydatasetchanged On Pageradapter

I have sample project with TabLayout and PagerAdapter. Strange things happens with TabLayout when I call pagerAdapter.notifyDataSetChanged(); after tabLayout.setupWithViewPager(vie

Solution 1:

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.
 */publicvoid removeAllTabs() {
    // Remove all the viewsfor (int i = mTabStrip.getChildCount() - 1; i >= 0; i--) {
        removeTabViewAt(i);
    }

    for (finalIterator<Tab> 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.

publicclassRetainableTabLayoutextendsTabLayout {

   /*
   * Variable to store invalid position.
   */privatestaticfinalintINVALID_TAB_POS= -1;

   /*
   * Variable to store last selected position, init it with invalid tab position.
   */privateintmLastSelectedTabPosition= INVALID_TAB_POS;

   publicRetainableTabLayout(Context context) {
       super(context);
   }

   publicRetainableTabLayout(Context context, AttributeSet attrs) {
       super(context, attrs);
   }

   publicRetainableTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
      super(context, attrs, defStyleAttr);
  }

   @OverridepublicvoidremoveAllTabs() {
       // Retain last selected position before removing all tabs
       mLastSelectedTabPosition = getSelectedTabPosition();
       super.removeAllTabs();
   }

   @OverridepublicintgetSelectedTabPosition() {
       // Override selected tab position to return your last selected tab positionfinalintselectedTabPositionAtParent=super.getSelectedTabPosition();
       returnselectedTabPositionAtParent== INVALID_TAB_POS ? 
              mLastSelectedTabPosition : selectedTabPositionAtParent;
   }
}

At the end make sure to reselect your tab after recreation of your TabLayout.

findViewById(R.id.fab).setOnClickListener(newView.OnClickListener() {
        @OverridepublicvoidonClick(View v) {
            pagerAdapter.notifyDataSetChanged();
            // At the end make sure to reselect your last item.newHandler().postDelayed(
                    newRunnable() {
                        @Overridepublicvoidrun() {
                            final TabLayout.TabselectedTab= 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.

Solution 2:

You can call:

setupWithViewPager(@Nullable final ViewPager viewPager, boolean autoRefresh)

and set param autoRefresh to false.

See also: TabLayout(ViewPager,boolean)

Solution 3:

I just added notifyDataSetChanged method inside of TabLayout implementation of Rasi.

It works for me.

publicclassCustomTabLayoutextendsTabLayout {

/*
   * Variable to store invalid position.
   */privatestaticfinalintINVALID_TAB_POS= -1;

/*
* Variable to store last selected position, init it with invalid tab position.
*/privateintmLastSelectedTabPosition= INVALID_TAB_POS;

publicCustomTabLayout(Context context) {
    super(context);
}

publicCustomTabLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
}

publicCustomTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

@OverridepublicvoidremoveAllTabs() {
    // Retain last selected position before removing all tabs
    mLastSelectedTabPosition = getSelectedTabPosition();
    super.removeAllTabs();
}

@OverridepublicintgetSelectedTabPosition() {
    // Override selected tab position to return your last selected tab positionfinalintselectedTabPositionAtParent=super.getSelectedTabPosition();
    returnselectedTabPositionAtParent== INVALID_TAB_POS ?
            mLastSelectedTabPosition : selectedTabPositionAtParent;
}

publicvoidnotifyDataSetChanged() {
    post(newRunnable() {
        @Overridepublicvoidrun() {
            TabLayout.TabselectedTab= getTabAt(getSelectedTabPosition());
            if (selectedTab != null) {
                selectedTab.select();
            }
        }
    });
}
}

and to notify call

mPagerAdapter.notifyDataSetChanged();
mCustomTabLayout.notifyDataSetChanged();

Solution 4:

you can directly set the current postion.

you can get the selected position before notifying dataset change and save it in static varaible and update it everytime:

publicstatic tabPostion;

    @OverridepublicvoidonTabSelected(TabLayout.Tab tab) {
      tabPostion = tab.getPosition();
    }

    @OverridepublicvoidonTabUnselected(TabLayout.Tab tab) {

    }

    @OverridepublicvoidonTabReselected(TabLayout.Tab tab) {

    }

//set the postion of the pervious tab
    yourviewpager.setCurrentItem(tabPostion);

below is the code from my project :

@OverridepublicvoidonTabSelected(TabLayout.Tab tab) {

        // imageHashMap = new HashMap<>();onClearOrSelectAllImage(FlickQuickEnum.PIC_SELECTION_ACTION.CLEAR);
        selectedImageUrls = newHashMap<>();
        String tag = tab.getText().toString();
        String tagName = tag.substring(1, tag.length());
        currentTab = tagName;
        if (tagName != null) {
            dbAlbumPhotoList = newArrayList<>();
            if (tagName.equals("All")) {
                dbAlbumPhotoList = dbAlbumPhotosHashMap.get(album.getAlbumName());
            } else {
                dbAlbumPhotoList = dbAlbumPhotosHashMap.get(tagName);
            }
        }
        updatePhotoCount();
        setPhotosSelectedActions(false);
    }

    @OverridepublicvoidonTabUnselected(TabLayout.Tab tab) {

    }

    @OverridepublicvoidonTabReselected(TabLayout.Tab tab) {

    }

Solution 5:

I fix the issue, you need modify the TabLayout source code

voidpopulateFromPagerAdapter() {
    removeAllTabs();

    if (mPagerAdapter != null) {
        finalintadapterCount= mPagerAdapter.getCount();
        for (inti=0; i < adapterCount; i++) {
            addTab(newTab().setText(mPagerAdapter.getPageTitle(i)), false);
        }

        // need call post to run the code, to fix children views not layout
        post(newRunnable() {
            @Overridepublicvoidrun() {
                // Make sure we reflect the currently set ViewPager itemif (mViewPager != null && adapterCount > 0) {
                    finalintcurItem= mViewPager.getCurrentItem();
                    if (curItem != getSelectedTabPosition() && curItem < getTabCount()) {
                        selectTab(getTabAt(curItem));
                    }
                }
            }
        });
    }
}

Post a Comment for "Tablayout Scrolls To Unkown Position After Calling Notifydatasetchanged On Pageradapter"