美文网首页Android Other进阶android
RecyclerView的焦点记忆封装

RecyclerView的焦点记忆封装

作者: AndroidXing | 来源:发表于2018-04-06 12:32 被阅读404次

    上一篇中介绍了TV开发中的列表焦点实现
    android tv列表焦点记忆实现,是用外部代码控制的方式实现的,比较繁琐,现在介绍用自定义RecyclerView的方式来实现,并增加了其他的功能:限制纵向和横向移出焦点,移入移出焦点的事件监听等。

    代码实现如下:

    
    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();
        //是否可以纵向移出
        private boolean mCanFocusOutVertical = true;
        //是否可以横向移出
        private boolean mCanFocusOutHorizontal = true;
        //焦点移出recyclerview的事件监听
        private FocusLostListener mFocusLostListener;
        //焦点移入recyclerview的事件监听
        private FocusGainListener mFocusGainListener;
        //默认第一次选中第一个位置
        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);
        }
    
       //覆写focusSearch寻焦策略
        @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) {
        //该方法返回焦点view所在的父view,如果是在recyclerview之外,就会是null.所以根据是否是null,来判断是否是移出了recyclerview
                View nextFocusItemView = findContainingItemView(view);
                if (nextFocusItemView == null) {
                    if (!mCanFocusOutVertical && (direction == View.FOCUS_DOWN || direction == View.FOCUS_UP)) {
                        //屏蔽焦点纵向移出recyclerview
                        return focused;
                    }
                    if (!mCanFocusOutHorizontal && (direction == View.FOCUS_LEFT || direction == View.FOCUS_RIGHT)) {
                        //屏蔽焦点横向移出recyclerview
                        return focused;
                    }
                  //调用移出的监听
                    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 子view 重新获取焦点,调用移入焦点的事件监听
                if (mFocusGainListener != null) {
                    mFocusGainListener.onFocusGain(child, focused);
                }
            }
            super.requestChildFocus(child, focused);//执行过super.requestChildFocus之后hasFocus会变成true
            mCurrentFocusPosition = getChildViewHolder(child).getAdapterPosition();
            Log.i(TAG,"focusPos = "+mCurrentFocusPosition);
        }
    
     //实现焦点记忆的关键代码
        @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()){
    //将当前的view放到Focusable views列表中,再次移入焦点时会取到该view,实现焦点记忆功能
                views.add(view);
            }else{
                super.addFocusables(views,direction,focusableMode);
            }
        }
    
       
        /**
         * 控制当前焦点最后绘制,防止焦点放大后被遮挡
         * 原顺序123456789,当4是focus时,绘制顺序变为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;
            }
        }
    
    }
    

    代码实现和注释说明如上。
    可以直接作为一个recyclerview使用,已经具有了焦点记忆的功能了,不需要在外层增加额外的代码;要增加限制纵向和横向移出焦点,移入移出焦点的事件监听的功能,可以再调用上面的setXXXListener等方法。

    相关文章

      网友评论

      本文标题:RecyclerView的焦点记忆封装

      本文链接:https://www.haomeiwen.com/subject/cpujhftx.html