Facing issue in setOnCheckedChangeListener position value in RecyclerView Android

早过忘川 提交于 2019-12-12 04:29:29

问题


I am using RecyclerView which supports dragging and dropping. My list item has a row with two TextView, CheckBox, ImageView for dragging rows.

I am using Helper classes from below link: https://github.com/iPaulPro/Android-ItemTouchHelper-Demo/tree/master/app/src/main/java/co/paulburke/android/itemtouchhelperdemo/helper

Values of arrTaxStatus are correct when I drag the rows.

Values of arrTaxStatus are correct when I enable/disable the checkboxes.

But I face issues when I enable/disable few checkboxes, drag few rows and then again enable/disable few checkboxes. The position gets disturbed in setOnCheckedChangeListener. Even though I am clicking on first row, I get a different 'position'

Please tell me how can I get the correct position of the clicked checkbox.

Code below:

public class RecyclerListFragment extends AppCompatActivity implements OnStartDragListener {
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.create_tax_group);

    arrTaxStatus = new ArrayList<Boolean>();
    HashMap<String, Object> temp;
    taxAccounts = new ArrayList<HashMap<String, Object>>();
    Cursor cursor = dh.getDetailsFromTaxGivenTaxCategory();
    if (cursor.moveToFirst()) {
        do {
            temp = new HashMap<String, Object>();
            temp.put("taxName", cursor.getString(cursor.getColumnIndex("scheme_name")));
            temp.put("taxPercent", numberFormat.format(cursor.getDouble(cursor.getColumnIndex("percentage"))));
            taxAccounts.add(temp);
            arrTaxStatus.add(false);
        } while (cursor.moveToNext());
    }
    cursor.close();

    RecyclerListAdapter adapter = new RecyclerListAdapter(this, taxAccounts);

    RecyclerView recyclerView = (RecyclerView) findViewById(android.R.id.list);
    recyclerView.setHasFixedSize(true);
    recyclerView.setAdapter(adapter);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));

    ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(adapter);
    mItemTouchHelper = new ItemTouchHelper(callback);
    mItemTouchHelper.attachToRecyclerView(recyclerView);
}

@Override
public void onStartDrag(RecyclerView.ViewHolder viewHolder) {
    mItemTouchHelper.startDrag(viewHolder);
}

public class RecyclerListAdapter extends RecyclerView.Adapter<RecyclerListAdapter.ItemViewHolder>
        implements ItemTouchHelperAdapter {
    private final OnStartDragListener mDragStartListener;

    public RecyclerListAdapter(OnStartDragListener dragStartListener, ArrayList<HashMap<String, Object>> taxAccounts) {
        mDragStartListener = dragStartListener;
    }

    @Override
    public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.create_tax_group_row, parent, false);
        ItemViewHolder itemViewHolder = new ItemViewHolder(view);
        return itemViewHolder;
    }

    @Override
    public void onBindViewHolder(final ItemViewHolder holder, final int position) {
        holder.taxName.setText(taxAccounts.get(position).get("taxName").toString());
        holder.taxPercent.setText(taxAccounts.get(position).get("taxPercent").toString());
        holder.handleView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
                    mDragStartListener.onStartDrag(holder);
                }
                return false;
            }
        });

        holder.taxStatus.setOnCheckedChangeListener(null);
        holder.taxStatus.setChecked(arrTaxStatus.get(position));
        holder.taxStatus.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                Log.i("arrTaxStatus pos|checked", position + "|" + isChecked);
                arrTaxStatus.set(position, isChecked);
                Log.i("arrTaxStatus onCheckedChanged", arrTaxStatus.toString());
            }
        });
    }

    @Override
    public void onItemDismiss(int position) {
        taxAccounts.remove(position);
        arrTaxStatus.remove(position);
        notifyItemRemoved(position);
    }

    @Override
    public boolean onItemMove(int fromPosition, int toPosition) {
        Collections.swap(taxAccounts, fromPosition, toPosition);
        Collections.swap(arrTaxStatus, fromPosition, toPosition);
        Log.i("arrTaxStatus onItemMove", arrTaxStatus.toString());
        notifyItemMoved(fromPosition, toPosition);
        return true;
    }

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

    /**
     * Simple example of a view holder that implements {@link ItemTouchHelperViewHolder} and has a
     * "handle" view that initiates a drag event when touched.
     */
    public class ItemViewHolder extends RecyclerView.ViewHolder implements
            ItemTouchHelperViewHolder {

        public final TextView taxName, taxPercent;
        public final CheckBox taxStatus;
        public final ImageView handleView;

        public ItemViewHolder(View itemView) {
            super(itemView);
            taxName = (TextView) itemView.findViewById(R.id.childname);
            taxPercent = (TextView) itemView.findViewById(R.id.balance);
            taxStatus = (CheckBox) itemView.findViewById(R.id.check_status);
            handleView = (ImageView) itemView.findViewById(R.id.handle);
        }

        @Override
        public void onItemSelected() {
            itemView.setBackgroundColor(Color.LTGRAY);
        }

        @Override
        public void onItemClear() {
            itemView.setBackgroundColor(0);
        }
    }
}
}

