stud91 stud91 - 2 months ago 12
Android Question

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

I am trying to implement a list selector for my

RecyclerView
grid on a touch free device. My implementation works fine but it requires
notifyItemChanged()
method which is not efficient performance wise. I have a grid with 100s of items so if i am scrolling fast (scrolling with a keyboard hence the onKey) the grid becomes distorted as lots of items are being updated. Is there a 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;
}

Answer

I found a much better solution that doesn't involve notifyItemChanged() at all. What I do is extend the RecyclerView class and create two new methods:

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

and call 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 as follows:

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