RecyclerView Adapter notifyItemChanged triggers twice - java

I'm trying to highlight my selected item in recyclerView when it's clicked.But it triggers two items instead. Please help me. Should i store clicked items as arraylist and clear them on new one clicked?
public class StationsAdapter extends RecyclerView.Adapter<StationsHolder> {
List<Station> stations;
public StationsAdapter(List<Station> stations){
this.stations = stations;
}
public void changeItemAtPosition(int position) {
notifyItemChanged(position);
}
#Override
public StationsHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new StationsHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.stations_item,parent,false));
}
#Override
public void onBindViewHolder(StationsHolder holder, int position) {
bind(holder);
}
private void bind(final StationsHolder holder) {
holder.tvTitle.setText(stations.get(holder.getAdapterPosition()).getName());
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
holder.tvTitle.setTextColor(ContextCompat.getColor(AppDelegate.getContext(),R.color.colorAccent));
}
});
}
#Override
public int getItemCount() {
return stations.size();
}
}

This is due to recycler reusing the same view when you scroll. In order to fix that, you will have to do the next:
Store the selected item when you click on it. In a variable or in an array if you want more than one item
Check the selected item variable/array in the bind method to know if you have to color it or not
That way it will work flawlessly

Related

Recycler View list modifying all the items when I delete one

I inserted a Recycler view on my app to make a shopbasket list.
Now I have two problems. When I copy my mUsers list to a JSON, all the items of the list copy the Values of the last item (e.g. If I have a list = [1,2,3,4] when I copy it to Json it transforms to [4,4,4,4]).
On my Recycler view, the items are showing correctly, but when I delete one item from the list, it transforms the list below it (e.g. If I have a list [1,2,3,4] and delete 2, the list transforms in [1,3,3].
Here is my RecyclerView Code:
public class LineAdapter extends RecyclerView.Adapter<LineHolder> {
private final List<Option> mUsers;
public LineAdapter(ArrayList users) {
mUsers = users;
}
#Override
public LineHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new LineHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.main_line_view, parent, false));
}
#Override
public void onBindViewHolder(LineHolder holder, final int position) {
holder.title.setText(String.format(Locale.getDefault(), "%s",
mUsers.get(position).getNome().label
));
holder.subtitle.setText(String.format(Locale.getDefault(), "Detalhes: %s",
mUsers.get(position).getDados()
));
lista.add(mUsers.get(position));
holder.deleteButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
removerItem(position);
}
});
}
#Override
public int getItemCount() {
return mUsers != null ? mUsers.size() : 0;
}
public List<Option> getList(){
return mUsers;
}
public void updateList(Option user) {
insertItem(user);
}
private void insertItem(Option user) {
mUsers.add(user);
notifyItemInserted(getItemCount());
}
private void removerItem(int position) {
mUsers.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, mUsers.size());
}
}
Solved the mistery, the code is working. The problem was when I was inserting to the list, I was using the same object everytime (I only updated it), so the pointer on every position was pointing to the same object.

Array deletion taking place from the lower index first?

