简体   繁体   中英

How to move item to the last position when Checkbox is checked? (RecyclerView, ViewHolder)

在此处输入图片说明

I try to make a RecyclerView who contains checkBoxes (if something is already bought). I want to move item to the last position on CheckBox checked change and set Background to green. I tried to remove item from ArrayList and then add it again but it works only with String name, it doesn't keep state (checkbox state or color). Other times it throws Exception: Cannot call this method while RecyclerView is computing a layout or scrolling. Can somebody tells me what i am doing wrong?

Sry for my english.

import android.graphics.Color;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.ArrayList;

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> 
{
private ArrayList<String> mDataset;
private ArrayList<FinalListItem> finalListItems;
public MyAdapter(ArrayList<String> mDataset) {
this.mDataset = mDataset;
[enter image description here][1]finalListItems = new ArrayList<>();
    for (String a : mDataset) {
        finalListItems.add(new FinalListItem(a, false));
    }
}
@Override
public long getItemId(int position) {
    return position;
}
@Override
public int getItemViewType(int position) {
    return position;
}
@Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
    holder.final_list_TextView1.setText(finalListItems.get(position).getName());
    if(finalListItems.get(position).isChecked){
        holder.linearLayout.setBackgroundColor(Color.rgb(0, 225, 0));
    }
    else{
        holder.linearLayout.setBackgroundColor(Color.rgb(255, 255, 255));
    }
    holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            FinalListItem finalItem = new FinalListItem(finalListItems.get(position).getName(),true);
            if (isChecked) {
                holder.linearLayout.setBackgroundColor(Color.rgb(0, 225, 0));
                finalListItems.remove(finalListItems.get(position));
                finalListItems.add(finalItem);
                notifyDataSetChanged(); //weird behaviour of app
            } else {
                holder.linearLayout.setBackgroundColor(Color.rgb(255, 255, 255));
            }

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

private class FinalListItem {
    private String name;
    private boolean isChecked;

    public FinalListItem(String name, boolean isChecked) {
        this.name = name;
        this.isChecked = isChecked;
    }

    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean isChecked) {
        this.isChecked = isChecked;
    }

    public String getName() {
        return name;
    }
}

public class MyViewHolder extends RecyclerView.ViewHolder {
    final public TextView final_list_TextView1;
    final public CheckBox checkBox;
    final public LinearLayout linearLayout;

    public MyViewHolder(View view) {
        super(view);
        final_list_TextView1 = (TextView) view.findViewById(R.id.final_list_TextView1);
        checkBox = (CheckBox) view.findViewById(R.id.checkBox1);
        linearLayout = (LinearLayout) view.findViewById(R.id.linearLayout1);
    }
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.finallist_list_layout, parent, false);
    return new MyViewHolder(view);
}

}

The main thing you should change about your code is that it is important to only change the data and then let the Adapter display the list according to the changed data. That means, you should not change eg the background yourself but you should change the list of FinalListItem and notify the Adapter.

@Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
    holder.final_list_TextView1.setText(finalListItems.get(position).getName());

    // remove the Listener before setting the checked state programmatically
    // and set all the attributes (checked state and background color) here
    holder.checkBox.setOnCheckedChangeListener(null);
    if(finalListItems.get(position).isChecked){
        holder.linearLayout.setBackgroundColor(Color.rgb(0, 225, 0));
        holder.checkBox.setChecked(true);
    }
    else{
        holder.linearLayout.setBackgroundColor(Color.rgb(255, 255, 255));
        holder.checkBox.setChecked(false);
    }
    holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            // Lint warning : don't use "final int position"
            int currentPosition = holder.getAdapterPosition();
            if (isChecked) {
                FinalListItem finalItemBefore = finalListItems.get(currentPosition);
                FinalListItem finalItemAfter = new FinalListItem(finalItemBefore.getName(),true);
                finalListItems.remove(finalItemBefore);
                finalListItems.add(finalItemAfter);
                notifyDataSetChanged();
            } else {
                finalListItems.get(currentPosition).setChecked(false);
                // no need to update the whole list since only one item has changed
                notifyItemChanged(currentPosition);
            }
        }
    });
}

Please note also that it is not a good idea to return position from getItemViewType() . By doing this, you tell the runtime that every View is unique and recycling View s is impossible which will be bad for the performance of the RecyclerView .

The way your list is implemented, one View type is sufficient, so you need not override the method at all since that's the default implementation:

@Override
public int getItemViewType(int position) {
    return 0;
}

@0X0nosugar answer is correct :), i found that we can also use

notifyItemRemoved(currentposition); //after removing an item
notifyItemInserted(currentposition); //after inserting an item

It gives us good looking smooth animation for our Adapter.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM