I am using RecyclerView to show the list of items. I have two TextViews one below other. Initially the second TextView is set to singleline = true.Now on item click, I am setting singleline = false. This is just done to expand it on click (Like ExpandableList). The code works fine but the problem is that as RecyclerView uses the recycled item other items TextView values are also being set to singleline = false. Now how can I avoid this?
Code
public class InboxAdapter extends RecyclerView.Adapter<InboxAdapter.InboxViewHolder> {
private Context context;
private List<InboxModel> listInbox;
private InboxModel currentItem;
public InboxAdapter(Context context, List<InboxModel> listInbox) {
this.context = context;
this.listInbox = listInbox;
}
#Override
public InboxViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.custom_inbox_item, parent, false);
InboxViewHolder viewHolder = new InboxViewHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(final InboxViewHolder holder, int position) {
currentItem = listInbox.get(position);
holder.tvHeader.setText(currentItem.header);
holder.tvMsg.setText(currentItem.msg);
holder.tvHeader.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
holder.tvMsg.setSingleLine(false);
LinearLayout.LayoutParams lp= (LinearLayout.LayoutParams) holder.tvMsg.getLayoutParams();
lp=new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
holder.tvMsg.setLayoutParams(lp);
}
});
}
#Override
public int getItemCount() {
return listInbox.size();
}
class InboxViewHolder extends RecyclerView.ViewHolder {
private TextView tvImageHeader;
private TextView tvHeader;
private TextView tvMsg;
private TextView tvDate;
public InboxViewHolder(View itemView) {
super(itemView);
tvImageHeader = (TextView) itemView.findViewById(R.id.tv_img_header);
tvHeader = (TextView) itemView.findViewById(R.id.tv_header);
tvMsg = (TextView) itemView.findViewById(R.id.tv_msg);
tvDate = (TextView) itemView.findViewById(R.id.tv_date);
}
}
You have to set the attribute in all cases to avoid issues with the recycled items.
#Override
public void onBindViewHolder(final InboxViewHolder holder, int position){
//....
if (currentItem.myBoolean){
holder.tvMsg.setSingleLine(true);
} else {
holder.tvMsg.setSingleLine(false);
}
//...
}
In your click event just change the value inside the object (not the view!). Somenthing like:
holder.tvHeader.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
currentItem.myBoolean = true;
//call the notify !
}
});
If this state is exclusive, instead of using a variable inside the item use a variable (InboxModel checkedItem) inside the Adapter.
Hmmm For that you have to saved click postion and write condition in onBindViewHolder() like below
#Override
public void onBindViewHolder(final InboxViewHolder holder, int position) {
if(Postion==Mysavedposition){
holder.tvMsg.setSingleLine(false);
}else{
holder.tvMsg.setSingleLine(true);
}
currentItem = listInbox.get(position);
holder.tvHeader.setText(currentItem.header);
holder.tvMsg.setText(currentItem.msg);
holder.tvHeader.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Mysavedposition=position;
holder.tvMsg.setSingleLine(false);
LinearLayout.LayoutParams lp= (LinearLayout.LayoutParams) holder.tvMsg.getLayoutParams();
lp=new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
holder.tvMsg.setLayoutParams(lp);
}
});
}
Related
I have two RecylerView one is inside other. Now I want to update my inner recyclerView item on click of an item. So after updating my local database I am calling notifyDataSetChanged(), but this is not calling my onBindViewHolder() again.
Below is my code for onItemClick():
public class MyViewHolder extends RecyclerView.ViewHolder {
private ImageView imageView;
private TextView tvTutorialName, text_new;
private ConstraintLayout main_item;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
main_item = itemView.findViewById(R.id.main_item);
tvTutorialName = itemView.findViewById(R.id.tutorial_name);
text_new = itemView.findViewById(R.id.text_new);
imageView = itemView.findViewById(R.id.tutorial_image);
main_item.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
manager.save("tutorialTapGuide", "notFirstTime");
TutorialViewModel tutorialViewModel = new TutorialViewModel(SmartMirror.getInstance());
int pos = (int) view.getTag();
tutorialName = mVideoList.get(pos).serverFolderName;
tutorialId = mVideoList.get(pos).objectId;
// update
tutorialViewModel.updateNewTutorialToOld(tutorialName);
notifyDataSetChanged();
// allCheckingAfterStoragePermissionGranted(view.findViewById(R.id.tutorial_image), pos);
}
});
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder myViewHolder, int position) {
myViewHolder.main_item.setTag(position);
String tutorialFolder = mVideoList.get(position).serverFolderName;
myViewHolder.tvTutorialName.setText(mVideoList.get(position).displayTutorialName);
myViewHolder.main_item.setTransitionName("trans_image" + position);
TutorialViewModel tutorialViewModel = new TutorialViewModel(SmartMirror.getInstance());
int isNewTutorial = tutorialViewModel.isNewTutorial(tutorialFolder);
if (isNewTutorial == 1)
myViewHolder.text_new.setVisibility(View.VISIBLE);
if (CommonUtilities.haveNetworkConnection(mContext)) {
Glide.with(mContext).load(mVideoList.get(position).imageURL)
.apply(new RequestOptions().placeholder(R.drawable.video_placeholder).error(R.drawable.video_placeholder))
.into(myViewHolder.imageView);
} else {
myViewHolder.imageView.setImageResource(R.drawable.video_placeholder);
}
}
I am setting the adapter of child recyclerView from parent recyclerView adapter.
holder.mAdapter = new TutorialVideoAdapter(mctx, mFrg, mTracker, videosArrayList, parentView, parent_fragment, catList.get(position), tutorialStepsList, player, holder.recyclerView_tutorial);
holder.recyclerView_tutorial.setLayoutManager(new GridLayoutManager(mctx, 3));
holder.recyclerView_tutorial.setAdapter(holder.mAdapter);
holder.recyclerView_tutorial.setScrollY(00);
I am now clueless why notifyDataSetChanged() not triggering onBindViewHolder.
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 really need some help regarding the recyclerview list.
Suppose there is a list with 10 items, Suppose i click one item (at position 1), i need make the view(button/image) visible on that particular item clicked. But when i will click any other item (at position 5) then the view(button/image)should be visible on position 5 and not on position 1. Can anyone tell me how can i achieve this result ? I would really appreciate if anybody help me with this. If you want some more details please do comment.
Thanks.
in recyclerview adapter when handling click listener in onBindViewHolder(Holder, Position) method you must choose the position of the view not fixed number(eg: 1,2 ,3)
ex:
=========
#Override
public void onBindViewHolder(final YourHolder holder, final int position) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
view.setVisibility(View.VISIBLE);
}
});
i hope this code is good enough to explain the way to do that:
public class myAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<Object> list;
public myAdapter(List<Object> list){
this.list=list;
}
ItemViewHolder openedRow=null;
#Override
public RecyclerView.ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list, parent, false);
ItemViewHolder view = new ItemViewHolder(v);
v.setOnClickListener(this);
return view;
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
final ItemViewHolder myRowHolder = (ItemViewHolder) holder;
myRowHolder.itemView.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
if(openedRow!=null)
{
openedRow.image.setVisibility(View.GONE);
}
openedRow=myRowHolder;
openedRow.image.setVisibility(View. VISIBLE);
}
});
}
public static class ItemViewHolder extends RecyclerView.ViewHolder {
ImageView image;
public ItemViewHolder(View itemView) {
super(itemView);
image = (ImageView)
itemView.findViewById(R.id.img_item_main);
}
}
}
I've updated mi code, this is working fine!
You need to add a boolean attribute to your ItemAdapter (the item you inflate on the recyclerView).
public class YourItem{
private String title;
private String image;
private boolean showView;
public String getTitle() {
return title;
}
public String getImage() {
return image;
}
public void setShowView(boolean showView) {
this.showView = showView;
}
public boolean mustShowView() {
return showView;
}
}
Then on your Adapter:
public class YourAdapter extends RecyclerView.Adapter<YourAdapter.MyViewHolder> {
...
private int lastPosition;
...
class MyViewHolder extends RecyclerView.ViewHolder {
private TextView title;
private ImageView image;
private View.OnClickListener onRowClicked = new View.OnClickListener() {
#Override
public void onClick(View view) {
if (lastPosition != getAdapterPosition()) {
Prize lastItem = getItem(lastPosition);
lastItem.setShowView(false);
adapterList.set(lastPosition, lastItem);
lastPosition = getAdapterPosition();
}
Prize item = getItem(getAdapterPosition());
item.setShowView(!item.mustShowView());
adapterList.set(getAdapterPosition(), item);
notifyDataSetChanged();
}
};
MyViewHolder(View itemView) {
super(itemView);
image = (ImageView) itemView.findViewById(R.id.image);
title = (TextView) itemView.findViewById(R.id.prize_title);
itemView.setOnClickListener(onRowClicked);
}
void bind(final Prize item) {
title.setText(item.getTitle().toUpperCase());
Picasso.with(context).load(item.getImage()).into(image);
image.setVisibility(item.mustShowView() ? View.VISIBLE : View.GONE);
}
}
}
I had this problem and change these:
1-Don't set attribute of android:visibility="visible" or "gone" in xml
2-Set this attribute in function of onBindViewHolder() in adapter():
holder.yourView.setVisibility(View.GONE);
holder.yourBottun.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!(holder.yourView.isShown() ) {
holder.yourView.setVisibility(View.VISIBLE);
}
}
});
Imagine a CardView with a button and text field. When I press on a button, text should change in the textfield. Code for my recyclerView is given below, where I am setting the text and a listener for a button:
#Override
public void onBindViewHolder(CategoriesDetailedViewHolder holder, int position) {
final CategoryDetailedModel model = categoriesDetailedModels.get(position);
holder.description.setText(model.getQuestion());
holder.btnPlus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
holder.description.setText("some text");
}
});
}
The main problem is with the string:
holder.description.setText("some text");
It cannot be accessed from an inner class.
Is anyone have any idea how to change text in card view from itself.
Update 1
public class CategoriesDetailedViewHolder extends RecyclerView.ViewHolder{
CardView cv;
TextView description;
TextView positiveCounter;
TextView negativeCounter;
TextView btnPlus;
View negativeCounterWrapper;
View positiveCounterWrapper;
CategoriesDetailedViewHolder(View itemView) {
super(itemView);
cv = (CardView)itemView.findViewById(R.id.cv);
description = (TextView)itemView.findViewById(R.id.main_text);
positiveCounter = (TextView)itemView.findViewById(R.id.tv_positive_count_in_circle);
negativeCounter = (TextView)itemView.findViewById(R.id.tv_negative_count_in_circle);
btnPlus = (TextView)itemView.findViewById(R.id.plus_button);
positiveCounterWrapper = itemView.findViewById(R.id.positive_count_wrapper);
negativeCounterWrapper = itemView.findViewById(R.id.negative_count_wrapper);
cv.setPreventCornerOverlap(false);
}
}
#Override
public CategoriesDetailedViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.categories_detailed_list_row_view, parent, false);
CategoriesDetailedViewHolder pvh = new CategoriesDetailedViewHolder(v);
context = parent.getContext();
return pvh;
}
Instead of,
TextView description;
Make it,
public TextView description;
EDIT:
Change the following,
#Override
public void onBindViewHolder(CategoriesDetailedViewHolder holder, int position) { ... }
to,
#Override
public void onBindViewHolder( final CategoriesDetailedViewHolder holder, int position) { .... }
I use this code and it works fine, you just need to use onBind method.
This is one example, I use this in my project and you can just change it to suit your needs:
public void onBindViewHolder(final TipsViewHolder TipsViewHolder, final int i) {
TipsViewHolder.cardText.setText("Old text");
TipsViewHolder.txtBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
TipsViewHolder.cardText.setText("New Text");
}
});
}
I am having trouble removing items from RecyclerView. When I click on delete, the item is removed from RecyclerView, but comes back when I open the app again. I'm hoping it is just a minor issue that someone here can point out or direct me to what area to troubleshoot. The removeItem(String item) in bold is what I think is the issue. You can't see it in this post, but it is "not used".
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.MyViewHolder> {
private List<Grocery> mListData;
private SQLGroceryHelper helper;
RecyclerViewAdapter adapter;
//Adapter's Constructor//
public RecyclerViewAdapter(List<Grocery> mDataList) {
this.mListData = mDataList;
}
//Provide a reference to the views for each contact item//
public class MyViewHolder extends RecyclerView.ViewHolder {
TextView rowItem;
ImageButton purchasedButton;
ImageButton deleteButton;
LinearLayout linearLayout;
public MyViewHolder(View itemView) {
super(itemView);
linearLayout = (LinearLayout) itemView.findViewById(R.id.recycler_row);
rowItem = (TextView) itemView.findViewById(R.id.item_field1);
purchasedButton = (ImageButton) itemView.findViewById(R.id.item_purchased);
deleteButton = (ImageButton) itemView.findViewById(R.id.delete_item);
}
}
//Inflate the view based on the viewtype provided//
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//Create a new view by inflating the row item xml//
View row = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_row, parent, false);
//Set the view to the ViewHolder//
MyViewHolder holder = new MyViewHolder(row);
return holder;
}
//Display data at the specified position//
#Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
holder.rowItem.setText(mListData.get(position).getTextItem());
holder.purchasedButton.setOnClickListener(new View.OnClickListener() {
//Ignore this click for now//
#Override
public void onClick(View v) {
removeItem(position);
}
});
holder.deleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
removeItem(position);
}
});
}
public void **removeItem**(String item) {
int position = mListData.indexOf(item);
if (position != -1) {
mListData.remove(item);
notifyItemRemoved(position);
}
}
public void removeItem(int position) {
mListData.remove(position);
notifyItemRemoved(position);
}
#Override
public int getItemCount() {
if (mListData == null) {
return 0;
}
return mListData.size();
}
}
You are removing the data from local object, mListData I guess the original data object remains intact. Remove the data item from the original data object as well
Declare a interface
public interface AdapterCommunication{
void removeStringItem(int position);
}
then in your adapter
private AdapterCommunication mListener;
public void setOnClickListener(AdapterCommunication listener){
mListener = listener;
}
Then from your activity where you initialize the adapter
RecyclerViewAdapter adapter = new RecyclerViewAdapter(list);
adapter.setOnClickListener(new AdapterCommunication{
public void removeStringItem(int position){
list.remove(position);
adapter.notifyDataSetChanged();
}
});
In your adaper,
holder.deleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mListener.remove(position);
}
});