回答1:


You need to create a variable in your data model e.g isChecked or isEnabled. Then when you enable/disable them. Just update your array which you used to populate list. Then onBindViewHoder check the variable value and enable/disable check boxes.

Concept is that rows are recycled once you move up/down. So checkboxes does not retain the values. So you need to retain values in array. And populate them afterwards.




回答2:


instead of getting position from onbindviewholder set a tag to your layout which holds position of row, use that tag value when you change onchangelistner




回答3:


You can use the notifyItemChanged(int position) method from the RecyclerView.Adapter class. From the documentation:

Notify any registered observers that the item at position has changed. Equivalent to calling notifyItemChanged(position, null);.

This is an item change event, not a structural change event. It indicates that any reflection of the data at position is out of date and should be updated. The item at position retains the same identity. As you already have the position, it should work for you.




回答4:


You need to maintain a flag as follows in your adapter :

private boolean onViewHolderBind;

public ViewHolder(View itemView) {
    super(itemView);
    mCheckBox = (CheckBox) itemView.findViewById(R.id.checkboxId);
    mCheckBox.setOnCheckChangeListener(this);
}

@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    if(!onViewHolderBind) {
        // perform your process when checkbox changed
        notifyDataSetChanged();
    }
}

...

@Override
public void onBindViewHolder(YourAdapter.ViewHolder viewHolder, int position) {
    // process other views 
    // ...

    onViewHolderBind = true;
    viewHolder.mCheckBox.setChecked(trueOrFalse);
    onViewHolderBind = false;
}



回答5:


I think the problem you are suffering is that the items controlled internally by the adapter are not in sync with your arrays, the problem being that Collections.swap(…) doesn't do what the adapters perform graphically. A swap() will exchange the position of two items in an array, but the items inside the range remain in position. However, when a user drags an item from the last position to the first position, all the item positions inside the range do change!

I started using this code myself and fixed it by replacing the swap() call with the following code (diff):

      override fun onItemMove(fromPosition: Int, toPosition: Int): Boolean {
 -        Collections.swap(items, fromPosition, toPosition)
 +        val temp = items[fromPosition]
 +        items.removeAt(fromPosition)
 +        items.add(toPosition, temp)
          notifyItemMoved(fromPosition, toPosition)
          return true
      }

To illustrate visually the bug, I added a dump() call which would show the contents of the Adapter after each drag interaction, then I created an adapter with seven elements, conveniently placed in alphabetical order:

