Recyclerview row item selection color change
Asked Answered
P

5

12

I am able to change the color of the text and background of imageview of the row clicked of my recyclerview in my navigation drawer fragment.

But my problem is after clicking the 4th item,the 1st item also gets selected.Likewise after clicking my 5th item the 2nd item is selected.

Like this:enter image description here enter image description here

How do i solve this? So that only one item is selected at a time?

FragmentDrawer.java

public class FragmentDrawer extends Fragment {

    private static String TAG = FragmentDrawer.class.getSimpleName();

    private RecyclerView recyclerView;
    private ActionBarDrawerToggle mDrawerToggle;
    private DrawerLayout mDrawerLayout;
    private NavigationDrawerAdapter adapter;
    private View containerView;
    private static String[] titles = null;
    private FragmentDrawerListener drawerListener;

    public FragmentDrawer() {

    }

    public void setDrawerListener(FragmentDrawerListener listener) {
        this.drawerListener = listener;
    }

    public static List<NavDrawerItem> getData() {
        List<NavDrawerItem> data = new ArrayList<>();
        int[] icons = {R.drawable.ic_home,
                R.drawable.ic_upcoming,
                R.drawable.ic_saved,
                R.drawable.ic_wehappened,
                R.drawable.ic_persons,
                R.drawable.ic_music,
                R.drawable.ic_parties,
                R.drawable.ic_art, R.drawable.ic_network, R.drawable.ic_sports};

        // preparing navigation drawer items
        for (int i = 0; i < titles.length && i < icons.length; i++) {
            NavDrawerItem navItem = new NavDrawerItem();
            navItem.setTitle(titles[i]);
            navItem.setIcon(icons[i]);
            data.add(navItem);
        }
        return data;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // drawer labels
        titles = getActivity().getResources().getStringArray(R.array.nav_drawer_labels);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflating view layout
        View layout = inflater.inflate(R.layout.fragment_navigation_drawer, container, false);
        recyclerView = (RecyclerView) layout.findViewById(R.id.drawerList);

        adapter = new NavigationDrawerAdapter(getActivity(), getData());
        recyclerView.setAdapter(adapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

        List<SimpleSectionedRecyclerViewAdapter.Section> sections =
                new ArrayList<SimpleSectionedRecyclerViewAdapter.Section>();

        //Sections
        sections.add(new SimpleSectionedRecyclerViewAdapter.Section(5, "Categories"));
        recyclerView.addOnItemTouchListener(new RecyclerTouchListener(getActivity(), recyclerView, new ClickListener() {
            @Override
            public void onClick(View view, int position) {
                NavigationDrawerAdapter.selected_item = position;
                recyclerView.getAdapter().notifyDataSetChanged();
                drawerListener.onDrawerItemSelected(view, position);
                mDrawerLayout.closeDrawer(containerView);

            }

            @Override
            public void onLongClick(View view, int position) {

            }
        }));

        SimpleSectionedRecyclerViewAdapter.Section[] dummy = new SimpleSectionedRecyclerViewAdapter.Section[sections.size()];
        SimpleSectionedRecyclerViewAdapter mSectionedAdapter = new
                SimpleSectionedRecyclerViewAdapter(getContext(), R.layout.section, R.id.section_text, adapter);
        mSectionedAdapter.setSections(sections.toArray(dummy));

        //Apply this adapter to the RecyclerView
        recyclerView.setAdapter(mSectionedAdapter);

        return layout;
    }


    public void setUp(int fragmentId, DrawerLayout drawerLayout, final Toolbar toolbar) {
        containerView = getActivity().findViewById(fragmentId);
        mDrawerLayout = drawerLayout;
        mDrawerToggle = new ActionBarDrawerToggle(getActivity(), drawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close) {
            @Override
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
                getActivity().invalidateOptionsMenu();
            }

            @Override
            public void onDrawerClosed(View drawerView) {
                super.onDrawerClosed(drawerView);
                getActivity().invalidateOptionsMenu();
            }

            @Override
            public void onDrawerSlide(View drawerView, float slideOffset) {
                super.onDrawerSlide(drawerView, slideOffset);
                toolbar.setAlpha(1 - slideOffset / 2);
            }
        };

        mDrawerLayout.setDrawerListener(mDrawerToggle);
        mDrawerLayout.post(new Runnable() {
            @Override
            public void run() {
                mDrawerToggle.syncState();
            }
        });

    }

