简体   繁体   English

RecyclerView has an instance for each item of a list

[英]RecyclerView has an instance for each item of a list

I have a fragment which shows a list of tv show episodes , swiping left or right will switch to the next or previous episode(this is done by ViewPager), then I created a horizontal RecyclerView to show the list of the episodes in a horizontal scroll bar.

Now I have 2 problems

Problem 1 For example I selected episode 9, now episode 9 is highlighted in the RecyclerView Scroll bar then I scroll until I reach 14, 15, 17 (this is the last scroll state of 9) then I click on 14 it gets highlighted but the scroll resets to 1 (which is not what I want) , now I scroll back and I see 14 is highlighted , now when I scroll back to 9 and tap on it , the scroll state of 9 goes to the last state that I scrolled to 15 and clicked on it. Please check this video

Problem 2 For example I selected 2 now I tap on 3, it switches to 3 but the highlight is still on 2 I have to click again on 3 to get it highlighted. please take a look at this video

I think this is all caused by the fact that each episode or I should say each page of ViewPager has its own instance of the RecyclerView. correct me if I'm wrong

But what I want is a single instance of RecyclerView to be available for all episodes.

How can I Fix that?

Hint RecyclerView Adapter is set inside the fragment, transfering the index of the clicked item (episode) from the adapter to the fragment via onItemClick interface from the adapter. From inside the fragment I use another interface sendText(int position); to tranfer the index of the clicked item to the Activity so that I can set ViewPager mViewPager.setCurrentItem(position,false);

RecyclerView Adapter

public class PlanetAdapter extends RecyclerView.Adapter<PlanetAdapter.PlanetViewHolder> {


    public interface OnItemClickListener {
        void onItemClick(String item);
    }
    private static int lastClickedPosition = -1;
    private int selectedItem;

    private ArrayList<String> episodeslist;
    private OnItemClickListener listener;


    SharedPreferences getPref1x1 = getContext().getSharedPreferences("PlanetAdapter", Context.MODE_PRIVATE);
    int pos1x1 = getPref1x1.getInt("position",0);

    int selectedPosition=pos1x1;


    public PlanetAdapter(ArrayList<String> episodeslist, OnItemClickListener listener) {
        this.episodeslist = episodeslist;
        this.listener = listener;
    }

    @Override
    public PlanetAdapter.PlanetViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v= LayoutInflater.from(parent.getContext()).inflate(R.layout.planet_row, parent,false);
        PlanetViewHolder vh=new PlanetViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(PlanetAdapter.PlanetViewHolder vh, int position) {
        TextView tv = (TextView) vh.itemView;
        tv.setText(episodeslist.get(position));
        tv.setCompoundDrawablesWithIntrinsicBounds(R.drawable.bg, 0, 0, 0);

        vh.bind(episodeslist.get(position), listener);


        //if(selectedPosition==position)
        //    vh.itemView.setBackgroundColor(getContext().getResources().getColor(R.color.colorPrimaryLight));
        //else
        //    vh.itemView.setBackgroundColor(getContext().getResources().getColor(R.color.colorPrimaryDark));

        vh.itemView.setBackgroundColor(getContext().getResources().getColor(R.color.colorPrimaryLight));

        if (selectedPosition == position) {
            vh.itemView.setBackgroundColor(getContext().getResources().getColor(R.color.colorPrimaryDark));
        }



        //holder.image.setImageResource(R.drawable.planetimage);
        //vh.text.setText(episodeslist.get(position).toString());
    }


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


    public class PlanetViewHolder extends RecyclerView.ViewHolder{

        protected TextView text;
        public PlanetViewHolder(View itemView) {
            super(itemView);
            text= (TextView) itemView.findViewById(R.id.text_id);
        }
        public void bind(final String item, final OnItemClickListener listener) {
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override public void onClick(View v) {
                    int position = getPosition();

                    int previousItem = selectedPosition;
                    selectedPosition = position;

                    notifyItemChanged(previousItem);
                    notifyItemChanged(position);

                    //selectedPosition=position;
                    //notifyDataSetChanged();




                    SharedPreferences setPref = v.getContext().getSharedPreferences("PlanetAdapter", Context.MODE_PRIVATE);
                    setPref.edit().putInt("position", position).apply ();



                     listener.onItemClick(item);
                    //Toast.makeText(getContext(), "You have clicked " + item, Toast.LENGTH_SHORT).show();
                    //Toast.makeText(getContext(), "You have clicked " + ((TextView) itemView).getText(), Toast.LENGTH_SHORT).show();
                }
            });
        }
    }
}

The Fragment

public class TvShowEpisodeDetailsFragment extends Fragment {

    TextClicked mCallback;

