Hello.
I want to implement single item selection function in RecyclerView
RecyclerView's item is made up of TextView only.
When i clicked items, i want to change TextView's background color
I want to click only single item.
But items are clicked only multiple
When I click the first item and then the second item, the background color of the first item should be changed.
This is i wanted.
However i can't change the Text of the previous item in 'Adapter'.
I don't know how to get previous selected View.
How should i do?
Adapter.java
public class RoutineListAdapter extends RecyclerView.Adapter<RoutineListAdapter.ViewHolder> {
// ArrayList<RoutineListModel> items;
List<String> items = new ArrayList<>();
Context context;
public void addItems(List<String> items) {
this.items = items;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View itemView = inflater.inflate(R.layout.routine_list_item, parent, false);
return new ViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
holder.setItem(items.get(position));
}
#Override
public int getItemCount() {
return items.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView routine;
boolean isSelected;
public ViewHolder(#NonNull View itemView) {
super(itemView);
routine = itemView.findViewById(R.id.routine_list);
routine.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(isSelected == false) {
routine.setBackgroundColor(context.getResources().getColor(R.color.light_green_dark));
isSelected = true;
}
else {
routine.setBackgroundColor(context.getResources().getColor(R.color.white));
isSelected = false;
}
}
});
}
private void setItem(String item) {
routine.setText(item);
}
}
}
ViewHolder's multiple instances are being created, so each instance has its own isSelected property and its value.
But what you need a singe common variable for all viewholder instance to refere it, hence the selectedPosition in the outer class, so it is only one, and when you click you set the current adapter position value to and with that you must notify the adapter, which will trigger to call the onBindViewHolder for all items and in the setItem() you can put up what you want to set background of views on basis of whatever conditions.
public class RoutineListAdapter extends RecyclerView.Adapter<RoutineListAdapter.ViewHolder> {
// ArrayList<RoutineListModel> items;
List<String> items = new ArrayList<>();
Context context;
int selectedPosition=-1;
//nothing selected initially
public void addItems(List<String> items) {
this.items = items;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View itemView = inflater.inflate(R.layout.routine_list_item, parent, false);
return new ViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
holder.setItem(items.get(position));
}
#Override
public int getItemCount() {
return items.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView routine;
public ViewHolder(#NonNull View itemView) {
super(itemView);
routine = itemView.findViewById(R.id.routine_list);
routine.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
selectedPosition=getAdapterPosition();
notifyDataSetChanged();
}
});
}
private void setItem(String item) {
if(selectedPosition== getAdapterPosition()) {
routine.setBackgroundColor(context.getResources().getColor(R.color.light_green_dark));
}
else {
routine.setBackgroundColor(context.getResources().getColor(R.color.white));
}
routine.setText(item);
}
}
Related
If someone can help me. I've a RecyclerView with items but when i try to select ONE item, i can select many items. I think that the problem is in the onClick callback but i don't know exactly. This is the adapter code:
public class HorizontalRecyclerViewAdapter extends RecyclerView
.Adapter<HorizontalRecyclerViewAdapter
.DataObjectHolder> {
private ArrayList<HorizontalData> mDataset;
private static MyClickListener myClickListener;
private SparseBooleanArray selectedItem = new SparseBooleanArray();
public class DataObjectHolder extends RecyclerView.ViewHolder
implements View
.OnClickListener {
TextView mLabel;
TextView mDateTime;
LinearLayout linearLayout;
public DataObjectHolder(View itemView) {
super(itemView);
mLabel = (TextView) itemView.findViewById(R.id.item_list_view_text_view);
mDateTime = (TextView) itemView.findViewById(R.id.item_list_view_text_view_two);
linearLayout = (LinearLayout) itemView.findViewById(R.id.myBackground);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
if (selectedItem.get(getAdapterPosition(), false)) {
selectedItem.delete(getAdapterPosition());
v.setSelected(false);
} else {
selectedItem.put(getAdapterPosition(), true);
v.setSelected(true);
}
myClickListener.onItemClick(getAdapterPosition(), v);
}
}
public void setOnItemClickListener(MyClickListener myClickListener) {
this.myClickListener = myClickListener;
}
public HorizontalRecyclerViewAdapter(ArrayList<HorizontalData> myDataset) {
mDataset = myDataset;
}
#Override
public DataObjectHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.date_item, parent, false);
DataObjectHolder dataObjectHolder = new DataObjectHolder(view);
return dataObjectHolder;
}
#Override
public void onBindViewHolder(DataObjectHolder holder, int position) {
holder.linearLayout.setSelected(selectedItem.get(position, false));
holder.mLabel.setText(mDataset.get(position).getmTitle());
holder.mDateTime.setText(mDataset.get(position).getmSubTitle());
}
#Override
public int getItemCount() {
return mDataset.size();
}
public interface MyClickListener {
void onItemClick(int position, View v);
}
}
Thanks!
First of all you don't need SparseBooleanArray, because you want only one item to be selected. It'd be enough to have int selectedItemPos with would represent currently selected item position.
Here's a simple adapter that I've just created and it's working, with comments explaining what's happening:
public class TestAdapter extends RecyclerView.Adapter<TestAdapter.ViewHolder> {
private ArrayList<String> items;
//field mentioned before
private int selectedItemPos = -1;
//I create some items to see if my adapter is working.
public TestAdapter() {
this.items = new ArrayList<>();
for (int i=0; i<20; i++) {
items.add("Test" + i);
}
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false));
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
String item = items.get(position);
holder.mName.setText(item);
//This line sets selection state to true if current position is the same as selected one, and false otherwise.
holder.itemView.setSelected(selectedItemPos == position);
}
#Override
public int getItemCount() {
return items.size();
}
//helper method
protected void setSelectedItem(int position) {
int oldSelected = selectedItemPos;
selectedItemPos = position;
// update view of unselected item
notifyItemChanged(oldSelected);
//update view of just selected item
notifyItemChanged(selectedItemPos);
}
protected class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public TextView mName;
public ViewHolder(View view) {
super(view);
mName = (TextView) view.findViewById(R.id.taskListItem_name);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
//if the clicked item is already selected,
// we will just unselect it and no new item will be selected
//(new one is -1, which is none)
int newSelectedItem = getAdapterPosition()==selectedItemPos?-1:getAdapterPosition();
//apply changes on adapter
setSelectedItem(newSelectedItem);
}
}
}
You should be able to easily merge this code with yours.
I am writing music player with track list in recycler view. I want to change image view in clicked item. Tried to use getItemViewType, but as I can understand it sets view type only once. Here's my code:
public class TrackListAdapter extends RecyclerView.Adapter<TrackListAdapter.RegularViewHolder> implements IntConstants{
private ArrayList<Track> trackList;
private Context context;
int selectedPos = -1;
public TrackListAdapter(ArrayList<Track> trackList, Context context) {
this.trackList = trackList;
this.context = context;
EventBus.getDefault().register(this);
}
#Override
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
EventBus.getDefault().unregister(this);
super.onDetachedFromRecyclerView(recyclerView);
}
#Override
public RegularViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.LayoutParams params = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
if (viewType == ITEM_TYPE_PLAYING){
//here i am sending xml for clicked item
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.playing_track_item, parent, false);
return new RegularViewHolder(itemView) ;
}else {
//here i'm sending xml for regular item
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.regular_track_item, parent, false);
return new RegularViewHolder(itemView) ;
}
}
#Override
public void onBindViewHolder(final RegularViewHolder holder, int position) {
Track t = trackList.get(position);
holder.textTrackTitle.setText(t.getTitle());
holder.textTrackAlbum.setText(t.getAlbum());
holder.textTrackArtist.setText(t.getArtist());
holder.parent.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
EventBus.getDefault().post(new MessageEvent(TRACK_CLICKED_ACTION_CODE, holder.getAdapterPosition()));
}
});
}
#Override
public int getItemCount() {
return trackList.size();
}
class RegularViewHolder extends RecyclerView.ViewHolder{
ImageView imageAlbumArt;
TextView textTrackTitle, textTrackArtist, textTrackAlbum;
RelativeLayout parent;
private RegularViewHolder(View itemView){
super(itemView);
parent = (RelativeLayout)itemView.findViewById(R.id.item_parent);
imageAlbumArt = (ImageView)itemView.findViewById(R.id.image_album_art);
textTrackTitle = (TextView) itemView.findViewById(R.id.text_track_title);
textTrackArtist = (TextView) itemView.findViewById(R.id.text_track_artist);
textTrackAlbum = (TextView) itemView.findViewById(R.id.text_track_album);
}}
#Override
public int getItemViewType(int position) {
if (position == selectedPos){
return ITEM_TYPE_PLAYING;
}else {
return ITEM_TYPE_REGULAR;
}
}
#Subscribe
public void handleTrack(MessageEvent event){
if (event.getCode() == TRACK_SELECTED_ACTION_CODE){
selectedPos = event.getPosition();
}
}
}
You would have to update the selected position stored by the adapter, then notifyItemChanged() twice, for the positions of the old item (that used to be selected) and the new one (that is now selected).
i have recycler view and i highlight the item when i click in it , the problem is i can highlight many item , i wont just one item , when i click into item is highlight and when i click another item is highlight too
this is MyAdapter
public class ScreenRecyclerAdapter extends RecyclerView.Adapter<ScreenRecyclerAdapter.ViewHolder> {
Context context;
int image_list[];
ImageView image_view_screen_item;
public ScreenRecyclerAdapter(int[] image_list, Context context){
super();
this.image_list = image_list;
this.context = context;}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.screen_items, parent, false);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Picasso.with(context).load(image_list[position]).into(holder.image_view_screen_item);
holder.list_row.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
}
#Override
public int getItemCount() {
return image_list.length;
}
class ViewHolder extends RecyclerView.ViewHolder{
ImageView image_view_screen_item , back;
RelativeLayout list_row;
public ViewHolder(View itemView) {
super(itemView);
image_view_screen_item = (ImageView) itemView.findViewById(R.id.plantImageView);
list_row = (RelativeLayout) itemView.findViewById(R.id.list_row);
list_row.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
list_row.setBackgroundColor(Color.parseColor("#d5d5d5"));
Intent i = new Intent(context, ImagePager.class);
context.startActivity(i);
}
});
}}}
First define a list of RelativeLayouts for your adapter which contains every rows:
List<RelativeLayout> items;
Then in your onBindViewHolder method, add:
items.add(holder.list_row);
Now add a method to your adapter like this:
private void makeAllWhite() {
for(RelativeLayout item : items) {
item.setBackgroundColor(Color.parseColor("#ffffff"));
}
}
Finally before this line:
list_row.setBackgroundColor(Color.parseColor("#d5d5d5"));
call:
makeAllWhite();
Your final code should be like this:
public class ScreenRecyclerAdapter extends RecyclerView.Adapter<ScreenRecyclerAdapter.ViewHolder> {
Context context;
int image_list[];
ImageView image_view_screen_item;
List<RelativeLayout> items;
public ScreenRecyclerAdapter(int[] image_list, Context context){
super();
this.image_list = image_list;
this.context = context;
this.items = new ArrayList<>();
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.screen_items, parent, false);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
items.add(holder.list_row);
Picasso.with(context).load(image_list[position]).into(holder.image_view_screen_item);
}
#Override
public int getItemCount() {
return image_list.length;
}
class ViewHolder extends RecyclerView.ViewHolder{
ImageView image_view_screen_item , back;
RelativeLayout list_row;
public ViewHolder(View itemView) {
super(itemView);
image_view_screen_item = (ImageView) itemView.findViewById(R.id.plantImageView);
list_row = (RelativeLayout) itemView.findViewById(R.id.list_row);
list_row.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
makeAllWhite();
list_row.setBackgroundColor(Color.parseColor("#d5d5d5"));
Intent i = new Intent(context, ImagePager.class);
context.startActivity(i);
}
});
}
}
private void makeAllWhite() {
for(RelativeLayout item : items) {
item.setBackgroundColor(Color.parseColor("#ffffff")));
}
}
}
I have done the same thing here. There are multiple solutions to it but what I found most convenient is storing a copy of the view (or viewholder, both work) and in every onClick event check if the view (or viewholder) is same, if not then change the background of the previously selected view and set the new background for the selected item else use return ;
In your adapter, you're setting the OnClickListener for the ViewHolder, which is basically representing every item. The binding (of data, design, events, etc.) is happening in onBindViewHolder according to the position in the dataset. So just move your event implementation from the constructor of ViewHolder to onBindViewHolder.
It may be irrelevant because it seems that your dataset isn't modified by the event, but if it does, don't forget to call notifyDataSetChanged() after modification.
In RecyclerView.Adapter i need to change favIcon in each row list that after select each position just specified row favIcon change.
All thing is right but when scroll RecyclerView , position of selected row change automatically!
it's my adapter class :
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private ItemData[] itemsData;
Activity activity;
public MyAdapter(Activity activity, ItemData[] itemsData) {
this.activity=activity;
this.itemsData = itemsData;
}
#Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View itemLayoutView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_layout, null);
ViewHolder viewHolder = new ViewHolder(itemLayoutView);
return viewHolder;
}
#Override
public void onBindViewHolder(final ViewHolder viewHolder, final int position) {
viewHolder.txtViewTitle.setText(itemsData[position].getTitle());
viewHolder.imgViewIcon.setImageResource(itemsData[position].getImageUrl());
viewHolder.imgViewIcon.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!viewHolder.isFav) {
Toast.makeText(activity,"Selected Row : "+position,Toast.LENGTH_SHORT).show();
viewHolder.isFav = true;
viewHolder.imgViewIcon.setImageResource(R.drawable.icon_fav);
} else {
viewHolder.imgViewIcon.setImageResource(R.drawable.pre_ic_ab_drawer);
viewHolder.isFav = false;
}
}
});
if (viewHolder.isFav)
viewHolder.imgViewIcon.setImageResource(R.drawable.icon_fav);
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView txtViewTitle;
public ImageView imgViewIcon;
public Boolean isFav=false;
public ViewHolder(View itemLayoutView) {
super(itemLayoutView);
txtViewTitle = (TextView) itemLayoutView.findViewById(R.id.item_title);
imgViewIcon = (ImageView) itemLayoutView.findViewById(R.id.item_icon);
}
}
#Override
public int getItemCount() {
return itemsData.length;
}
}
What I see in the output :
As seen i select first row icon , but after scroll RecyclerView , selected position change.
Then try to use changing favIconand imgViewIcon.setOnClickListener in onCreateViewHolder again wrong position!
Too i try to store position in sharedPreferences or modelClass , but this return wrong position in scrolling yet
There is a way to avoid this problem?
I saw this solution Too :Get clicked item and its position in RecyclerView
Try to use your model ItemData[] itemsData; to save if element is selected.
eg.
if (itemsData[position].isFav){
viewHolder.imgViewIcon.setImageResource(R.drawable.icon_fav);
}else{
viewHolder.imgViewIcon.setImageResource(R.drawable.pre_ic_ab_drawer);
}
viewHolder.imgViewIcon.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!itemsData[position].isFav) {
Toast.makeText(activity,"Selected Row : "+position,Toast.LENGTH_SHORT).show();
itemsData[position].isFav = true;
viewHolder.imgViewIcon.setImageResource(R.drawable.icon_fav);
} else {
viewHolder.imgViewIcon.setImageResource(R.drawable.pre_ic_ab_drawer);
itemsData[position].isFav = false;
}
}
});
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private ItemData[] itemsData;
Activity activity;
public MyAdapter(Activity activity, ItemData[] itemsData) {
this.activity=activity;
this.itemsData = itemsData;
}
#Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View itemLayoutView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_layout, null);
ViewHolder viewHolder = new ViewHolder(itemLayoutView);
return viewHolder;
}
#Override
public void onBindViewHolder(final ViewHolder viewHolder, final int position) {
viewHolder.txtViewTitle.setText(itemsData[position].getTitle());
viewHolder.imgViewIcon.setImageResource(itemsData[position].getImageUrl());
viewHolder.imgViewIcon.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!viewHolder.isFav) {
Toast.makeText(activity,"Selected Row : "+position,Toast.LENGTH_SHORT).show();
viewHolder.isFav = true;
viewHolder.imgViewIcon.setImageResource(R.drawable.icon_fav);
} else {
viewHolder.imgViewIcon.setImageResource(R.drawable.pre_ic_ab_drawer);
viewHolder.isFav = false;
}
}
});
if (viewHolder.isFav)
viewHolder.imgViewIcon.setImageResource(R.drawable.icon_fav);
else
// The change
viewHolder.imgViewIcon.setImageResource(R.drawable.pre_ic_ab_drawer);
}
When you bind the ViewHolder you should reset the ImageView icon if it's not fav.
On click of button when I'm scrolling the view, it changes the
position of that clicked button. On every scroll it's showing different
position.
public class ProductAdapter extends
RecyclerView.Adapter<ProductAdapter.ProductViewHolder> {
Context ctx;
ArrayList<ProductDetail> productList;
public ProductAdapter(Context ctx, ArrayList<ProductDetail> productList) {
this.ctx = ctx;
this.productList = productList;
}
#Override
public int getItemCount() {
return productList.size();
}
#Override
public void onBindViewHolder(final ProductViewHolder vHolder, int pos) {
vHolder.txt_prod_name.setText(productList.get(pos).getProduct_desc());
vHolder.btn_add.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
vHolder.lyt_prod_qty.setVisibility(View.VISIBLE);
vHolder.btn_add.setVisibility(View.GONE);
}
});
}
#Override
public ProductViewHolder onCreateViewHolder(ViewGroup parent, int arg1) {
View view = LayoutInflater.from(parent.getContext()).inflate(
R.layout.activity_recycler_search_item, null);
ProductViewHolder viewHolder = new ProductViewHolder(view);
return viewHolder;
}
public class ProductViewHolder extends RecyclerView.ViewHolder {
TextView txt_prod_name;
TextView txt_delivery_type;
TextView txt_prod_mrp;
Spinner spn_prod_qty;
LinearLayout list_lyt, lyt_prod_qty;
Button btn_add;
public ProductViewHolder(View itemView) {
super(itemView);
txt_prod_name = (TextView) itemView
.findViewById(R.id.txt_prod_name);
txt_delivery_type = (TextView) itemView
.findViewById(R.id.txt_delivery_type);
txt_prod_mrp = (TextView) itemView.findViewById(R.id.txt_prod_mrp);
btn_add = (Button) itemView.findViewById(R.id.btn_add);
lyt_prod_qty = (LinearLayout) itemView
.findViewById(R.id.lyt_prod_qty);
}
}
}
On click of Button when I'mm scrolling the view, it changes the position of that clicked button. On every scroll it's showing different
position. Where should I out my click logic or should refresh the adapter every time?
In your ProductDetail modal class add a member boolean isButtonClicked; and add its getter and setter as well. Then make following changes in your onBindViewHolder method:
#Override
public void onBindViewHolder(final ProductViewHolder vHolder, int pos) {
final ProductDetail productDetail = productList.get(pos);
vHolder.txt_prod_name.setText(productDetail.getProduct_desc());
if(productDetail.isButtonClicked()){
vHolder.lyt_prod_qty.setVisibility(View.VISIBLE);
vHolder.btn_add.setVisibility(View.GONE);
} else {
vHolder.lyt_prod_qty.setVisibility(View.GONE);
vHolder.btn_add.setVisibility(View.VISIBLE);
}
vHolder.btn_add.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
vHolder.lyt_prod_qty.setVisibility(View.VISIBLE);
vHolder.btn_add.setVisibility(View.GONE);
productDetail.setIsButtonClicked(true);
}
});
}