My app is in the foreground. and there are 2 items into a recyclerview list. whenever I delete my 2nd item from the list and after that click on the 1st item my app gets crash. and in Logcat I get the error like - java.lang.IndexOutOfBoundsException: Index: 1, Size: 1. But when I delete 1st item from my list and after that click on the remaining 2nd item my app works fine.
public void onClick(View v) {
onClick.onItemCli(position, banners.get(position));
}
I tried out by doing position -1 in this method like the below code.
public void onClick(View v) {
onClick.onItemCli(position-1, banners.get(position-1));
}
but by this case my app is crashing whenever there are 2 items in list and i click on 2nd item.
The error I am getting is :
java.lang.ArrayIndexOutOfBoundsException: length=12; index=-1
public class HeaderSliderAdapter extends RecyclerView.Adapter<HeaderSliderAdapter.ViewHolder> {
public List<Banner> banners;
public Context context;
private OnItemClicked onClick;
public HeaderSliderAdapter(Context context, List<Banner> banners) {
this.banners = banners;
this.context = context;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.home_header, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final HeaderSliderAdapter.ViewHolder holder, final int position) {
if (TextUtils.isEmpty(banners.get(position).getmSliderImage())) {
holder.mSliderImageView.setImageResource(R.drawable.no_img_ava);
holder.mBackgroundImage.setVisibility(View.GONE);
} else {
Picasso.with(context)
.load(banners.get(position).getmSliderImage())
.fit()
.into(holder.mSliderImageView, new Callback() {
#Override
public void onSuccess() {
holder.mBackgroundImage.setVisibility(View.GONE);
holder.tvWarningFailedtoLoad.setVisibility(View.GONE);
holder.tvTitle.setVisibility(View.VISIBLE);
holder.mSliderImageView.setClickable(true);
}
#Override
public void onError() {
holder.mBackgroundImage.setVisibility(View.GONE);
holder.tvWarningFailedtoLoad.setVisibility(View.VISIBLE);
holder.tvTitle.setVisibility(View.GONE);
holder.mSliderImageView.setClickable(false);
}
});
}
holder.mSliderImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onClick.onItemCli(position, banners.get(position));
}
});
}
#Override
public int getItemCount() {
return banners.size(); }
public void setOnClick(OnItemClicked onClick) {
this.onClick = onClick;
}
public interface OnItemClicked {
void onItemCli(int position, Banner passData);
}
public class ViewHolder extends RecyclerView.ViewHolder {
public ImageView mSliderImageView,mBackgroundImage;
public TextView tvWarningFailedtoLoad;
public TextView tvTitle;
View mView;
public ViewHolder(View itemView) {
super(itemView);
mView = itemView;
mSliderImageView = mView.findViewById(R.id.mSliderImage);
mBackgroundImage = mView.findViewById(R.id.backgroundImage);
tvWarningFailedtoLoad = mView.findViewById(R.id.tvFailedtoLoad);
tvTitle = mView.findViewById(R.id.mSliderImagetitle);
tvTitle.setSelected(true);
tvTitle.setSingleLine(true);
}
}
}
The error is getting in the below method.
holder.mSliderImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onClick.onItemCli(position, banners.get(position));
}
});
java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
I gues that the clue to your issue is in the following part of the documentation:
Note that unlike ListView, RecyclerView will not call this method again if the position of the item changes in the data set unless the item itself is invalidated or the new position cannot be determined. For this reason, you should only use the position parameter while acquiring the related data item inside this method and should not keep a copy of it. If you need the position of an item later on (e.g. in a click listener), use getAdapterPosition() which will have the updated adapter position. Override onBindViewHolder(ViewHolder, int, List) instead if Adapter can handle efficient partial bind.
So change the implementation of your setOnClickListener() method so it does not use the parametere position, but calls getAdapterPosition() instead to acquire the fresh value of your position.

Android Firebase Recyclerview - How to deselect previous selected item?

I have a simple question. I have a recyclerview with list items, what I can do is highlight the items you click on. My problem is that I don't know how to deselect the previous item the user has clicked on so only 1 item can be selected.
private void loadCategories() {
adapter = new FirebaseRecyclerAdapter<Category, CategoryViewHolder>(
Category.class,
R.layout.categoryaddquestions_layout,
CategoryViewHolder.class,
categories
) {
#Override
protected void populateViewHolder(CategoryViewHolder viewHolder, final Category model, int position) {
viewHolder.category_name.setText(model.getName());
viewHolder.setItemClickListener(new ItemClickListener() {
#Override
public void onClick(View view, int position, boolean isLongClick) {
view.setSelected(false);
}
});
}
};
adapter.notifyDataSetChanged();
listCategory.setAdapter(adapter);
}
Here is my view holder I use
public class CategoryViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
public TextView category_name;
public ImageView category_image;
public LinearLayout category_layout;
private ItemClickListener itemClickListener;
public CategoryViewHolder(View itemView) {
super(itemView);
category_layout = itemView.findViewById(R.id.categoryaddquestion_layout);
category_image = itemView.findViewById(R.id.category_image);
category_name = itemView.findViewById(R.id.category_name);
itemView.setOnClickListener(this);
}
public void setItemClickListener(ItemClickListener itemClickListener) {
this.itemClickListener = itemClickListener;
}
#Override
public void onClick(View view) {
itemClickListener.onClick(view,getAdapterPosition(),false);
}
}
Create a variable in your adapter called SelectedPosition. On click, update that variable to the adapterPosition and reload the recyclerView.
Make a check in populateViewHolder saying:
if selectedPosition == position {
//highlighted
}
else {
//set back to normal
}
here is my own solution.
#Override
protected void populateViewHolder(CategoryViewHolder viewHolder, final
Category model, int position) {
viewHolder.category_name.setText(model.getName());
if (position == Common.index) {
viewHolder.category_layout.setSelected(true);
} else {
viewHolder.category_layout.setSelected(false);
}
viewHolder.setItemClickListener(new ItemClickListener() {
#Override
public void onClick(View view, int position, boolean isLongClick) {
Common.index = position;
adapter.notifyDataSetChanged();
}
});
}
};
adapter.notifyDataSetChanged();
listCategory.setAdapter(adapter);
}
I just used a Common java class and problem solved with a public static int index

