Focus Memory Encapsulation of Android RecyclerView
- 2021-08-31 09:15:09
- OfStack
The list focus implementation in TV development was introduced in the previous article
android tv list focus memory implementation, is to use external code control way to achieve, more cumbersome, now introduce the use of custom RecyclerView way to achieve, and added other functions: limit vertical and horizontal out of focus, moving in and out of focus event monitoring, etc.
The code is implemented as follows:
import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
public class FocusKeepRecyclerView extends RecyclerView {
private static final String TAG = FocusKeepRecyclerView.class.getSimpleName();
// Can I move out vertically
private boolean mCanFocusOutVertical = true;
// Can I move out laterally
private boolean mCanFocusOutHorizontal = true;
// Focus shift out recyclerview Event listening of
private FocusLostListener mFocusLostListener;
// Focus shift in recyclerview Event listening of
private FocusGainListener mFocusGainListener;
// Default number 1 The second selected number 1 Position
private int mCurrentFocusPosition = 0;
public FocusKeepRecyclerView(Context context) {
this(context, null);
}
public FocusKeepRecyclerView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public FocusKeepRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
setChildrenDrawingOrderEnabled(true);
setItemAnimator(null);
this.setFocusable(true);
}
public boolean isCanFocusOutVertical() {
return mCanFocusOutVertical;
}
public void setCanFocusOutVertical(boolean canFocusOutVertical) {
mCanFocusOutVertical = canFocusOutVertical;
}
public boolean isCanFocusOutHorizontal() {
return mCanFocusOutHorizontal;
}
public void setCanFocusOutHorizontal(boolean canFocusOutHorizontal) {
mCanFocusOutHorizontal = canFocusOutHorizontal;
}
@Override
public View focusSearch(int direction) {
return super.focusSearch(direction);
}
// Overwrite focusSearch Focus finding strategy
@Override
public View focusSearch(View focused, int direction) {
Log.i(TAG, "focusSearch " + focused + ",direction= " + direction);
View view = super.focusSearch(focused, direction);
if (focused == null) {
return view;
}
if (view != null) {
// This method returns the focus view Where the father is view, If it is in recyclerview Outside, it will be null. So according to whether it is null, To judge whether it was moved out recyclerview
View nextFocusItemView = findContainingItemView(view);
if (nextFocusItemView == null) {
if (!mCanFocusOutVertical && (direction == View.FOCUS_DOWN || direction == View.FOCUS_UP)) {
// Shielding focus moves out longitudinally recyclerview
return focused;
}
if (!mCanFocusOutHorizontal && (direction == View.FOCUS_LEFT || direction == View.FOCUS_RIGHT)) {
// Transverse shift of shielding focus recyclerview
return focused;
}
// Invoke removed listening
if (mFocusLostListener != null) {
mFocusLostListener.onFocusLost(focused, direction);
}
return view;
}
}
return view;
}
public void setFocusLostListener(FocusLostListener focusLostListener) {
this.mFocusLostListener = focusLostListener;
}
public interface FocusLostListener {
void onFocusLost(View lastFocusChild, int direction);
}
public void setGainFocusListener(FocusGainListener focusListener) {
this.mFocusGainListener = focusListener;
}
public interface FocusGainListener {
void onFocusGain(View child, View focued);
}
@Override
public void requestChildFocus(View child, View focused) {
Log.i(TAG, "nextchild= " + child + ",focused = " + focused);
if (!hasFocus()) {
//recyclerview Sub view Recover the focus and call the event listening that moved into the focus
if (mFocusGainListener != null) {
mFocusGainListener.onFocusGain(child, focused);
}
}
super.requestChildFocus(child, focused);// Executed super.requestChildFocus After hasFocus Will become true
mCurrentFocusPosition = getChildViewHolder(child).getAdapterPosition();
Log.i(TAG,"focusPos = "+mCurrentFocusPosition);
}
// Key codes to realize focus memory
@Override
public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
View view = null;
if (this.hasFocus() || mCurrentFocusPosition < 0 || (view = getLayoutManager().findViewByPosition(mCurrentFocusPosition)) == null) {
super.addFocusables(views,direction,focusableMode);
}else if(view.isFocusable()){
// Will the current view Put Focusable views List, when you move into focus again, you will get the view, Realize the focus memory function
views.add(view);
}else{
super.addFocusables(views,direction,focusableMode);
}
}
/**
* Control the final drawing of the current focus to prevent the focus from being blocked after being enlarged
* Original sequence 123456789 , when 4 Yes focus The drawing order becomes 123567894
* @param childCount
* @param i
* @return
*/
@Override
protected int getChildDrawingOrder(int childCount, int i) {
View focusedChild = getFocusedChild();
Log.i(TAG,"focusedChild ="+focusedChild);
if(focusedChild== null){
return super.getChildDrawingOrder(childCount, i);
}else{
int index = indexOfChild(focusedChild);
Log.i(TAG, " index = " + index + ",i=" + i + ",count=" + childCount);
if(i == childCount-1){
return index;
}
if(i<index){
return i;
}
return i+1;
}
}
}
Code implementation and comments are described above.
Can be directly used as an recyclerview, already has the focus memory function, do not need to add additional code in the outer layer; To increase the ability to limit vertical and horizontal out-of-focus, in-out-of-focus event listening, you can call the above setXXXListener and other methods.