    public static interface ClickListener {
        public void onClick(View view, int position);

        public void onLongClick(View view, int position);
    }

    static class RecyclerTouchListener implements RecyclerView.OnItemTouchListener {

        private GestureDetector gestureDetector;
        private ClickListener clickListener;

        public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final ClickListener clickListener) {
            this.clickListener = clickListener;
            gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
                @Override
                public boolean onSingleTapUp(MotionEvent e) {

                    return true;
                }

                @Override
                public void onLongPress(MotionEvent e) {
                    View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
                    if (child != null && clickListener != null) {
                        clickListener.onLongClick(child, recyclerView.getChildPosition(child));
                    }
                }
            });
        }

        @Override
        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {

            View child = rv.findChildViewUnder(e.getX(), e.getY());
            if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
                clickListener.onClick(child, rv.getChildPosition(child));
            }
            return false;
        }

        @Override
        public void onTouchEvent(RecyclerView rv, MotionEvent e) {
        }

        @Override
        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

        }


    }

    public interface FragmentDrawerListener {
        public void onDrawerItemSelected(View view, int position);
    }
}

NavigationDrawerAdapter.java

public class NavigationDrawerAdapter extends RecyclerView.Adapter<NavigationDrawerAdapter.MyViewHolder> {
    List<NavDrawerItem> data = Collections.emptyList();
    private LayoutInflater inflater;
    private Context context;
    public static int selected_item = 0;

    public NavigationDrawerAdapter(Context context, List<NavDrawerItem> data) {
        this.context = context;
        inflater = LayoutInflater.from(context);

        this.data = data;
    }

    public void delete(int position) {
        data.remove(position);
        notifyItemRemoved(position);
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = inflater.inflate(R.layout.nav_drawer_row, parent, false);
        MyViewHolder holder = new MyViewHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        if(position == selected_item)
        {
            holder.title.setTextColor(Color.parseColor("#00aaff"));
            holder.imgViewIcon.setBackgroundResource(R.drawable.ic_circle);

        }
            NavDrawerItem current = data.get(position);
            holder.title.setText(current.getTitle());
            holder.imgViewIcon.setImageResource(current.getIcon());


    }


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

    class MyViewHolder extends RecyclerView.ViewHolder {
        TextView title;
        public ImageView imgViewIcon;

        public MyViewHolder(View itemView) {
            super(itemView);
            title = (TextView) itemView.findViewById(R.id.title);
            imgViewIcon = (ImageView) itemView.findViewById(R.id.item_icon);
        }
    }
}

EDIT: SimpleSectionedRecyclerViewAdapter.java

