3

I have a ViewPager with 3 tabs, each tab loads a fragment, new instance of the same fragment class but with an extra parameter.

Below you can find the fragment class, I have a RecyclerView in that fragment, I am trying to open a context menu when the user long presses on an item in the RecyclerView.

I am able to display the context menu and get the position of the selected item for a single instance of a fragment, when I swipe to the next fragment I am getting the same index position, when I debugged further I find that I am getting the same adapter for the RecyclerView on the previous fragment. This can be seen when I log the adapter size for that RecyclerView.

Can anyone help me understand why I am getting the previous adapter data when I have swiped to a completely different instance of that fragment in the ViewPager, even when I can see the correct data in the Fragment and get the correct values in the onBindViewHolder method.

Below you can find the simplified code for the fragment class

public class HistoryFragment extends Fragment{
    private static final String LOG_TAG = HistoryFragment.class.getSimpleName();

    private RecyclerView mRecyclerView;
    private HistoryAdapter mHistoryAdapter;
    private List<Group> mGroups;

    private String mGroupType;

    /**
     * tried using a static int value to track which context menu was opened and on which fragment
     * refer post https://caughtinthemobileweb.wordpress.com/2013/10/22/android-viewpager-holds-reference-to-old-fragments/
     * BUG LOGGED AT https://code.google.com/p/android/issues/detail?id=19211
     * does not work :(
     */
    /*private static int selectedContextMenu;*/ // returns valid index as int is static, but adapter still has old data

    /**
     * The fragment argument for this fragment.
     */
    private static final String ARG_GROUP_TYPE = "group_type";

    public HistoryFragment() { }

    /**
     * Returns a new instance of this fragment
     */
    public static HistoryFragment newInstance(String groupType) {
        HistoryFragment fragment = new HistoryFragment();
        Bundle args = new Bundle();
        args.putString(ARG_GROUP_TYPE, groupType);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_history, container, false);
        mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerViewHistoryItem);
        RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
        mRecyclerView.setLayoutManager(layoutManager);
        mGroups = new ArrayList<>();
        mHistoryAdapter = new HistoryAdapter(getActivity(), mGroups);
        mRecyclerView.setAdapter(mHistoryAdapter);


        Bundle bundle = getArguments();
        mGroupType = bundle.getString(ARG_GROUP_TYPE);
        if(mGroupType != null && !mGroupType.equals("")){
            loadDataFromDb();
        } else {
            if(IS_DEBUG_ON) Log.d(LOG_TAG, "invalid fragment arguments");
            getActivity().finish();
        }
        return rootView;
    }

    @Override
    public boolean onContextItemSelected(MenuItem item){
        // int id = item.getItemId();
        Log.d(LOG_TAG, "position2: " + mHistoryAdapter.getSelectedItemPosition() + 
                " " + mGroups.get(mHistoryAdapter.getSelectedItemPosition()).getName()); // returns old data
        Log.d(LOG_TAG, "size2: " + mHistoryAdapter.getItemCount()); // returns old data
        // Log.d(LOG_TAG, "position3: " + selectedContextMenu); // returns correct index, but cant use as adapter still has old data
        Log.d(LOG_TAG, "position4: " + ((HistoryAdapter) mRecyclerView.getAdapter()).getSelectedItemPosition()); // returns old data
        return true;
    }

    private void loadDataFromDb(){
        new LoadGroups().execute();
    }

    class HistoryAdapter extends RecyclerView.Adapter<HistoryFragment.HistoryAdapter.ViewHolder> {
        private Context mContext;
        private List<Group> mList;
        private int mSelectedItemPosition;

        HistoryAdapter(Context mContext, List<Group> mList) {
            this.mContext = mContext;
            this.mList = mList;
        }

        class ViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener {
            View mView;
            TextView mTextViewName;
            TextView mTextViewDescription;
            ImageView mImageViewProfile;
            ImageView mImageCheck;

            public ViewHolder(View itemView) {
                super(itemView);
                this.mView = itemView;
                this.mTextViewName = (TextView) itemView.findViewById(R.id.textViewGroupName);
                this.mTextViewDescription = (TextView) itemView.findViewById(R.id.textViewGroupDescription);
                this.mImageViewProfile = (ImageView) itemView.findViewById(R.id.groupImage);
                this.mImageCheck = (ImageView) itemView.findViewById(R.id.imageCheck);
                this.mView.setOnCreateContextMenuListener(this); // registering listener
            }

            @Override
            public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
                if(mGroupType.equals(GroupContract.GroupEntry.GROUP_TYPE_HISTORY_GROUP)){
                    menu.add(Menu.NONE, R.id.action_resubscribed_group, Menu.NONE, R.string.action_resubscribed_group);
                } else if(mGroupType.equals(GroupContract.GroupEntry.GROUP_TYPE_SUBSCRIBED_GROUP)){
                    if(!mList.get(getAdapterPosition()).isDefault()){
                        menu.add(Menu.NONE, R.id.action_unsubscribe_group, Menu.NONE, R.string.action_unsubscribe_group);
                    }
                    menu.add(Menu.NONE, R.id.action_mute, Menu.NONE, R.string.action_mute);
                    menu.add(Menu.NONE, R.id.action_view_group_info, Menu.NONE, R.string.action_view_group_info);

                }
            }
        }

        @Override
        public HistoryAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(mContext).inflate(R.layout.recycler_view_history, parent, false);
            return new HistoryAdapter.ViewHolder(view);
        }

        @Override
        public void onBindViewHolder(final HistoryAdapter.ViewHolder holder, final int position) {
            if(mGroupType.equals(GroupContract.GroupEntry.GROUP_TYPE_SUBSCRIBED_GROUP)){
                holder.mImageCheck.setVisibility(View.VISIBLE);
            } else {
                holder.mImageCheck.setVisibility(View.GONE);
            }
            holder.mTextViewName.setText(mList.get(position).getName());
            holder.mTextViewDescription.setText(mList.get(position).getDescription());

            holder.mView.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    // mSelectedItemPosition = position; // tried this, returns correct 
                    // selectedContextMenu = position; // also tried this, returns correct
                    mSelectedItemPosition = holder.getAdapterPosition(); // this also returns correct 
                    Log.d(LOG_TAG, "position1 - onBind: " + mSelectedItemPosition + " "+ mList.get(mSelectedItemPosition).getName()); // returns correct data
                    return false;
                }
            });

        }

        @Override
        public void onViewRecycled(ViewHolder holder) {
            holder.mView.setOnLongClickListener(null); // clearing listener to avoid collision between list items
            super.onViewRecycled(holder);
        }

        @Override
        public int getItemViewType(int position) {
            return 0;
        }

        @Override
        public int getItemCount() {
            return mList.size();
        }

        int getSelectedItemPosition() {
            Log.d(LOG_TAG, "getter: " + mSelectedItemPosition); // this returns wrong data, shows that fragment is referring old adapter data
            return mSelectedItemPosition;
        }
    }

    // get data from db
    class LoadGroups extends AsyncTask<Void, Void, Void>{

        @Override
        protected Void doInBackground(Void... voids) {
            mGroups = GroupContract.getGroupEntriesFromDB(getActivity(), mGroupType);
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);

            mHistoryAdapter = new HistoryAdapter(getActivity(), mGroups);
            mRecyclerView.setAdapter(mHistoryAdapter);
        }
    }
}

