Tab+ViewPager not updating instead shows weird warning expected state 3 found 2
Asked Answered
S

6

8

I have a mainactivity which includes a TabLayout with ViewPager

I have added 3 tabs and each tab has separate fragments which contains a recyclerview, and these recyclerviews has a checkbox that should be simultaneously updated/refreshed whenever i swipe the viewpager(i save checked positions in shared preference and updates via shared preference).

My problem here is whenever i check a checkbox in tab1, tab2 is not updating/refreshing until i scroll down the Recyclerview. and tab3 is working fine. and i am getting a weird warning in logcat too.

 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 Tab3{10a5f1f0 #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 Tab1{2d9aa887 #1 id=0x7f0d00b6} not updated inline; expected state 3 found 2 

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

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

code for each fragments.

 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;
            }
Siliqua answered 5/3, 2016 at 4:42 Comment(17)
So checking something in tab1 is supposed to update tab 2?Kiushu
yes . it should be updated in tab 2 and tab 3, but tab2 is updating only when scroll down therecyclerview and tab 3 is working fine.Siliqua
Well likely whats happening is that going to tab 2 is calling something (maybe adapter.notifyDataSetChanged();) that is updating tab 3.Kiushu
Try placing adapter.notifyDataSetChanged(); in onTabUnselectedKiushu
No. still tab2 is not updating the change. but tab3 does .Siliqua
but if i check a checkbox in tab 2 , tab1 and tab3 is updating ,but it also happening after more than one swipe .Siliqua
Going to need to see more code then beause something to do with the tab changing is triggering updates, but only after arriving in a tab (ie. the current tab doesn't know it needs to update as well)Kiushu
@JoeMaher: when i searched about that error, i got this code.google.com/p/android/issues/… from google . Is that related to my problem.?Siliqua
The thing with viewpagers is that they load the next pages as well. You could override by changing setPageLimit on the viewpager, but the minimum is one. I think loading simultaneous pages with the view pager may be your issue.Muscle
@TaylorCourtney: i tried viewPager1.setOffscreenPageLimit(0); . but still the same resultSiliqua
It can't be zero. It probably used 1.Muscle
I don't see where you are setting the check box position from preferences. Please post that.Muscle
@TaylorCourtney:added the code of fragment.Siliqua
Try using different variable names for each string in the shared preferences in the fragments.Muscle
@JoeMaher @Override public void setMenuVisibility(final boolean visible) { super.setMenuVisibility(visible); if (visible) { Log.e("fragment Visible","true"); }else{ Log.e("fragment Visible","false"); } } i just added this code to my tab2 fragment. and then i called viewPager1.getAdapter().notifyDataSetChanged(); in onTabUnselected as you told. Now everything works correctly, but viewpager is very very slow on swipe..Siliqua
Did you recently update to the support library version 23.2.0? I just did and I got a very similar problem. I've since downgraded to 23.1.1, and I no longer get the issue. May be an issue with the update.Dordrecht
There's an issue in 23.2.0, you can keep an eye here code.google.com/p/android/issues/…Sponsor
T
4

The FragmentPagerAdapter calls setUserVisibleHint(true|false) on neighbourhoods of the active fragment which changes the state of this fragments. This is at least the answer of the "weird warning messages" but it may not solve your problem.

Regarding your comment about how to solve that warning message I have created my own FragmentPagerAdapter as follows:

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

}

The warning message is gone now and currently I do not experience any flaws - but I am still in the middle of research.

Thorny answered 14/3, 2016 at 10:59 Comment(2)
Added example of how to overcome the warning message.Thorny
it shows There is no default constructor available in PagerAdapter.Siliqua
R
2

It is not a important. It is only a warning. Please try to change the background of ViewPager layout to null(not set the background).

Reluctance answered 19/4, 2016 at 5:53 Comment(2)
Could you please elaborate more the solution you provide?Hippolytus
ohk where you have problem?Reluctance
A
2

I spent a lot of time for this issue. Problem is you try to update viewpager in OnPageChangeListener listener, it will throw warning

not update in line

Solution:

All data change and update view should call out of OnPageChangeListener, ex: onCreateView of Fragment

My warning was gone! And viewpager update completelly

Appose answered 3/5, 2016 at 9:24 Comment(3)
i didnt understood it completely, can you pls show me an example ..??Siliqua
@Ruben in onTabSelected, just call 'viewPager1.setCurrentItem(tab.getPosition());' only, notifydatasetChange is in this method is incorrect, it will throw warning and fragment not update inlineAppose
I do not care about warning, but the middle tab is not updating properly. it still shows the old result.Siliqua
I
2

Its late but i figured a best way out using FragmentPagerAdapter , though its not eliminating the warnings, but its solving the purpose, if anyone can inform me what does that warning actually means and whats actually causing the warning, it would be of my best interest.

First in all of the fragments used in the viewpager create method as below.

public void updateView() {
  //Update whatever views or data you want to update
}

Also override method setUserVisibleHint which is used in FragmentPagerAdapter to notify if the fragment is visible to user or not.

 @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        this.isVisibleToUser = isVisibleToUser;
        super.setUserVisibleHint(isVisibleToUser);
    }

finally in your fragment add the following code to update the view/date when the fragment is visible.

@Override
    public void onStart() {
            if (isVisibleToUser)
                updateView();
    }

Then implement this in your TabFragment or Activity

viewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {

                switch (position) {
                    case 0: {
                         ((Fragment) mFragmentList.get(0)).updateView();
                        break;
                    }
                    case 1: {
                         ((Fragment) mFragmentList.get(1)).updateView();
                        break;
                    }
                }
            }

This will take care of update the data consistantly, either you swipe or you click the tab, if anyone finds any issues or bugs, please comment and let me know or feel free to edit the solution

And I have voted for the solution above which gave me approach to apply this solution. Thanks @mikes

Immunize answered 27/5, 2016 at 23:9 Comment(0)
A
2

In view pager adjacent tabs are loaded simultaneously. So your actions in tab 1 will not be reflected in tab 2 since its already loaded. You should be using Broadcast receivers to solve this issue. Broadcast a event in tab 1 and receive the event in tab 2

Acerbate answered 8/6, 2017 at 19:28 Comment(0)
F
1

The "W/FragmentManager: moveToState: … expected state 3 found 2" warning can be ignored and was removed in Support Library v24.0.0.

Quote from the official developer answer:

You don't need to do anything additional; this is an informational log message only. […]

The log itself as described here does not affect behavior. Please open a new bug if you are having other issues; conflating different issues in the same bug makes those separate issues more difficult to track.

Closing comments as the log issue has been resolved for a future release.

Feathering answered 25/7, 2016 at 20:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.