Tab+ViewPager 不更新而是显示奇怪的警告预期状态 3 found 2

Posted

技术标签:

【中文标题】Tab+ViewPager 不更新而是显示奇怪的警告预期状态 3 found 2【英文标题】:Tab+ViewPager not updating instead shows weird warning expected state 3 found 2 【发布时间】:2016-06-19 00:26:34 【问题描述】:

我有一个 mainactivity,其中包括一个带有 ViewPager 的 TabLayout

我添加了 3 个选项卡,每个选项卡都有单独的片段,其中包含一个 recyclerview,这些 recyclerviews 有一个复选框,每当我滑动 viewpager 时都应该同时更新/刷新(我将选中的位置保存在共享首选项和更新中通过共享偏好)。

我的问题是每当我选中 tab1 中的复选框时,tab2 不会更新/刷新,直到我向下滚动 Recyclerview。 tab3 工作正常。 我也在 logcat 中收到了一个奇怪的警告。

 03-05 09:35:53.345 4317-4327/com.example.rubin W/art: Suspending all threads took: 6.805ms
    03-05 09:35:58.310 4317-4317/com.example.rubin W/FragmentManager: moveToState: Fragment state for Tab310a5f1f0 #2 id=0x7f0d00b6 not updated inline; expected state 3 found 2
    03-05 09:36:01.363 4317-4317/com.example.rubin W/FragmentManager: moveToState: Fragment state for Tab12d9aa887 #1 id=0x7f0d00b6 not updated inline; expected state 3 found 2 

我的 PagerAdapter

public class PagerAdapter1 extends FragmentStatePagerAdapter 
    int mNumOfTabs;
    public PagerAdapter1(FragmentManager fm, int NumOfTabs) 
        super(fm);
        this.mNumOfTabs = NumOfTabs;
    
    @Override
    public Fragment getItem(int position) 
        switch (position) 
            case 0:
               return new Tab1();
            case 1:
               return new Tab2();
            case 2:
               return new Tab3();

            default:
                return null;

        
    

    @Override
    public int getCount() 
        return mNumOfTabs;
    

标签布局 OnPageChangelistener

final PagerAdapter1 adapter = new PagerAdapter1
                (getSupportFragmentManager(), tabLayout1.getTabCount(), getApplicationContext());
        viewPager1.setAdapter(adapter);
        viewPager1.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout1));
        tabLayout1.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() 
            @Override
            public void onTabSelected(TabLayout.Tab tab) 
                viewPager1.setCurrentItem(tab.getPosition());
                adapter.notifyDataSetChanged();
            

            @Override
            public void onTabUnselected(TabLayout.Tab tab) 

            

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

每个片段的代码。

 public class Tab1 extends Fragment 

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
            View v = inflater.inflate(R.layout.home_tab1_recycler, container, false);
            RecyclerView rv = (RecyclerView) v.findViewById(R.id.home_recyclerview);   
            LinearLayoutManager llm = new LinearLayoutManager(getContext());
            rv.setLayoutManager(llm);
            rv.setHasFixedSize(true); // to improve performance
            rv.setAdapter(new HomeManager()); // the projectdatabase manager is assigner to the RV
            return v;
        

        public class HomeManager extends RecyclerView.Adapter<HomeManager.RecyclerViewHolder> 
    int Length,h;
 View v1;
        ArrayList<String> PROJECT_ID = new ArrayList<String>();
Set<String> set;
        List<String> selected;
     public class RecyclerViewHolder extends RecyclerView.ViewHolder 
     CheckBox mCheck;
      RecyclerViewHolder(final View itemView) 
                    super(itemView);
      mCheck = (CheckBox) itemView.findViewById(R.id.PROJECT_fav);
     SharedPreferences pref = getContext().getSharedPreferences("MirSP", Context.MODE_PRIVATE);
                    set = pref.getStringSet("FAV", null);
      if (set != null) 
                        selected = new ArrayList<String>(set);
                     else 
                        selected = new ArrayList<String>();
                    
    mCheck.setOnClickListener(new View.OnClickListener() 
                        @Override
                        public void onClick(View v) 
     h = getAdapterPosition();
    check = PROJECT_ID.get(h); // for saving the project id from json.
     if (selected.contains(check)) 
                                    selected.remove(check);
                                    mCheck.setBackgroundResource(R.drawable.ic_favorite_white1_24dp);
                                    Snackbar snackbar = Snackbar.make(v, "Property Unfavorited", Snackbar.LENGTH_SHORT);
                                    snackbar.show();
                                 else 
                                    selected.add(check);
                                    mCheck.setBackgroundResource(R.drawable.ic_favorite_white_24dp);
                                    Snackbar snackbar = Snackbar.make(v, "Property Favorited", Snackbar.LENGTH_SHORT);
                                    snackbar.show();

                                
                                Log.e("HF update checked", String.valueOf(selected));
                                Set<String> set = new HashSet<String>();
                                set.addAll(selected);
                                SharedPreferences pref = getContext().getSharedPreferences("MirSP", Context.MODE_PRIVATE);
                                SharedPreferences.Editor editor = pref.edit();
                                editor.putStringSet("FAV", set);
                                editor.commit();
                            
         
                    );
     @Override
            public RecyclerViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) 
                v1 = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recyclerview_item, viewGroup, false);
                return new RecyclerViewHolder(v1);
            

            @Override
            public void onBindViewHolder(final RecyclerViewHolder viewHolder, int i) 
    SharedPreferences pref = getContext().getSharedPreferences("MirSP", Context.MODE_PRIVATE);
                set = pref.getStringSet("FAV", null);
                if (set != null) 
                    selected = new ArrayList<String>(set);
                 else 
                    selected = new ArrayList<String>();
                
                Log.e("HF update UI", String.valueOf(selected));
    if (String.valueOf(selected).contains(String.valueOf(PROJECT_ID.get(i)))) 
                        viewHolder.mCheck.setBackgroundResource(R.drawable.ic_favorite_white_24dp);
                     else 
                        viewHolder.mCheck.setBackgroundResource(R.drawable.ic_favorite_white1_24dp);
                    
                
     @Override
            public int getItemCount() 
                //Code for Total length of json
                return Length;
            

【问题讨论】:

那么在 tab1 中检查某些内容应该会更新 tab 2? 很可能发生的事情是转到选项卡 2 正在调用正在更新选项卡 3 的东西(可能是 adapter.notifyDataSetChanged();)。 尝试将adapter.notifyDataSetChanged(); 放入onTabUnselected 您最近是否更新到支持库版本 23.2.0?我刚刚做了,我遇到了一个非常相似的问题。我已经降级到 23.1.1,我不再遇到这个问题。可能是更新的问题。 23.2.0出现问题,可以关注这里code.google.com/p/android/issues/… 【参考方案1】:

FragmentPagerAdapter 在活动片段的邻域上调用setUserVisibleHint(true|false),这会改变该片段的状态。 这至少是“奇怪的警告消息”的答案,但它可能无法解决您的问题。

关于您对如何解决该警告消息的评论,我创建了自己的 FragmentPagerAdapter,如下所示:

public abstract class AbstractTabPagerAdapter extends PagerAdapter 

    private static final String TAG = AbstractTabPagerAdapter.class.getCanonicalName();

    private final FragmentManager mFragmentManager;

    private FragmentTransaction mCurTransaction;

    private Fragment mCurrentPrimaryItem = null;

    public AbstractTabPagerAdapter(FragmentManager fragmentManager) 
        mFragmentManager = fragmentManager;
    

    @Override
    public Object instantiateItem(ViewGroup container, int position) 
        if (mCurTransaction == null) 
            throw new IllegalArgumentException("current transaction must not be null");
        
        String fragmentTag = makeFragmentName(container.getId(), position);
        Fragment fragment = (Fragment) mFragmentManager.findFragmentByTag(fragmentTag);
        if (fragment != null) 
            mCurTransaction.attach(fragment);
            Log.d(TAG, "Attaching existing fragment " + fragment + " at position " + position);
            //mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), position));
         else 
            fragment = getItem(position);
            mCurTransaction.add(container.getId(), fragment, fragmentTag);
            Log.d(TAG, "Attaching new fragment " + fragment + " at position " + position);
        

        if (fragment != mCurrentPrimaryItem) 
            fragment.setMenuVisibility(false);
            //fragment.setUserVisibleHint(false);
        

        return fragment;
    

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) 
        if (mCurTransaction == null) 
            throw new IllegalArgumentException("current transaction must not be null");
        
        mCurTransaction.detach((Fragment) object);
        //mCurTransaction.remove((Fragment)object);
    

    @Override
    public void setPrimaryItem(ViewGroup container, int position, Object object) 
        //super.setPrimaryItem(container, position, object);
        Fragment fragment = (Fragment) object;
        if (fragment != mCurrentPrimaryItem) 
            Log.d(TAG, "set Primary item " + position + " to " + fragment);
            if (mCurrentPrimaryItem != null) 
                mCurrentPrimaryItem.setMenuVisibility(false);
                // this command unexpectedly changes the state of the fragment which leads to a warning message and possible some strange behaviour
                //mCurrentPrimaryItem.setUserVisibleHint(false);
            
            if (fragment != null) 
                fragment.setMenuVisibility(true);
                //fragment.setUserVisibleHint(true);
            
            mCurrentPrimaryItem = fragment;
        
    

    @Override
    public boolean isViewFromObject(View view, Object fragment) 
        return ((Fragment) fragment).getView() == view;
    

    public abstract Fragment getItem(int position);

    @Override
    public void startUpdate(ViewGroup container) 
        super.startUpdate(container);
        if (mCurTransaction != null) 
            throw new IllegalArgumentException("current transaction must not be null");
        
        mCurTransaction = mFragmentManager.beginTransaction();
        Log.d(TAG, "FragmentTransaction started");
    

    @Override
    public void finishUpdate(ViewGroup container) 
        if (mCurTransaction != null) 
            mCurTransaction.commit();
            mCurTransaction = null;
            //mFragmentManager.executePendingTransactions();
            Log.d(TAG, "FragmentTransaction committed");
         else 
            throw new IllegalArgumentException("current transaction must not be null");
        
    

    private String makeFragmentName(int viewId, int position) 
        if (viewId <= 0)
            throw new IllegalArgumentException("viewId " + viewId);
        return "tabpageradptr:" + getPageTitle(position) + ":" + viewId + ":" + position;
    