How to change the text of a Button in recyclerview when adapter class is loading second time?

This is what I want according to picture
I am sharing my code
Model Class
class Msg{
private Boolean BtnName;
public Boolean getBtnName() {
return BtnName;
}
public void setBtnName(Boolean btnName) {
BtnName = btnName;
}
}
AdapterClass:
#Override
public void onBindViewHolder(final TeachersTodayScheduleViewHolder holder, final int position) {
mTeacherScheduleArrayList.get(position).setBtnName(false);
holder.btn_checkin.setTag(position);
holder.btn_checkin.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Object data = v.getTag();
if (data != null) {
// I have set the Boolean value to true after clicking this particular button, but whn the arraylist again loading, the boolean value disaapear
mTeacherScheduleArrayList.get(position).setBtnName(true);
if(mFlagtrue!=null && mFlagtrue.contentEquals("flag_true"))
{
if(mTeacherScheduleArrayList.get(position).getBtnName().equals(true))// when it return true, then below colde work, this is not happening
{
holder.btn_checkin.setText("Attended");
holder.btn_checkin.setBackgroundResource(R.color.green_btn);
holder.btn_checkin.setEnabled(false);
}
}
}
What am I doing wrong, please help,
Thanks in advance
public void onBindViewHolder(final TeachersTodayScheduleViewHolder holder, final int position) {
mTeacherScheduleArrayList.get(position).setBtnName(false); // <<<<<<
.......
You're setting the value to false every time the view is bound. Remove this line.

Able to click on two items at the same time in a RecyclerView

I have a list of items in a RecyclerView and i set the onClickListener in the onBindViewHolder for each view. The click listener works just fine, the issue is that I can click on two items in the list at the same time and both of them will run their onClick method. When you have ListViews if you try to click on two items at the same time it does not allow you.
For example:
Lets say you are already touching on an item in a listview and during that time you try to touch another item it won't let you. Recyclerview allows that.
How can we make the RecyclerView to work like a ListView for when clicking?
Below is my implementation
public class DataCardAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context mContext;
private ArrayList<Data> mDatas = new ArrayList<>();
private Data mData;
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View card = LayoutInflater.from(mContext).inflate(R.layout.card, parent, false);
return new DataCardViewHolder(mContext, card, mData);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
Data data = mDatas.get(position);
((DataCardViewHolder )holder).configureDataCard(data);
}
public static class DataCardViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
private Context mContext;
private Data mData;
public DataCardViewHolder(Context context, View view, Data data) {
super(view);
mContext = context;
mData= data;
}
public void configureDataCard(final Data data) {
mData= data;
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
Log.v("DataCardViewHolder","onClick with data: " + mData.getData().toString());
}
}
}
My RecyclerView is programmatically add in Java but not in xml. And I try this and it works:
mRecyclerView.setMotionEventSplittingEnabled(false);
If your RecyclerView is add in xml, you may try adding this in your RecyclerView:
android:splitMotionEvents="false"
And now in the recycler-list when you click on one item and don't let go, you can not click another item.
Unfortunately, RecyclerView will not handle that for you. Create a Handler with timeout:
public class DelayedClick {
protected boolean canClick = true;
protected Handler myHandler = new Handler();
private Runnable mMyRunnable = new Runnable()
{
#Override
public void run() {
canClick = true;
}
};
public boolean getState() {
if(canClick) {
myHandler.postDelayed(mMyRunnable, 200);
canClick = false;
return true;
}
else return false;
}
}
#Override
public void onClick(View v) {
if (!reClick.getState()) {
return;
}
//Code to execute on click
}

Categories

Resources