14

を使用して作成したアイテムのリストがありますRecyclerView。ユーザーがそれらのいずれかをクリックすると、選択したアイテムの背景色が変更されます。問題は、アイテムをスクロールしてリサイクルすると、一部のアイテムが選択したアイテムの背景色になることです (これは間違っています)。Adapterここで私のコードを見ることができます:

public class OrderAdapter extends RecyclerView.Adapter<OrderAdapter.ViewHolder> {

private static final String SELECTED_COLOR = "#ffedcc";

private List<OrderModel> mOrders;

public OrderAdapter() {
    this.mOrders = new ArrayList<>();
}

public void setOrders(List<OrderModel> orders) {
    mOrders = orders;
}

public void addOrders(List<OrderModel> orders) {
    mOrders.addAll(0, orders);
}

public void addOrder(OrderModel order) {
    mOrders.add(0, order);
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    Context context = parent.getContext();
    LayoutInflater inflater = LayoutInflater.from(context);

    // Inflate the custom layout
    View contactView = inflater.inflate(R.layout.order_main_item, parent, false);

    // Return a new holder instance
    ViewHolder viewHolder = new ViewHolder(contactView);
    return viewHolder;
}

@Override
public void onBindViewHolder(final ViewHolder viewHolder, final int position) {
    final OrderModel orderModel = mOrders.get(position);

    // Set item views based on the data model
    TextView customerName = viewHolder.customerNameText;

    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MM/dd/yyyy'   'HH:mm:ss:S");
    String time = simpleDateFormat.format(orderModel.getOrderTime());
    customerName.setText(time);

    TextView orderNumber = viewHolder.orderNumberText;
    orderNumber.setText("Order No: " + orderModel.getOrderNumber());

    Button button = viewHolder.acceptButton;
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            viewHolder.userActions.acceptButtonClicked(position);
        }
    });

    final LinearLayout orderItem = viewHolder.orderItem;
    orderItem.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            viewHolder.userActions.itemClicked(orderModel);
            viewHolder.orderItem.setBackgroundColor(Color.parseColor(SELECTED_COLOR));
        }
    });
}

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


public static class ViewHolder extends RecyclerView.ViewHolder implements OrderContract.View {

    public TextView customerNameText;
    public Button acceptButton;
    public TextView orderNumberText;
    public OrderContract.UserActions userActions;
    public LinearLayout orderItem;

    public ViewHolder(View itemView) {
        super(itemView);

        userActions = new OrderPresenter(this);

        customerNameText = (TextView) itemView.findViewById(R.id.customer_name);
        acceptButton = (Button) itemView.findViewById(R.id.accept_button);
        orderNumberText = (TextView) itemView.findViewById(R.id.order_number);
        orderItem = (LinearLayout) itemView.findViewById(R.id.order_item_selection);
    }

    @Override
    public void removeItem() {

    }
}
4

3 に答える 3

11

問題は、画面外のアイテムを画面に表示される新しいアイテムにrecyclerView割り当てるリサイクル動作です。上記のすべての回答のように、オブジェクトにViewHolder基づいてロジックをバインドすることはお勧めしません。ViewHolderそれは本当にあなたに問題を引き起こします。ViewHolderいつリサイクルされるかわからないため、オブジェクトではなくデータオブジェクトの状態に基づいてロジックを構築する必要があります。

チェックのために状態ブール isSelectedを保存するとしますViewHolder。ただし、それが true の場合、新しいアイテムviewHolderがリサイクルされるときに同じ状態になります。

上記を行うためのより良い方法は、DataModel オブジェクトで任意の状態を保持することです。あなたの場合、ブール値の isSelected だけです。

のようなサンプル例

package chhimwal.mahendra.multipleviewrecyclerproject;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.support.v7.widget.CardView;
import android.widget.TextView;

import java.util.List;

/**
 * Created by mahendra.chhimwal on 12/10/2015.
 */
public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.ViewHolder> {

    private Context mContext;
    private List<DataModel> mRViewDataList;


    public MyRecyclerViewAdapter(Context context, List<DataModel> rViewDataList) {
        this.mContext = context;
        this.mRViewDataList = rViewDataList;
    }

    @Override
    public MyRecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View view = inflater.inflate(R.layout.item_recycler_view, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        holder.bindDataWithViewHolder(mRViewDataList.get(position));
    }

    @Override
    public int getItemCount() {
        return mRViewDataList != null ? mRViewDataList.size() : 0;
    }


    public class ViewHolder extends RecyclerView.ViewHolder {
        private TextView textView;
        private LinearLayout llView;
        private DataModel mDataItem=null;

        public ViewHolder(View itemView) {
            super(itemView);
            llView=(LinearLayout)itemView.findViewById(R.id.ll_root_view);
            textView = (TextView) itemView.findViewById(R.id.tvItemName);
            cvItemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                  // One should handle onclick of event here based on the dataItem i.e. mDataItem in this case.
                  // something like that..
                /* Intent intent = new Intent(mContext,ResultActivity.class);
                 intent.putExtra("MY_DATA",mDataItem);   //If you want to pass data.
                 intent.putExtra("CLICKED_ITEM_POSTION",getAdapterPosition()); // If one want to get selected item position
                 startActivity(intent);*/
                 Toast.makeText(mContext,"You clicked item number "+ViewHolder.this.getAdapterPosition(),Toast.LENTH_SHORT).show();
                }
            });
        }

        //This is clean method to bind data with viewHolder. Do all dirty things on View based on dataItem.
        //Must be called from onBindViewHolder(),with dataItem. In our case dataItem is String object.
        public void bindDataWithViewHolder(DataModel dataItem){
            this.mDataItem=dataItem;

            if(mDataItem.isSelected()){
                llView.setBackgroundColor(Color.ParseColor(SELCTED_COLOR);
            }else{
                llView.setBackgroundColor(Color.ParseColor(DEFAULT_COLOR);
            }
            //other View binding logics like setting text , loading image  etc.
            textView.setText(mDataItem);
        }
    }
}

@Gabrielがコメントで尋ねたように、

一度に 1 つのアイテムを選択したい場合はどうすればよいでしょうか。

その場合、選択したアイテムの状態をオブジェクトに保存しないでViewHolderください。同じものがリサイクルされて問題が発生するためです。そのより良い方法は、クラスではなくフィールドint selectedItemPositionAdapter持つことViewHolderです。次のコード スニペットはそれを示しています。

public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.ViewHolder> {



        private Context mContext;
        private List<DataModel> mRViewDataList;

        //variable to hold selected Item position
        private int mSelectedItemPosition = -1;


        public MyRecyclerViewAdapter(Context context, List<DataModel> rViewDataList) {
            this.mContext = context;
            this.mRViewDataList = rViewDataList;
        }

        @Override
        public MyRecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            View view = inflater.inflate(R.layout.item_recycler_view, parent, false);
            return new ViewHolder(view);
        }

        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            holder.bindDataWithViewHolder(mRViewDataList.get(position),position);
        }

        @Override
        public int getItemCount() {
            return mRViewDataList != null ? mRViewDataList.size() : 0;
        }


        public class ViewHolder extends RecyclerView.ViewHolder {
            private TextView textView;
            private LinearLayout llView;
            private DataModel mDataItem=null;

            public ViewHolder(View itemView) {
                super(itemView);
                llView=(LinearLayout)itemView.findViewById(R.id.ll_root_view);
                textView = (TextView) itemView.findViewById(R.id.tvItemName);
                cvItemView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        //Handling for background selection state changed
                        int previousSelectState=mSelectedItemPosition;
                        mSelectedItemPosition = getAdapterPosition();
                        //notify previous selected item
                        notifyItemChanged(previousSelectState);
                        //notify new selected Item
                        notifyItemChanged(mSelectedItemPosition);

                        //Your other handling in onclick

                    }
                });
            }

            //This is clean method to bind data with viewHolder. Do all dirty things on View based on dataItem.
            //Must be called from onBindViewHolder(),with dataItem. In our case dataItem is String object.
            public void bindDataWithViewHolder(DataModel dataItem, int currentPosition){
                this.mDataItem=dataItem;
                //Handle selection  state in object View.
                if(currentPosition == mSelectedItemPosition){
                    llView.setBackgroundColor(Color.ParseColor(SELCTED_COLOR);
                }else{
                    llView.setBackgroundColor(Color.ParseColor(DEFAULT_COLOR);
                }
                //other View binding logics like setting text , loading image  etc.
                textView.setText(mDataItem);
            }
        }
    }

選択したアイテムの状態のみを維持する必要がある場合は、Adapter クラスのnotifyDataSetChanged()メソッドの使用を強くお勧めしません。RecyclerView は、これらのケースに対してはるかに柔軟に対応できるからです。

于 2016-02-08T09:37:12.193 に答える
4

ビューではなく、アイテム (オブジェクト) 内の値を割り当てるロジックを変更する必要があります。

orderItem.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
           orderItem.setSelected(xxxx);
        }
    });

次に、onBindViewHolderメソッドで、アイテムのこの値に従って色を割り当てる必要があります。

if (orderItem.isSelected()){
   viewHolder.orderItem.setBackgroundColor(xxxx);
} else {
  viewHolder.orderItem.setBackgroundColor(xxxx);
}
于 2016-02-08T09:19:50.143 に答える
2

これはよくある間違いで、簡単に解決できます。

簡単な答え:onBindViewHolderメソッドに次の行を追加します。

if (orderItem.isSelected()){
    viewHolder.orderItem.setBackgroundColor(Color.parseColor(SELECTED_COLOR));
} else {
    viewHolder.orderItem.setBackgroundColor(Color.parseColor(DEFAULT_COLOR));
}

(DEFAULT_COLORビューホルダーのデフォルトの色)

説明された回答: システムがビューホルダーをリサイクルするとき、メソッドを呼び出すだけonBindViewHolderなので、そのビューホルダーの何かを変更した場合は、それをリセットする必要があります。これは、背景やアイテムの位置などを変更した場合に発生します。コンテンツ自体に関連しない変更は、そのメソッドでリセットする必要があります。

于 2016-02-08T09:20:08.573 に答える