Adapter: did load 7
Adapter: Dumping 7 items
Adapter: …0 SharingItem(uri=content://FIRST, kind=Image)
Adapter: …1 SharingItem(uri=content://B, kind=Image)
Adapter: …2 SharingItem(uri=content://C, kind=Image)
Adapter: …3 SharingItem(uri=content://D, kind=Audio)
Adapter: …4 SharingItem(uri=content://E, kind=Audio)
Adapter: …5 SharingItem(uri=content://F, kind=Video)
Adapter: …6 SharingItem(uri=content://LAST, kind=Audio)

Displaying these items in a 2x4 grid, I dragged the last item to the first position. As I dragged the finger up, the item would take place in different parts of the collection during the drag movement, which are highlighted by the dumps for each interaction received by the adapter:

Adapter: Move from 6 to 4
Adapter: Dumping 7 items
Adapter: …0 SharingItem(uri=content://FIRST, kind=Image)
Adapter: …1 SharingItem(uri=content://B, kind=Image)
Adapter: …2 SharingItem(uri=content://C, kind=Image)
Adapter: …3 SharingItem(uri=content://D, kind=Audio)
Adapter: …4 SharingItem(uri=content://LAST, kind=Audio)
Adapter: …5 SharingItem(uri=content://F, kind=Video)
Adapter: …6 SharingItem(uri=content://E, kind=Audio)
Adapter: Move from 4 to 2
Adapter: Dumping 7 items
Adapter: …0 SharingItem(uri=content://FIRST, kind=Image)
Adapter: …1 SharingItem(uri=content://B, kind=Image)
Adapter: …2 SharingItem(uri=content://LAST, kind=Audio)
Adapter: …3 SharingItem(uri=content://D, kind=Audio)
Adapter: …4 SharingItem(uri=content://C, kind=Image)
Adapter: …5 SharingItem(uri=content://F, kind=Video)
Adapter: …6 SharingItem(uri=content://E, kind=Audio)
Adapter: Move from 2 to 0
Adapter: Dumping 7 items
Adapter: …0 SharingItem(uri=content://LAST, kind=Audio)
Adapter: …1 SharingItem(uri=content://B, kind=Image)
Adapter: …2 SharingItem(uri=content://FIRST, kind=Image)
Adapter: …3 SharingItem(uri=content://D, kind=Audio)
Adapter: …4 SharingItem(uri=content://C, kind=Image)
Adapter: …5 SharingItem(uri=content://F, kind=Video)
Adapter: …6 SharingItem(uri=content://E, kind=Audio)

After replacing the swap() call with the implementation above, the logs showed a much better picture of the internals during interaction:

Adapter: did load 7
Adapter: Dumping 7 items
Adapter: …0 SharingItem(uri=content://FIRST, kind=Image)
Adapter: …1 SharingItem(uri=content://B, kind=Image)
Adapter: …2 SharingItem(uri=content://C, kind=Image)
Adapter: …3 SharingItem(uri=content://D, kind=Audio)
Adapter: …4 SharingItem(uri=content://E, kind=Audio)
Adapter: …5 SharingItem(uri=content://F, kind=Video)
Adapter: …6 SharingItem(uri=content://LAST, kind=Audio)
Adapter: Move from 6 to 4
Adapter: Dumping 7 items
Adapter: …0 SharingItem(uri=content://FIRST, kind=Image)
Adapter: …1 SharingItem(uri=content://B, kind=Image)
Adapter: …2 SharingItem(uri=content://C, kind=Image)
Adapter: …3 SharingItem(uri=content://D, kind=Audio)
Adapter: …4 SharingItem(uri=content://LAST, kind=Audio)
Adapter: …5 SharingItem(uri=content://E, kind=Audio)
Adapter: …6 SharingItem(uri=content://F, kind=Video)
Adapter: Move from 4 to 2
Adapter: Dumping 7 items
Adapter: …0 SharingItem(uri=content://FIRST, kind=Image)
Adapter: …1 SharingItem(uri=content://B, kind=Image)
Adapter: …2 SharingItem(uri=content://LAST, kind=Audio)
Adapter: …3 SharingItem(uri=content://C, kind=Image)
Adapter: …4 SharingItem(uri=content://D, kind=Audio)
Adapter: …5 SharingItem(uri=content://E, kind=Audio)
Adapter: …6 SharingItem(uri=content://F, kind=Video)
Adapter: Move from 2 to 0
Adapter: Dumping 7 items
Adapter: …0 SharingItem(uri=content://LAST, kind=Audio)
Adapter: …1 SharingItem(uri=content://FIRST, kind=Image)
Adapter: …2 SharingItem(uri=content://B, kind=Image)
Adapter: …3 SharingItem(uri=content://C, kind=Image)
Adapter: …4 SharingItem(uri=content://D, kind=Audio)
Adapter: …5 SharingItem(uri=content://E, kind=Audio)
Adapter: …6 SharingItem(uri=content://F, kind=Video)


来源:https://stackoverflow.com/questions/43337554/facing-issue-in-setoncheckedchangelistener-position-value-in-recyclerview-androi

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!