    public interface TextClicked{
        void sendText(int position);
    }

// other code here

@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.episode_details, container, false);
    }

@Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        // This makes sure that the container activity has implemented
        // the callback interface. If not, it throws an exception
        try {
            mCallback = (TextClicked) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                    + " must implement TextClicked");
        }
    }

@Override
    public void onViewCreated(final View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);


// other code here

// getting episodeslist
        mEpisodesList = (RecyclerView) view.findViewById(R.id.episodesLIST);
        ArrayList<String> episodeslist = new ArrayList<String>(mEpisodes.size()); //set initialize capacity as it's know, some negligible performance related reason

        for(TvShowEpisode e : mEpisodes){
            episodeslist.add(e.mEpisode);
        }


        // Setting LinearLayoutManager
        LinearLayoutManager layoutManager
                = new LinearLayoutManager(mContext.getApplicationContext(), LinearLayoutManager.HORIZONTAL, false);
        //mEpisodesList.setLayoutManager(new LinearLayoutManager(mContext));
        mEpisodesList.setLayoutManager(layoutManager);


        // Setting RecyclerView Adapter
        mEpisodesList.setAdapter(new PlanetAdapter(episodeslist, new PlanetAdapter.OnItemClickListener() {
            @Override public void onItemClick(String item) {
                SharedPreferences getPref = getActivity ().getSharedPreferences("PlanetAdapter", Context.MODE_PRIVATE);
                int pos = getPref.getInt("position",0);
                mCallback.sendText(pos);
            }
        }));


// other code here

}

@Override
    public void onDetach() {
        mCallback = null; // => avoid leaking, thanks @Deepscorn
        super.onDetach();
    }

// other code here

}

The activity

public class TvShowEpisodeDetails extends MizActivity implements TvShowEpisodeDetailsFragment.TextClicked{

    private static final String SHOW_ID = "showId";

    private ArrayList<TvShowEpisode> mEpisodes = new ArrayList<TvShowEpisode>();
    private int mSeason, mEpisode;
    private String mShowId, mShowTitle;
    private ViewPager mViewPager;
    private DbAdapterTvShowEpisodes mDatabaseHelper;
    private Bus mBus;

    @Override
    protected int getLayoutResource() {
        return R.layout.viewpager_with_toolbar_overlay;
    }


    @Override
    public void onCreate(Bundle savedInstanceState) {
        mBus = MizuuApplication.getBus();
        super.onCreate(savedInstanceState);

        // Set theme
        setTheme(R.style.Mizuu_Theme_NoBackground);

        ViewUtils.setupWindowFlagsForStatusbarOverlay(getWindow(), true);

        ViewUtils.setProperToolbarSize(this, mToolbar);

        mShowId = getIntent().getExtras().getString(SHOW_ID);
        mSeason = getIntent().getExtras().getInt("season");
        mEpisode = getIntent().getExtras().getInt("episode");

        mDatabaseHelper = MizuuApplication.getTvEpisodeDbAdapter();

        Cursor cursor = mDatabaseHelper.getEpisodes(mShowId);
        try {
            while (cursor.moveToNext()) {
                mEpisodes.add(new TvShowEpisode(this, mShowId,
                        cursor.getString(cursor.getColumnIndex(DbAdapterTvShowEpisodes.KEY_EPISODE_TITLE)),
                        cursor.getString(cursor.getColumnIndex(DbAdapterTvShowEpisodes.KEY_EPISODE_PLOT)),
                        cursor.getString(cursor.getColumnIndex(DbAdapterTvShowEpisodes.KEY_SEASON)),
                        cursor.getString(cursor.getColumnIndex(DbAdapterTvShowEpisodes.KEY_EPISODE)),
                        cursor.getString(cursor.getColumnIndex(DbAdapterTvShowEpisodes.KEY_EPISODE_AIRDATE)),
                        cursor.getString(cursor.getColumnIndex(DbAdapterTvShowEpisodes.KEY_EPISODE_DIRECTOR)),
                        cursor.getString(cursor.getColumnIndex(DbAdapterTvShowEpisodes.KEY_EPISODE_WRITER)),
                        cursor.getString(cursor.getColumnIndex(DbAdapterTvShowEpisodes.KEY_EPISODE_GUESTSTARS)),
                        cursor.getString(cursor.getColumnIndex(DbAdapterTvShowEpisodes.KEY_EPISODE_RATING)),
                        cursor.getString(cursor.getColumnIndex(DbAdapterTvShowEpisodes.KEY_HAS_WATCHED)),
                        cursor.getString(cursor.getColumnIndex(DbAdapterTvShowEpisodes.KEY_FAVOURITE))
                        ));
            }
        } catch (Exception e) {
        } finally {
            cursor.close();
        }

        mShowTitle = MizuuApplication.getTvDbAdapter().getShowTitle(mShowId);
        setTitle(mShowTitle);

        mViewPager = (ViewPager) findViewById(R.id.awesomepager);
        mViewPager.setAdapter(new TvShowEpisodeDetailsAdapter(getSupportFragmentManager()));
        mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                ViewUtils.updateToolbarBackground(TvShowEpisodeDetails.this, mToolbar, 0, mEpisodes.get(position).getTitle(), Color.TRANSPARENT);
            }
        });

        if (savedInstanceState != null) {
            mViewPager.setCurrentItem(savedInstanceState.getInt("tab", 0));
        } else {
            for (int i = 0; i < mEpisodes.size(); i++) {
                if (mEpisodes.get(i).getSeason().equals(MizLib.addIndexZero(mSeason)) && mEpisodes.get(i).getEpisode().equals(MizLib.addIndexZero(mEpisode))) {
                    mViewPager.setCurrentItem(i);
                    SharedPreferences setPref1x1 = getContext().getSharedPreferences("PlanetAdapter", Context.MODE_PRIVATE);
                    setPref1x1.edit().putInt("position", i).apply ();
                    break;
                }
            }
        }
    }

    @Subscribe
    public void onScrollChanged(TvShowEpisodeDetailsFragment.BusToolbarColorObject object) {
        ViewUtils.updateToolbarBackground(this, mToolbar, object.getAlpha(),
                mEpisodes.get(mViewPager.getCurrentItem()).getTitle(), object.getToolbarColor());
    }

    public void onResume() {
        super.onResume();

        mBus.register(this);
        ViewUtils.updateToolbarBackground(this, mToolbar, 0, mShowTitle, Color.TRANSPARENT);
    }

    @Override
    public void onPause() {
        super.onPause();

        mBus.unregister(this);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case android.R.id.home:
            finish();
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public void onStart() {
        super.onStart();
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt("tab", mViewPager.getCurrentItem());
    }


    @Override
    public void sendText(int position) {
        mViewPager.setCurrentItem(position,false);
        //Toast.makeText(getContext(), "Item Clicked " + text, Toast.LENGTH_SHORT).show();
    }


    private class TvShowEpisodeDetailsAdapter extends FragmentPagerAdapter {

        public TvShowEpisodeDetailsAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override  
        public Fragment getItem(int index) {
            return TvShowEpisodeDetailsFragment.newInstance(mShowId, Integer.parseInt(mEpisodes.get(index).getSeason()), Integer.parseInt(mEpisodes.get(index).getEpisode()));
        }  

        @Override  
        public int getCount() {  
            return mEpisodes.size();
        }
    }
    
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == 0) {
            if (resultCode == Activity.RESULT_OK) {
                setResult(Activity.RESULT_OK);
                finish();
            }
        }
    }
}

You have just saved the position of the selected item but not the state of all the items in the list i.e whether they are selected or unselected. Recycler View recycles the views when scrolled. So, you just need to use ArrayList of PlanelModel instead of ArrayList of String as shared below which has a variable called "isPlanetSelected" to save state of the item.

public class PlanetModel {
    private String planetName;
    private boolean isPlanetSelected;

    public String getPlanetName() {
        return planetName;
    }

    public void setPlanetName(String planetName) {
        this.planetName = planetName;
    }

    public boolean isPlanetSelected() {
        return isPlanetSelected;
    }

    public void setPlanetSelected(boolean planetSelected) {
        isPlanetSelected = planetSelected;
    }
}

Make following changes in your PlanetAdapter class:

ArrayList of PlanetModel instead of String:

private ArrayList<PlanetModel> episodeslist;

In onBindViewHolder set name of the episode as:

tv.setText(episodeslist.get(position).getPlanetName());

In onBindViewHolder set selected state of the view:

    if (episodeslist.get(position).isPlanetSelected()) {
        vh.itemView.setBackgroundColor(getContext().getResources().getColor(R.color.colorPrimaryDark));
    }else{
        vh.itemView.setBackgroundColor(getContext().getResources().getColor(R.color.colorPrimaryLight));
    }

Also, update the isPlanetSelected for that particular index

mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
    @Override
    public void onPageSelected(int position) {
    for (int i=0; i<mEpisodes.length; i++)
    {
        mEpisodes.get(i).setIsPlanetSelected(false);
    }
    mEpisodes.get(position).setIsPlanetSelected(true);
    //notify your recycler views adaper

    }
});

mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { @Override public void onPageSelected(int position) { episodeslist.get(position).setPlanetSelected(true) } });

Don't forget to set planet selected false wen it is unselected

episodeslist.get(position).setPlanetSelected(false)

Both your issues will be resolved with above approach.

Thanks.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM