ListSelector for RecyclerView on a touch device (using onKey Listener)

I am trying to implement a list selector for my RecyclerView grid on a touch device. My implementation works fine, but it requires a notifyItemChanged() method, which is inefficient. I have a grid with 100 elements, so if I scroll quickly (by scrolling the keyboard, therefore, onKey), the grid is distorted, as many elements are updated. Is there any way to avoid this?

activity

 mRecyclerView.setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager(); if (event.getAction() == KeyEvent.ACTION_DOWN) { switch (keyCode) { case Constants.KEYCODE_UP: return moveSelection(lm, -1, true); case Constants.KEYCODE_DOWN: return moveSelection(lm, 1, true); } } return false; } }); public boolean moveSelection(RecyclerView.LayoutManager lm, int direction, boolean verticalMovement) { ... //just calculate the position to move to and pass it to selectedPosition return mAdapter.tryMoveSelection(lm, selectedPosition); } 

Adapter

  @Override public void onBindViewHolder(ViewHolder holder, int position) { // - get element from your dataset at this position // - replace the contents of the view with that element mViewHolder = holder; holder.itemView.setSelected(mFocusedItem == position); ... } public boolean tryMoveSelection(RecyclerView.LayoutManager lm, int selectedPosition) { notifyItemChanged(mFocusedItem); mFocusedItem = selectedPosition; notifyItemChanged(mFocusedItem); lm.scrollToPosition(mFocusedItem); return true; } 
+5
source share
4 answers

I found a much better solution that does not include notifyItemChanged() . What I am doing is expanding the RecyclerView class and creating two new methods:

  • clearSelection(int position)
  • setSelection(int position)

and name them in tryMoveSelection as follows:

 boolean tryMoveSelection (RecyclerView.LayoutManager lm, int selectedPosition) { myRecyclerView.clearSelection(selectedPosition); mFocusedItem = selectedPosition; myRecyclerView.setSelection(mFocusedItem); lm.scrollToPosition(mFocusedItem); mRecyclerView.requestFocus(); return true; } 

Details:

 public void setSelection(int position) { ViewHolder viewHolder = this.findViewHolderForAdapterPosition(position); if (this.getChildCount() > 0 && viewHolder != null) { viewHolder.itemView.setSelected(true); viewHolder.itemView.requestFocus(); } } public void clearSelection(int position) { ViewHolder viewHolder = this.findViewHolderForAdapterPosition(position); if (viewHolder != null) { viewHolder.itemView.setSelected(false); viewHolder.itemView.clearFocus(); viewHolder.itemView.setBackgroundResource(0); } } 
+1
source

You can add several optimizations to the code:

+1
source

You must use the "payload" when you call " notifyItemChanged ". Payloads have been designed specifically for these cases: when you only need to perform a partial update and not complete the binding.

So, for example, you can notify your adapter: notifyItemChanged(1, "focusedItemChange") , and then do something like this in your adapter:

 @Override public void onBindViewHolder(VH holder, int position, List<Object> payloads) { if (payloads.isEmpty()) { // Do a full bind } else if (payloads.contains("focusedItemChange")) { holder.itemView.setSelected(isFocusedPositon(position)); } } 
0
source

I do this on the DPAD button, scrolling through the Recycler view click here show demo

0
source

All Articles