Below you can find the ViewPager Adapter class

public class HomeTabsPagerAdapter extends FragmentPagerAdapter {
    private Context mContext;

    HomeTabsPagerAdapter(Context context, FragmentManager fm) {
        super(fm);
        mContext = context;
    }

    @Override
    public Fragment getItem(int position) {
        switch (position) {
            case 0:
                return HistoryFragment.newInstance("TYPE_1");
            case 1:
                return HistoryFragment.newInstance("TYPE_2");
            case 2:
                return HistoryFragment.newInstance("TYPE_3");
        }
        return null;
    }

    @Override
    public int getCount() {
        return 3;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        switch (position) {
            case 0:
                return mContext.getString(R.string.text_type_1);
            case 1:
                return mContext.getString(R.string.text_type_2);
            case 2:
                return mContext.getString(R.string.text_type_3);
        }
        return null;
    }

    // Doesn't work even when I use this or not
    /*@Override
    public int getItemPosition(Object object) {
        // try http://stackoverflow.com/a/8024557
        // return super.getItemPosition(object);
        return POSITION_NONE;
    }*/
}
John
  • 465
  • 5
  • 15

2 Answers2

0

I suspect, the use of RecyclerView.LayoutManager can be an issue, because you have a static class and the first two created fragments will have the same content (of the second one). The same is for the next fragments . Further, the getItemPosition(Object object) -> return POSITION_NONE is only usable with the FragmentStatePagerAdapter . The FragmentAdapter you have never changes the fragments, once created. So, I'd suggest instead of :

RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false); mRecyclerView.setLayoutManager(layoutManager);

to use

mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

//or set an GridLayoutManager instead

use FragmentStatePagerAdapter if you plan to update the content of the fragments.

Helmwag
  • 460
  • 5
  • 7
0

I was having a similar issue and fixed it by putting this on my fragment:

@Override
public void onDetach() {
    super.onDetach();
    recyclerView.getRecycledViewPool().clear();
}

In your case you can call getRecycledViewPool when you change the tab.