public class SimpleSectionedRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private final Context mContext;
    private static final int SECTION_TYPE = 0;

    private boolean mValid = true;
    private int mSectionResourceId;
    private int mTextResourceId;
    private LayoutInflater mLayoutInflater;
    private RecyclerView.Adapter mBaseAdapter;
    private SparseArray<Section> mSections = new SparseArray<Section>();


    public SimpleSectionedRecyclerViewAdapter(Context context, int sectionResourceId, int textResourceId,
                                              RecyclerView.Adapter baseAdapter) {

        mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        mSectionResourceId = sectionResourceId;
        mTextResourceId = textResourceId;
        mBaseAdapter = baseAdapter;
        mContext = context;

        mBaseAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
            @Override
            public void onChanged() {
                mValid = mBaseAdapter.getItemCount()>0;
                notifyDataSetChanged();
            }

            @Override
            public void onItemRangeChanged(int positionStart, int itemCount) {
                mValid = mBaseAdapter.getItemCount()>0;
                notifyItemRangeChanged(positionStart, itemCount);
            }

            @Override
            public void onItemRangeInserted(int positionStart, int itemCount) {
                mValid = mBaseAdapter.getItemCount()>0;
                notifyItemRangeInserted(positionStart, itemCount);
            }

            @Override
            public void onItemRangeRemoved(int positionStart, int itemCount) {
                mValid = mBaseAdapter.getItemCount()>0;
                notifyItemRangeRemoved(positionStart, itemCount);
            }
        });
    }


    public static class SectionViewHolder extends RecyclerView.ViewHolder {

        public TextView title;

        public SectionViewHolder(View view,int mTextResourceid) {
            super(view);
            title = (TextView) view.findViewById(mTextResourceid);
        }
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int typeView) {
        if (typeView == SECTION_TYPE) {
            final View view = LayoutInflater.from(mContext).inflate(mSectionResourceId, parent, false);
            return new SectionViewHolder(view,mTextResourceId);
        }else{
            return mBaseAdapter.onCreateViewHolder(parent, typeView -1);
        }
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder sectionViewHolder, int position) {
        if (isSectionHeaderPosition(position)) {
            ((SectionViewHolder)sectionViewHolder).title.setText(mSections.get(position).title);
        }else{
            mBaseAdapter.onBindViewHolder(sectionViewHolder,sectionedPositionToPosition(position));
        }

    }

    @Override
    public int getItemViewType(int position) {
        return isSectionHeaderPosition(position)
                ? SECTION_TYPE
                : mBaseAdapter.getItemViewType(sectionedPositionToPosition(position)) +1 ;
    }


    public static class Section {
        int firstPosition;
        int sectionedPosition;
        CharSequence title;

        public Section(int firstPosition, CharSequence title) {
            this.firstPosition = firstPosition;
            this.title = title;
        }

        public CharSequence getTitle() {
            return title;
        }
    }


    public void setSections(Section[] sections) {
        mSections.clear();

        Arrays.sort(sections, new Comparator<Section>() {
            @Override
            public int compare(Section o, Section o1) {
                return (o.firstPosition == o1.firstPosition)
                        ? 0
                        : ((o.firstPosition < o1.firstPosition) ? -1 : 1);
            }
        });

        int offset = 0; // offset positions for the headers we're adding
        for (Section section : sections) {
            section.sectionedPosition = section.firstPosition + offset;
            mSections.append(section.sectionedPosition, section);
            ++offset;
        }

        notifyDataSetChanged();
    }

    public int positionToSectionedPosition(int position) {
        int offset = 0;
        for (int i = 0; i < mSections.size(); i++) {
            if (mSections.valueAt(i).firstPosition > position) {
                break;
            }
            ++offset;
        }
        return position + offset;
    }

    public int sectionedPositionToPosition(int sectionedPosition) {
        if (isSectionHeaderPosition(sectionedPosition)) {
            return RecyclerView.NO_POSITION;
        }

        int offset = 0;
        for (int i = 0; i < mSections.size(); i++) {
            if (mSections.valueAt(i).sectionedPosition > sectionedPosition) {
                break;
            }
            --offset;
        }
        return sectionedPosition + offset;
    }

    public boolean isSectionHeaderPosition(int position) {
        return mSections.get(position) != null;
    }


    @Override
    public long getItemId(int position) {
        return isSectionHeaderPosition(position)
                ? Integer.MAX_VALUE - mSections.indexOfKey(position)
                : mBaseAdapter.getItemId(sectionedPositionToPosition(position));
    }

    @Override
    public int getItemCount() {
        return (mValid ? mBaseAdapter.getItemCount() + mSections.size() : 0);
    }

}
Pastoral answered 11/9, 2015 at 18:52 Comment(0)
D
13

