In my recyclerview has one imageview and textview. I'm changing image of ImageView onn onClickListener. Now the problem is if i click on image of position 3 and scroll down... image of position 8 is also changed and again if i scroll up...image of position 2 is changed.
public class PortraitListviewAdapter extends RecyclerView.Adapter<PortraitListviewAdapter.ViewHolder> {
Context context;
static List<PortraitParentListAdapterBean> list;
static List<String> selectedPosition ;
public PortraitListviewAdapter(Context context, List<PortraitParentListAdapterBean> list) {
this.context = context;
this.list = list;
selectedPosition = new ArrayList<>();
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.listview_parent_portrait, parent, false);
return new ViewHolder(itemView);
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
PortraitParentListAdapterBean portBean = list.get(position);
Log.i("pos",position+"");
holder.parentHeading.setText(portBean.getHeading());
if (selectedPosition.contains(list.get(position).getHeading())){
holder.parentImage.setImageResource(R.drawable.sad);
}
}
#Override
public int getItemCount() {
return list.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
protected TextView parentHeading;
protected ImageView parentImage;
public ViewHolder(View itemView) {
super(itemView);
parentHeading = (TextView)itemView.findViewById(R.id.parent_heading);
parentImage = (ImageView)itemView.findViewById(R.id.imageView);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
selectedPosition.add(list.get(getAdapterPosition()).getHeading());
parentImage.setImageResource(R.drawable.sad);
}
});
}
}
}
the above code is my implementation for recyclerview adapter. please help to understand the concept.
SOLUTION
change
if (selectedPosition.contains(list.get(position).getHeading())){
holder.parentImage.setImageResource(R.drawable.sad);
}
with
if (selectedPosition.contains(list.get(position).getHeading())){
holder.parentImage.setImageResource(R.drawable.sad);
} else {
holder.parentImage.setImageResource(R.drawable.your_default_drawable);
}
EXPLANATION
When you scroll your RecyclerView the system doesn't recreate always your ViewHolder but reuse one you previously scrolled that is no longer visible, so you need to reset your standard values in order to avoid showing wrong values.
Related
I have an app which has a recyclerview. I want to give an opportunity to the users to switch between Night and Day mode theme. I know how to change text color and background color, but in this case I can't. Actually I am unable to find the item layout variables from MainActivity. How to create an object of ViewHolder class from MainActivity? Can anyone please help me?
Here is my Adapter class:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{
private List<ListItemModel> listItems;
private Context context;
public MyAdapter(List<ListItemModel> listItems, Context context) {
this.listItems = listItems;
this.context = context;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_model, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
final ListItemModel listItem = listItems.get(position);
holder.index_number.setText(listItem.getIndexNumber());
holder.title_name.setText(listItem.getTitle());
}
#Override
public int getItemCount() {
return listItems.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView index_number, title_name;
public ViewHolder(View itemView) {
super(itemView);
index_number = (TextView) itemView.findViewById(R.id.model_text_index_id);
title_name = (TextView) itemView.findViewById(R.id.model_text_title_id);
}
}
}
If its a single choice list, you can define an int parameter in your list and define a method in adapter
....
private int selectedPosition = -1;
public void setSelectedPosition(int index){
selectedPosition = index;
notifyItemChanged(selectedPosition)
}
then in OnBindViewHolder do this:
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
final ListItemModel listItem = listItems.get(position);
holder.index_number.setText(listItem.getIndexNumber());
holder.title_name.setText(listItem.getTitle());
if(position == selectedPosition){
holder.index_number.setTextColor(MY_COLOR)
holder.title_name.setTextColor(MY_COLOR)
} else {
holder.index_number.setTextColor(NORMAL_COLOR)
holder.title_name.setTextColor(NORMAL_COLOR)
}
}
For multi selected list, you can define a parameter like isChosen in your ListItemModel and change that parameter to true and false and in your OnBindViewHolder check that parameter
I found the solution of your problem
Create a method inside your activity and call when click on item in adapter and send position
public void click(int position) {
TestAdapter.ViewHolder viewHolder = (TestAdapter.ViewHolder) recyclerView.findViewHolderForAdapterPosition(position);
viewHolder.text_color.setTextColor(Color.parseColor("#245251"));
}
Hope this will help you.
I want to have a button at the end of my recyclerview at all times. Basically the user can add more items in the recyclerview and delete them as well but the button should always be at the end. I'm pretty sure I dont add it to my row.xml because it will just make each items in the list have a button.
Easiest way in my opinion is to have the button and a content container in your recyclerview item layout, default with the button's visibility set to gone and the content's layout visible. When displaying the last item, just set the layout's visibility gone and the button's visible.
Or you can use 2 different layout files altogether, like in this sample adapter:
public class YourAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public static final int VIEW_TYPE_BUTTON = 0;
public static final int VIEW_TYPE_ITEM = 1;
private List<Object> items = Collections.emptyList();
public void setData(List<YourItem> itemList) {
items.clear();
items.addAll(itemList);
items.add("button");
notifyDataSetChanged();
}
public class ItemViewHolder extends RecyclerView.ViewHolder {
public TextView tvItemDescription; // just an example
public ItemViewHolder(View itemView) {
super(itemView);
tvItemDescription = itemView.findViewById(R.id.tv_description);
}
}
public class ButtonViewHolder extends RecyclerView.ViewHolder {
public Button button;
public ButtonViewHolder(View itemView) {
super(itemView);
button = itemView.findViewById(R.id.button);
button.addOnClickListener(/*add logic what should happen when the button is clicked*/);
}
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
switch (viewType) {
case VIEW_TYPE_ITEM:
return new ItemViewHolder(inflater.inflate(R.layout.item, parent, false));
case VIEW_TYPE_BUTTON:
return new ButtonViewHolder(inflater.inflate(R.layout.item_button, parent, false));
default:
throw new IllegalArgumentException("no such view type defined");
}
return null;
}
#Override
public int getItemViewType(int position) {
Object item = items.get(position);
return (item instanceof YourItem) ? VIEW_TYPE_ITEM : VIEW_TYPE_BUTTON;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
Object item = items.get(position);
if (item instanceof YourItem) {
YourItem yourItem = (YourItem) item;
YourAdapter.ItemViewHolder itemViewHolder = (ItemViewHolder) holder;
itemViewHolder.tvItemDescription.setText(yourItem.description);
}
}
#Override
public int getItemCount() {
return items.size();
}
}
In this case, by adding an item at the end of the adapter's data set which ist not of type YourItem, the recyclerview will show an item inflated as defined in item_button.xml. Haven't actually tried the code myself, but see if you can get this running.
I am getting data from server and then parsing it and storing it in a List. I am using this list for the RecyclerView's adapter. I am using Fragments.
I extended
RecyclerView.Adapter<RecyclerView.ViewHolder>
this my complete adapter class
public class GridAdapter extends RecyclerView.Adapter<GridAdapter.ViewHolder> {
private List<ProductModel> list;
ItemClickListener itemClickListener;
public GridAdapter(List<ProductModel> list, ItemClickListener itemClickListener) {
this.list = list;
this.itemClickListener = itemClickListener;
}
public void update(int position,ProductModel pm){
Log.v("update adapter",position+"");
list.set(position,pm);
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.grid_item, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final ProductModel cp = list.get(position);
holder.tvName.setText(cp.name);
holder.tvPrice.setText(cp.price);
holder.ratingBar.setRating(cp.rate);
Log.v("grid",cp.id + "=="+ cp.checked);
if(cp.checked==1){
holder.imgCheck.setVisibility(View.VISIBLE);
}else{
holder.imgCheck.setVisibility(View.GONE);
}
LayerDrawable stars = (LayerDrawable) holder.ratingBar.getProgressDrawable();
stars.getDrawable(2).setColorFilter(Color.YELLOW, PorterDuff.Mode.SRC_ATOP);
holder.lnrItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
itemClickListener.onItemClick(cp,position);
}
});
}
#Override
public int getItemCount() {
return list.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
TextView tvName, tvPrice;
ImageView imgMenu, imgCheck;
LinearLayout lnrItem;
RatingBar ratingBar;
public ViewHolder(View itemView) {
super(itemView);
lnrItem = itemView.findViewById(R.id.lnrItem);
imgMenu = itemView.findViewById(R.id.imgMenu);
imgCheck = itemView.findViewById(R.id.imgCheck);
tvName = itemView.findViewById(R.id.tvName);
ratingBar = itemView.findViewById(R.id.ratingBar);
tvPrice = itemView.findViewById(R.id.tvPrice);
}
}
}
and in fragment activity i use this code to load adapter to recyclerview
public void fillListProduct(List<ProductModel> mItems) { //method for load adapter for the first time
mAdapterGrid = new GridAdapter(mItems,this);
recGrid.setAdapter(mAdapterGrid);
}
void update(){ //method to change checked value
mAdapterGrid.update(cp.row,cp);
mAdapterGrid.notifyDataSetChanged();
}
in my custom adapter i have 3 variable lets call id, name, checked, everything work properly, in first load i set 0 as default value for checked, in my scenario user can change checked value by tap the row of recycleview.
my question is, when user tap desire row then checked will change from 0 to 1 and display it to recycelview, i already use notifydatasetchange() but not working.
Please help.
thanks
Try to remove mAdapterGrid.notifyDataSetChanged();
and modify this function in your adpater:
public void update(int position,ProductModel pm){
Log.v("update adapter",position+"");
list.set(position,pm);
notifyItemChanged(position);
}
I have horizontalRecycleView inside verticalRecycleview. and I have made horizontatRecycleView selectable using SparseBooleanArray. So every user clicks on an item from Horizontal list I change the background of that position.
The problem is SparseBooleanArray initialized for each view/row uses same physical address I guess. Because every position I select reflects on all horizontal list.
here is my code:
RecycleView-Vertical:
public class ChatAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case 3:
return new HorizontalListHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.chat_adapter_recycleview_horizontal, parent, false));
}
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (type) {
int type = chatAdapterModels.get(position).getVIEWTYPE();
case HORIZONTAL_LIST:
HorizontalListHolder viewHolder = (HorizontalListHolder) holder;
viewHolder.chatHorizontalAdapter.updateData(chatAdapterModels.get(position).getChatHorizontalModels());
break;
}
}
public final class HorizontalListHolder extends RecyclerView.ViewHolder {
ChatHorizontalAdapter chatHorizontalAdapter = null;
private SparseBooleanArray sparseBooleanArray;
#BindView(R.id.recycle_view)
RecyclerView recyclerView;
public HorizontalListHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
sparseBooleanArray = new SparseBooleanArray();
chatHorizontalAdapter = new ChatHorizontalAdapter(context, sparseBooleanArray);
chatHorizontalAdapter.onItemClickListener(this);
recyclerView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false));
recyclerView.setAdapter(chatHorizontalAdapter);
}
}
}
Horizontal-RecycleView
public class ChatHorizontalAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context context;
private ChatHorizontalAdapter.ClickListner clickListner;
ArrayList<ChatHorizontalModel> horizontalModels;
private SparseBooleanArray sparseBooleanArray;
private int lastPosition;
public ChatHorizontalAdapter(Context activity, SparseBooleanArray sparseBooleanArray) {
context=activity;
this.sparseBooleanArray=sparseBooleanArray;
}
public void updateData(ArrayList<ChatHorizontalModel> horizontalModels,int viewType){
this.horizontalModels=horizontalModels;
notifyDataSetChanged();
// sparseBooleanArray.clear();
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType){
case 1: return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.chat_recycle_adapter_view, parent, false));
// case 2: return new something
}
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
ViewHolder viewHolder = (ViewHolder) holder;
String strings=horizontalModels.get(position).getStr();
viewHolder.textView.setText(strings);
viewHolder.imageView.setImageResource(horizontalModels.get(position).getImages());
changeBackgroundColor(sparseBooleanArray.get(position),viewHolder.cardView,viewHolder.imageView,viewHolder.textView,horizontalModels.get(position).getImages());
}
#Override
public int getItemViewType(int position) {
return viewType;
}
#Override
public int getItemCount() {
return horizontalModels.size();
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
#BindView(R.id.mainCardView)CardView cardView;
#BindView(R.id.image)ImageView imageView;
#BindView(R.id.textView)TextView textView;
public ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this,itemView);
cardView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
if(clickListner!=null){
clickListner.onItemClick(getAdapterPosition());
handleSelection(getAdapterPosition());
notifyDataSetChanged();
}
}
}
public interface ClickListner {
void onItemClick(int position);
}
public void onItemClickListener(ChatHorizontalAdapter.ClickListner clickListner){
this.clickListner=clickListner;
}
public void handleSelection(int position){
sparseBooleanArray.clear();
for (int i = 0; i <=horizontalModels.size() ; i++) {
if (i==position) {
sparseBooleanArray.put(i, true);
} else {
sparseBooleanArray.put(i,false);
}
}
}
public void changeBackgroundColor(boolean set, View cardView, ImageView imageView, TextView textView, int images){
// change bck color of selected item
}
}
RecyclerView doesn't create a view to each row in your list, if you have 100 rows, it will create maybe about 7 or 8 " just the number visible on your screen" so you will have about 7 or 8 SparseBooleanArray and it will reuse them with all rows, so a change in one row will affect another if it uses the same view reference.
You should create an ArrayList<SparseBooleanArray> with a number that matches your rows count in the VerticalAdapter and update the SparseBooleanArray inside HorizontalAdapter when onBindViewHolder() is called
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
HorizontalListHolder viewHolder = (HorizontalListHolder) holder;
// Where mSparseList is ArrayList<SparseBooleanArray>
viewHolder.updateSparseArray(mSparseList.get(position));
}
and in the horizontal adapter add the method updateSparseArray() that updates the SparseBooleanArray data.
Im trying show a toast msg when clicked on a item from my RecycleView, Ive tried many examples,
but its not giving me anything. Can somebody give me a different example that i can follow, at the end i wanna set the onClick to show a new fragment. If I can get an example on that, it will be great.
Im using this code:
public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.ViewHolder> {
private List<Movie> movies;
private int card_layout;
private Context mContext;
public MovieAdapter(List<Movie> movies, int card_layout, Context context) {
this.movies = movies;
this.card_layout = card_layout;
this.mContext = context;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
final View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(card_layout, viewGroup, false);
return new ViewHolder(itemView);
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
final Movie movie = movies.get(i);
viewHolder.movieImage.setImageDrawable(mContext.getDrawable(movie.getImageResourceId(mContext)));
viewHolder.movieName.setText(movie.mName);
viewHolder.currentMovie = movie;
}
#Override
public int getItemCount(){
return movies.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView movieName;
public ImageView movieImage;
public Movie currentMovie;
public ViewHolder( View itemView) {
super(itemView);
movieName = (TextView) itemView.findViewById(R.id.movieName);
movieImage = (ImageView)itemView.findViewById(R.id.movieImage);
itemView.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View itemView){
Toast.makeText(itemView.getContext(),currentMovie.mName,Toast.LENGTH_SHORT ).show();
}
});
}
}
}
Do I have to implement something in my MainActivity as well?
and please dont get mad with me, Im just a starting with all this. All your help will be appriciated. thanks
Layout is not clickable by default. to make clickable add setClickable to true :
View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(
card_layout, viewGroup, false);
itemView.setClickable(true);
itemView.setFocusableInTouchMode(true);
After wasting an hour of time, I found the most appropriate and simplest solution to this problem:
Try this, it will definitely work.No matters whether cards are used in grids or not.
RecyclerView Adapter:
ProductCardRecyclerViewAdapter.java
public class ProductCardRecyclerViewAdapter extends RecyclerView.Adapter<ProductCardViewHolder> {
public final String TAG=getClass().getSimpleName();
Context context;
private List<ProductEntry> productList;
private Integer[] cardImages;
String[] cardTitle;
String[] cardSubtitle;
public ProductCardRecyclerViewAdapter(Context context, Integer[] imageList, String[] cardTitle, String[] cardSubtitle) {
this.cardImages = imageList;
this.cardTitle = cardTitle;
this.cardSubtitle = cardSubtitle;
this.context = context;
}
#NonNull
#Override
public ProductCardViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.product_card, parent, false);
return new ProductCardViewHolder(layoutView);
}
#Override
public void onBindViewHolder(#NonNull ProductCardViewHolder holder, int position) {
// TODO: Put Recycler ViewHolder Cards binding code here in MDC-102
holder.imgCard.setImageResource(cardImages[position]);
holder.productTitle.setText(cardTitle[position]);
holder.productPrice.setText(cardSubtitle[position]);
holder.productCard.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "onClick: Material Card clicked "+cardTitle[position]+" : "+context.getClass());
//TODO: Perform card clicked working
Context c = v.getContext();
Toast.makeText(c, cardTitle[position], Toast.LENGTH_SHORT).show();
}
});
}
#Override
public int getItemCount() {
return cardImages.length;
}
}
RecyclerViewHolder
ProductCardViewHolder.java
public class ProductCardViewHolder extends RecyclerView.ViewHolder {
CardView productCard;
ImageView imgCard;
public TextView productTitle;
public TextView productPrice;
public ProductCardViewHolder(#NonNull View itemView) {
super(itemView);
imgCard = itemView.findViewById(R.id.product_image);
productTitle = itemView.findViewById(R.id.product_title);
productPrice = itemView.findViewById(R.id.product_price);
productCard=itemView.findViewById(R.id.cardofproducts);
// TODO: Find and store views from itemView
}
}
Hope, it will help a lot.