警告信息现在消失了,目前我没有遇到任何缺陷 - 但我仍在研究中。

【讨论】:

添加了如何克服警告消息的示例。 谢谢mike,我会在尝试后反馈。 它显示PagerAdapter中没有可用的默认构造函数。【参考方案2】:

我花了很多时间来解决这个问题。问题是你尝试在 OnPageChangeListener 监听器中更新 viewpager,它会抛出警告

不在线更新

解决方案:

所有数据更改和更新视图都应调用 OnPageChangeListener, 例如:片段的onCreateView

我的警告消失了!并且viewpager完全更新

【讨论】:

我没有完全理解,你能给我举个例子吗??? @Ruben 在 onTabSelected,只需调用 'viewPager1.setCurrentItem(tab.getPosition());'只是,notifydatasetChange在这个方法中是不正确的,它会抛出警告和fragment not update inline 我不关心警告,但中间选项卡没有正确更新。它仍然显示旧的结果。【参考方案3】:

它已经晚了,但我想出了一个使用 FragmentPagerAdapter 的最佳方法,虽然它没有消除警告,但它解决了目的,如果有人能告诉我该警告的实际含义以及实际导致警告的原因,它将是我的最大兴趣。

viewpager create 方法中使用的所有片段中的第一个,如下所示。

公共无效更新视图() //更新您要更新的任何视图或数据

还覆盖方法 setUserVisibleHint,该方法在 FragmentPagerAdapter 中用于通知片段是否对用户可见。

@覆盖 公共无效 setUserVisibleHint(boolean isVisibleToUser) this.isVisibleToUser = isVisibleToUser; super.setUserVisibleHint(isVisibleToUser);

最后在您的片段中添加以下代码以在片段可见时更新视图/日期。

@覆盖 公共无效 onStart() if (isVisibleToUser) 更新视图();

然后在你的 TabFragment 或 Activity 中实现它

viewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() @覆盖 public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) @覆盖 公共无效 onPageSelected(int position) 开关(位置) 案例0: ((Fragment) mFragmentList.get(0)).updateView(); 休息; 情况1: ((Fragment) mFragmentList.get(1)).updateView(); 休息;

这将负责一致地更新数据,无论是滑动还是单击选项卡,如果有人发现任何问题或错误,请发表评论并让我知道或随时编辑解决方案

我已经投票支持上面的解决方案,它为我提供了应用此解决方案的方法。谢谢@mikes

【讨论】:

【参考方案4】:

这并不重要。这只是一个警告。 请尝试将ViewPager布局的背景更改为null(不设置背景)。

【讨论】:

能否详细说明您提供的解决方案? 哦,你有什么问题吗?【参考方案5】:

在视图分页器中,相邻的选项卡是同时加载的。因此,您在选项卡 1 中的操作将不会反映在选项卡 2 中,因为它已经加载。您应该使用广播接收器来解决此问题。在标签 1 中广播事件并在标签 2 中接收事件

【讨论】:

【参考方案6】:

"W/FragmentManager: moveToState: … expected state 3 found 2" 警告可以忽略并在支持库 v24.0.0 中被删除。

引用官方开发者answer:

你不需要做任何额外的事情;这只是一条信息性日志消息。 […]

此处描述的日志本身不会影响行为。请打开 如果您遇到其他问题,则会出现新错误;将不同的问题混为一谈 在同一个错误中使这些单独的问题更难以跟踪。

由于日志问题已在未来解决,因此关闭 cmets 释放。

【讨论】:

以上是关于Tab+ViewPager 不更新而是显示奇怪的警告预期状态 3 found 2的主要内容,如果未能解决你的问题,请参考以下文章

fragment中嵌套viewpager,vierpager中有多个fragment,不显示 ...

使用FragmentTabHost+TabLayout+ViewPager实现双层嵌套Tab

在不更改选定选项卡的情况下动态更新 TabLayout

tab界面实现方式之viewpager+view

TabLayout关联ViewPager后不显示文字

TabLayout关联ViewPager后不显示文字