Change the onBindViewHolder method of your NavigationDrawerAdapter.java into this:

@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
    if(position == selected_item) {
        holder.title.setTextColor(Color.parseColor("#00aaff"));
        holder.imgViewIcon.setBackgroundResource(R.drawable.ic_circle);
    } else {
        holder.title.setTextColor(Color.parseColor("#00000")); //actually you should set to the normal text color
        holder.imgViewIcon.setBackgroundResource(0);
    }
    NavDrawerItem current = data.get(position);
    holder.title.setText(current.getTitle());
    holder.imgViewIcon.setImageResource(current.getIcon());


}
Deutsch answered 12/9, 2015 at 1:49 Comment(7)
This works like a charm..If you dont mind,please explain this part holder.imgViewIcon.setBackgroundResource(0);Pastoral
@SteveKamau this line will clear the imgViewIcon's background you set for selection, say the ic_circle drawable.Deutsch
Thank you for that..I have run into another related issue.I added a section header "Categories" at position 5(see FragmentDrawer.java).When i click on the rows after the section header the row below it is highlighted.How do i add or remove the position?Pastoral
EDIT: Added class SimpleSectionedRecyclerViewAdapter.java which handles the header sections.Pastoral
I don't understand what do you want to achieve, maybe you can refer to the expandable-recycler-view or sticky-headers-recyclerviewDeutsch
I followed this example gist.github.com/gabrielemariotti/4c189fb1124df4556058Pastoral
I was getting stuck with this problem while selecting movie SeatLayout, Thank you @Deutsch you saved my day!!Dumbbell
C
12

Simple and Best

Inside Adapter class

int row_index=0;

inside onBindViewHOlder

holder.titleLL.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                row_index=position;
                notifyDataSetChanged();
            }
        });
        if (row_index==position) {
            holder.title.setTextColor(Color.RED);
        } else {
            holder.title.setTextColor(Color.BLACK);
        }
Cornel answered 10/9, 2019 at 4:31 Comment(1)
Though an old question, thanks for helping others who come across this benefit from your solution.Pastoral
P
2

You can use class for Recycler view TouchListener

    class RecyclerTouchListener implements RecyclerView.OnItemTouchListener{

        private ClickListener clicklistener;
        private GestureDetector gestureDetector;

        public RecyclerTouchListener(Context context, final RecyclerView recycleView, final ClickListener clicklistener){

            this.clicklistener=clicklistener;
            gestureDetector=new GestureDetector(context,new GestureDetector.SimpleOnGestureListener(){
                @Override
                public boolean onSingleTapUp(MotionEvent e) {
                    return true;
                }

                @Override
                public void onLongPress(MotionEvent e) {
                    View child=recycleView.findChildViewUnder(e.getX(),e.getY());
                    if(child!=null && clicklistener!=null){
                        clicklistener.onLongClick(child,recycleView.getChildAdapterPosition(child));

                    }
                }
            });
        }

        @Override
        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
            View child=rv.findChildViewUnder(e.getX(),e.getY());

            RecyclerView.ViewHolder viewHolder=rv.findContainingViewHolder(child);
            if(child!=null && clicklistener!=null && gestureDetector.onTouchEvent(e)){
                clicklistener.onClick(child,rv.getChildAdapterPosition(child),viewHolder);

            }

            return false;
        }

        @Override
        public void onTouchEvent(RecyclerView rv, MotionEvent e) {

        }

        @Override
        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

        }
    }
}

Implement an Interface

public interface ClickListener{
    public void onClick(View view, int position,RecyclerView.ViewHolder viewHolder);
    public void onLongClick(View view, int position);
}

And in your Main Activity (Here I set Image resource You can set as You like ,Any thing)

Create an arraylist named "selected" as Integer and add following code as OnClickListener

