2016-02-08 36 views
7

私はRecyclerViewを使用して作成したアイテムのリストを持っています。ユーザーがそれらの1つをクリックすると、その選択された項目の背景色が変更されます。 問題は、アイテムをスクロールしてリサイクルすると、アイテムの一部が選択されたアイテムの背景色(間違っている)を取得することです。あなたのロジックが項目(オブジェクト)ではないビュー内の値を代入変更する必要がありRecyclerViewはリサイクル時に問題を引き起こします

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() { 

    } 
} 
+0

あなたはその設定された背景色に基づいて選択された項目を維持する必要があります –

答えて

7

問題はViewHolder項目に、画面のあなたのアウトを割り当てるrecyclerViewリサイクル行動であるにリセットする必要があります自体をコンテンツに関連されていないすべての変更を変更する場合に発生します新しいアイテムが画面に表示されるようになりました。 上記のすべての回答と同様に、ViewHolderオブジェクトに基づいてロジックをバインドすることはお勧めしません。それは本当にあなたに問題を引き起こすでしょう。 データオブジェクトの状態に基づいてロジックを構築する必要があります。ViewHolderオブジェクトは、リサイクルされるタイミングを決して知らないため、使用しないでください。

あなたは 状態ブール値をチェックするためにViewHolderをisSelectedが、それが本当であるならば、このviewHolderがリサイクルされますと、その後、同じ状態が新たな項目のために存在します保存したとします。

上記の操作を行うと、DataModelオブジェクトにany状態が保持されます。あなたの場合はブール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オブジェクトに保存しないでください。同じオブジェクトがリサイクルされ、問題が発生します。その良い方法のためには、のAdapterクラスにはViewHolderのフィールドがありません。 次のコードスニペットを表示してください。あなたが唯一の選択した項目の状態を維持する必要がある場合RecyclerViewは、これらのケースのためのより多くの柔軟性を提供して

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); 
      } 
     } 
    } 

、私は強くnotifyDataSetChanged()アダプタクラスの方法の使用を思いとどまら。

+0

私はあなたの答えを正しいものとしてマークしました、私はそれを完全に考えなかったのです。しかし、これに問題があります。これは、mDataItem.isSelected()がtrueであるかどうかをチェックするif文で問題があります。選択された項目を選択すると、いつもそうです。しかし、別のアイテムが選択されると、それを選択解除する必要があります! – Gabriel

+0

@Gabrielナビゲーションのような1つのアイテムを1つだけ選択したい場合は、引き出しが一般的に別のケースです。質問は一度に1つの選択の要件を要求しません。あなたは非常に優雅にそれを扱うことができます。私の更新された答えを見てください。 –

+0

@Gabrielがあなたの問題を解決したかどうか教えてください。 –

1

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

次に、あなたが持っているあなたのonBindViewHolder方法でASSINGにここでは、私のアダプタのコードを見ることができます 項目のこの値に応じた色。

if (orderItem.isSelected()){ 
    viewHolder.orderItem.setBackgroundColor(xxxx); 
} else { 
    viewHolder.orderItem.setBackgroundColor(xxxx); 
} 
+0

私はあなたの答えにプラス1を与えました、私はそれを完全に考えていませんでした。しかし、これに問題があります。これは、mDataItem.isSelected()がtrueであるかどうかをチェックするif文で問題があります。選択された項目を選択すると、いつもそうです。しかし、別のアイテムが選択されると、それを選択解除する必要があります! – Gabriel

+0

@ Gabrielスクロール時に問題が発生する理由を説明します。もちろん、すべてのケースを解決することはできません。アイテムの選択を解除する必要がある場合は、clickListenerのログインを変更するだけです。他のアイテムを無効にするか、単に選択したアイテムのID(位置ではなく)を保存します。 –

1

これは簡単な解決策を持つ非常によくある間違いです。

クイック答え:あなたのonBindViewHolder方法で次の行を追加します。

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

を(viewholderがデフォルトで持っているDEFAULT_COLOR色で)

は答えをの説明:システムは、それだけで呼び出すviewholderをリサイクルするときonBindViewHolderメソッドですので、そのビューホルダーの何かを変更した場合は、それをリセットする必要があります。あなたは、背景、アイテムの位置などその方法

関連する問題