Android 在PagerAdapter上调用notifyDataSetChanged后,TableLayout滚动到未知位置

Android 在PagerAdapter上调用notifyDataSetChanged后,TableLayout滚动到未知位置,android,android-tablayout,Android,Android Tablayout,我有一个带有TabLayout和PagerAdapter的示例项目。 调用pagerAdapter.notifyDataSetChanged()时,TableLayout会发生奇怪的事情在表格布局之后。使用viewPager(viewPager)设置 TabLayout正在滚动到未知的x位置,因此当前选项卡不可见。但是,如果我向左滚动到预期选项卡,则该选项卡具有指示器 发生了什么事?有人能帮我吗?我花在这上面的时间太多了 下面是代码 public class MainActivity exte

我有一个带有
TabLayout
PagerAdapter
的示例项目。 调用
pagerAdapter.notifyDataSetChanged()时,TableLayout会发生奇怪的事情在<代码>表格布局之后。使用viewPager(viewPager)设置

TabLayout正在滚动到未知的x位置,因此当前选项卡不可见。但是,如果我向左滚动到预期选项卡,则该选项卡具有指示器

发生了什么事?有人能帮我吗?我花在这上面的时间太多了

下面是代码

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Get the ViewPager and set it's PagerAdapter so that it can display items
        ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
        final SampleFragmentPagerAdapter pagerAdapter = new SampleFragmentPagerAdapter(getSupportFragmentManager(), MainActivity.this);
        viewPager.setAdapter(pagerAdapter);

        // Give the TabLayout the ViewPager
        TabLayout tabLayout = (TabLayout) findViewById(R.id.sliding_tabs);
        tabLayout.setupWithViewPager(viewPager);

        findViewById(R.id.fab).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                pagerAdapter.notifyDataSetChanged();
            }
        });
    }

}
我在nexus模拟器和nexus real设备(api 21+)上进行了测试

渐变设置:

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "xx.xxx.myapplication4"
        minSdkVersion 21
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

链接到报告的问题并准备作为附件测试项目

您可以直接设置当前位置

您可以在通知数据集更改之前获取所选位置,并将其保存在静态变量中,并每次更新:

public static tabPostion;

    @Override
    public void onTabSelected(TabLayout.Tab tab) {
      tabPostion = tab.getPosition();
    }

    @Override
    public void onTabUnselected(TabLayout.Tab tab) {

    }

    @Override
    public void onTabReselected(TabLayout.Tab tab) {

    }

//set the postion of the pervious tab
    yourviewpager.setCurrentItem(tabPostion);
以下是我的项目中的代码:

   @Override
    public void onTabSelected(TabLayout.Tab tab) {

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

    @Override
    public void onTabUnselected(TabLayout.Tab tab) {

    }

    @Override
    public void onTabReselected(TabLayout.Tab tab) {

    }
@覆盖
已选择的公共选项卡(TabLayout.Tab){
//imageHashMap=新建HashMap();
onClear或SelectalImage(flickum.PIC\u SELECTION\u ACTION.CLEAR);
selectedImageUrls=newHashMap();
String tag=tab.getText().toString();
字符串标记名=tag.substring(1,tag.length());
currentTab=标记名;
if(标记名!=null){
dbAlbumPhotoList=新的ArrayList();
if(标记名.equals(“全部”)){
dbAlbumPhotoList=dbAlbumPhotosHashMap.get(album.getAlbumName());
}否则{
dbAlbumPhotoList=dbAlbumPhotosHashMap.get(标记名);
}
}
updatePhotoCount();
SetPhotoselectedActions(假);
}
@凌驾
已选择的公共选项卡(TabLayout.Tab){
}
@凌驾
已重新选择公共选项卡(TabLayout.Tab){
}

每次使用
setupWithViewPager
设置查看寻呼机时,成本可能会很高。因为ViewPager适配器上的
notifyDataSetChanged()
,导致TableLayout重新绘制其所有视图。因此,对于重绘,
TabLayout
删除所有相关的
视图
并重新添加它们

请检查寻呼机适配器上
notifyDataSetChanged
之后发生的以下线程执行步骤

  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)
为了保留上次选择的选项卡,我创建了自己的CustomTableLayout类并保留了上次选择的位置

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;
   }
}
最后,确保在重新创建
表格布局后重新选择选项卡

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);
        }
    });

此问题将解决您的问题,但我认为,
TabLayout
必须保留上次选择的位置,以防数据集更改。这就是我所理解的,欢迎任何评论或更多的理解。

我刚刚在Rasi的TableLayout实现中添加了notifyDataSetChanged方法

它对我有用

public class CustomTabLayout 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 CustomTabLayout(Context context) {
    super(context);
}

public CustomTabLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public CustomTabLayout(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;
}

public void notifyDataSetChanged() {
    post(new Runnable() {
        @Override
        public void run() {
            TabLayout.Tab selectedTab = getTabAt(getSelectedTabPosition());
            if (selectedTab != null) {
                selectedTab.select();
            }
        }
    });
}
}
并通知电话

mPagerAdapter.notifyDataSetChanged();
mCustomTabLayout.notifyDataSetChanged();
你可以致电:

setupWithViewPager(@Nullable final ViewPager viewPager, boolean autoRefresh)
并将param
autoRefresh
设置为
false


另请参见:

如果要解决此问题,您需要修改TabLayout源代码

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);
        }

        // need call post to run the code, to fix children views not layout
        post(new Runnable() {
            @Override
            public void run() {
                // 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));
                    }
                }
            }
        });
    }
}
void populateFromPagerAdapter(){
移除所有选项卡();
if(mPagerAdapter!=null){
final int adapterCount=mPagerAdapter.getCount();
对于(int i=0;i0){
final int curItem=mViewPager.getCurrentItem();
if(curItem!=getSelectedTabPosition()&&curItem
你能发布那个项目吗?点击蓝色链接
这里
-我帖子的最后一段错误来自第行:如果没有
-(getWidth()/2)
它会按预期工作。您可以在从方法返回并将
scrollBase
值更改为正确值之前放置一个断点。看。很好的一个,我能设法破解它吗?我试着通过反射来实现,但没有足够的接缝来实现。尽管如此,
-width
还是有道理的,这意味着你会通过改变它来破坏其他东西。我想应该有一些if-else语句。是的,这段代码来自我的项目,它工作正常。我还刷新了一些操作选项卡的UI,但我没有使用notifydatasetchange,相反,我使用Eventbus根据我的need@deadfish我已经在我的应用程序中使用了这段代码,它在fineAfter中运行良好。notifyDataSetChanged无需创建新的处理程序()和设置延迟。TabLayout包含处理程序。使用tabLayout.post()非常感谢您的回答@Mohammadnabil我的荣幸您也可以使用该库如果您有一个带有片段的自定义viewPager的表格布局,此解决方案将非常有效。我需要更新片段UI,但它在onTabSelected中崩溃了,因为它重新绘制了选项卡内容。我花了大约3个小时来实现这一点。重置viewPager在内存管理方面不是很昂贵吗?这就是notifyDataSetChanged()出现在图片中的原因。这似乎是Rahul现有答案的更清晰版本,如上所示:
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);
        }

        // need call post to run the code, to fix children views not layout
        post(new Runnable() {
            @Override
            public void run() {
                // 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));
                    }
                }
            }
        });
    }
}