recyclerViewSchedule.addOnItemTouchListener(new RecyclerTouchListener(this,
                recyclerViewSchedule, new ClickListener() {
            @Override
            public void onClick(View view, final int position, RecyclerView.ViewHolder v) {



                //Values are passing to activity & to fragment as well

                  if(!selected.contains(position))
            {
                selected.add(position);

                ((ScheduleAdapter.MyHolder) v).txtImage.setBackgroundColor(getResources().getColor(R.color.colorAccent));

            }else {

                ((ScheduleAdapter.MyHolder) v).txtImage.setBackgroundColor(getResources().getColor(R.color.colorTransparent));
                selected.remove(new Integer(position));

            }
            Toast.makeText(ScheduleActivity.this, "Single Click on position        :"+position,
                    Toast.LENGTH_SHORT).show();


        }


                Toast.makeText(ScheduleActivity.this, "Single Click on position        :"+position,
                        Toast.LENGTH_SHORT).show();


            }

            @Override
            public void onLongClick(View view, int position) {
                Toast.makeText(ScheduleActivity.this, "Long press on position :"+position,
                        Toast.LENGTH_LONG).show();
            }
        }));

The txtImage I specified here is from RecyclerAdapter,Replace it with Your View

Happy Coding :-)

Perfectible answered 11/8, 2017 at 7:53 Comment(1)
Though an old question, thanks for helping others who come across this benefit from your solution. Thanks matePastoral
H
2

most of the answers for such a question, are about to Override Onclick in onBindViewHolder or implementing an interface for clicking in recycler view. but as we all know, onBindViewHolder is called as your views are populated during scrolling. Thus, you will have numerous calls to setOnClickListener so it is not an efficient way and also implementing interface is not as easy as the answer I got.

here is how you can access selecting items in recycler view and change its background or do what ever you want when an item is selected .

in your ViewHolder constructor use setOnTouchListener for itemView (the argument of constructor) in this way

itemView.setOnTouchListener((v, event) -> {
    if (event.getAction() == MotionEvent.ACTION_UP) {
        if (v.getTag() == null) {
            v.setTag(true);
            v.setBackgroundColor(Color.parseColor("#F31807"));
        } 
        else {
            v.setBackgroundColor(Color.TRANSPARENT);
            v.setTag(null);
        }
    }

    if (event.getAction() == MotionEvent.ACTION_MOVE) {
       //default color
        v.setBackgroundColor(Color.TRANSPARENT);
    }
    return true;
});

We use ACTION_UP for check if user pressed the item and after that we save a boolean in view's flag so that we can press every items how many times we want and see the color is changing. ACTION_MOVE if block is for prevent selecting items when scrolling the recycler view. if you see that selecting one item is affecting another items selection, you can use this answer:

issue : RecyclerView messed up data when scrolling

in the end if you are curious about why we return true in method see this video to understand.

tutorial on OnTouchListener And Motionevent

Hebrides answered 6/10, 2021 at 9:48 Comment(1)
Interesting take, thanks.Pastoral
S
1

I have tried many different approaches but in the end, the solution that worked for me is

like first I was doing this

binding.download.setOnClickListener {
        binding.download.visibility = View.GONE
        binding.saved.visibility = View.VISIBLE

    }

this was not working for me but after the below change

  • Instead of changing the color or visibility in onBindViewHolder, make a function in ViewHolder class and call it from onBindViewHolder

    class ViewHolder(var binding: ImageRowBinding) : RecyclerView.ViewHolder(binding.root) {

      fun bind(uri: Uri, mContext: Context) {
    
          binding.download.setOnClickListener {
              binding.download.visibility = View.GONE
              binding.saved.visibility = View.VISIBLE
          }
      }
    

    }

and calling this bind method from onBindViewHolder like this

holder.bind(items[position].uri, mContext)

works best for me

Scheer answered 25/11, 2021 at 6:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.