美文网首页
Recycleview在电视端聚焦问题

Recycleview在电视端聚焦问题

作者: 遥遥的远方 | 来源:发表于2018-05-10 15:44 被阅读0次

    需求

    项目需求:在电视端加载视频列表并播放视频,用遥控器操作每一个menu和子项目的ietm。

    问题

      1. recycleview如何在电视端聚焦
      1. recycleview聚焦混乱
      1. 第一个recycleview(菜单栏menu)切换到第二个recycleview(子项Item)
        TV开发和手机开发有个不同的就是焦点问题。
        在手机端,手指只要一划就可以到后面了,而在TV端,需要用遥控器左右键控制焦点移动。

    RecyclerView(简称RV)在TV端的应用可以借鉴的案例不多,相比于ListView,RV的很多功能都需要自己实现。诸如,wrap_content自适应问题,选中的背景问题,setSelection()方法等。

    menu列表

    本项目中的menu采用重写recycleview:
    主要解决Recycleview切换到子项item时,返回记住当前选择的menu。如图所示:


    左侧切换到子项item时,记住当前的menu
    • TvRecyclerView属性介绍 :
            <attr name="scrollMode"/>
            <attr name="focusDrawable" format="reference" />
            <attr name="isAutoProcessFocus" format="boolean" />
            <attr name="focusScale" format="float" />
    

    (1) scrollMode控制TvRecyclerView的滑动模式, TvRecyclerView的滑动模式有两种:

    • ① normalScroll: 默认情况下是这种模式, 当TvRecyclerView是这种模式时, 当焦点滑动到的view是没有全部显示出来的, TvRecyclerView将会向按键的方向滑动屏幕一半的距离.
    • ② followScroll: 当TvRecyclerView是这种模式时, 当焦点滑动到view在屏幕当中就一直滑动, 效果与android Tv上的HorizontalGridView差不多.

    (2) focusDrawable 设置选择的Drawable, 如图一的白色选择框, 默认是没有设置, 想要这种效果需要设置此属性或在代码中设置.

    (3) isAutoProcessFocus 控制焦点处理是由谁来做, 默认焦点由TvRecyclerView来处理.
    当isAutoProcessFocus 为false, 子view是可以获得焦点的, 当isAutoProcessFocus为true, 子view获取
    不到焦点, 焦点由TvRecyclerView来处理.

    (4)focusScale 设置选中view时, view的放大系数. 大于1.0f才生效.

    主要代码如下:

    public class TvRecyclerView extends RecyclerView {
    
        public static final String TAG = "TvRecyclerView";
        private static final float DEFAULT_SELECT_SCALE = 1.04f;
    
        private static final int SCROLL_NORMAL = 0;
        private static final int SCROLL_FOLLOW = 1;
    
        private FocusBorderView mFocusBorderView;
    
        private Drawable mDrawableFocus;
        public boolean mIsDrawFocusMoveAnim;
        private float mSelectedScaleValue;
        private float mFocusMoveAnimScale;
    
        private int mSelectedPosition;
        private View mNextFocused;
        private boolean mInLayout;
    
        private int mFocusFrameLeft;
        private int mFocusFrameTop;
        private int mFocusFrameRight;
        private int mFocusFrameBottom;
    
        private boolean mReceivedInvokeKeyDown;
        protected View mSelectedItem;
        private OnItemStateListener mItemStateListener;
        private Scroller mScrollerFocusMoveAnim;
        private boolean mIsFollowScroll;
    
        private int mScreenWidth;
        private int mScreenHeight;
        private boolean mIsAutoProcessFocus;
        private int mOrientation;
        private boolean mIsSetItemSelected = false;
        private boolean mIsNeedMoveForSelect = false;
    
        public TvRecyclerView(Context context) {
            this(context, null);
        }
    
        public TvRecyclerView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public TvRecyclerView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            init();
            setAttributeSet(attrs);
            // 解决问题: 当需要选择的item没有显示在屏幕上, 需要滑动让item显示出来.
            // 这时需要调整item的位置, 并且item获取焦点
            addOnScrollListener(new OnScrollListener() {
                @Override
                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                    if (mIsNeedMoveForSelect) {
                        mIsNeedMoveForSelect = false;
    
                        int firstVisiblePos = getFirstVisiblePosition();
                        View selectView = getChildAt(mSelectedPosition - firstVisiblePos);
                        if (selectView != null) {
                            mSelectedItem = selectView;
                            adjustSelectOffset(selectView);
                        }
                    }
                }
            });
        }
    
        private void init() {
            mScrollerFocusMoveAnim = new Scroller(getContext());
            mIsDrawFocusMoveAnim = false;
            mReceivedInvokeKeyDown = false;
            mSelectedPosition = 0;
            mNextFocused = null;
            mInLayout = false;
            mIsFollowScroll = false;
            mSelectedScaleValue = DEFAULT_SELECT_SCALE;
            mIsAutoProcessFocus = true;
    
            mFocusFrameLeft = 22;
            mFocusFrameTop = 22;
            mFocusFrameRight = 22;
            mFocusFrameBottom = 22;
            mOrientation = HORIZONTAL;
            mScreenWidth = getContext().getResources().getDisplayMetrics().widthPixels;
            mScreenHeight = getContext().getResources().getDisplayMetrics().heightPixels;
        }
    
        private void setAttributeSet(AttributeSet attrs) {
            if (attrs != null) {
                TypedArray typeArray = getContext().obtainStyledAttributes(attrs, R.styleable.TvRecyclerView);
                int type = typeArray.getInteger(R.styleable.TvRecyclerView_scrollMode, 0);
                if (type == 1) {
                    mIsFollowScroll = true;
                }
    
                final Drawable drawable = typeArray.getDrawable(R.styleable.TvRecyclerView_focusDrawable);
                if (drawable != null) {
                    setFocusDrawable(drawable);
                }
    
                mSelectedScaleValue = typeArray.getFloat(R.styleable.TvRecyclerView_focusScale, DEFAULT_SELECT_SCALE);
                mIsAutoProcessFocus = typeArray.getBoolean(R.styleable.TvRecyclerView_isAutoProcessFocus, true);
                if (!mIsAutoProcessFocus) {
                    mSelectedScaleValue = 1.0f;
                    setChildrenDrawingOrderEnabled(true);
                }
                typeArray.recycle();
            }
            if (mIsAutoProcessFocus) {
                // set TvRecyclerView process Focus
                setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
            }
        }
    
        private void addFlyBorderView(Context context) {
            if (mFocusBorderView == null) {
                mFocusBorderView = new FocusBorderView(context);
                ((Activity) context).getWindow().addContentView(mFocusBorderView,
                        new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
                mFocusBorderView.setSelectPadding(mFocusFrameLeft, mFocusFrameTop,
                        mFocusFrameRight, mFocusFrameBottom);
            }
        }
    
        public int getFirstVisiblePosition() {
            int firstVisiblePos = -1;
            LayoutManager layoutManager = getLayoutManager();
            if (layoutManager != null) {
                if (layoutManager instanceof LinearLayoutManager) {
                    firstVisiblePos = ((LinearLayoutManager) layoutManager)
                            .findFirstVisibleItemPosition();
                } else if (layoutManager instanceof ModuleLayoutManager) {
                    firstVisiblePos = ((ModuleLayoutManager) layoutManager)
                            .findFirstVisibleItemPosition();
                }
            }
            return firstVisiblePos;
        }
    
        public int getLastVisiblePosition() {
            int lastVisiblePos = -1;
            LayoutManager layoutManager = getLayoutManager();
            if (layoutManager != null) {
                if (layoutManager instanceof LinearLayoutManager) {
                    lastVisiblePos = ((LinearLayoutManager) layoutManager)
                            .findLastVisibleItemPosition();
                } else if (layoutManager instanceof ModuleLayoutManager) {
                    lastVisiblePos = ((ModuleLayoutManager) layoutManager)
                            .findLastVisibleItemPosition();
                }
            }
            return lastVisiblePos;
        }
    
        @Override
        public void setLayoutManager(LayoutManager layoutManager) {
            if (layoutManager instanceof LinearLayoutManager) {
                mOrientation = ((LinearLayoutManager) layoutManager).getOrientation();
            } else if (layoutManager instanceof ModuleLayoutManager) {
                mOrientation = ((ModuleLayoutManager) layoutManager).getOrientation();
            }
            Log.i(TAG, "setLayoutManager: ====orientation==" + mOrientation);
            super.setLayoutManager(layoutManager);
        }
    
        /**
         * note: if you set the property of isAutoProcessFocus is false, the listener will be invalid
         *
         * @param listener itemStateListener
         */
        public void setOnItemStateListener(OnItemStateListener listener) {
            mItemStateListener = listener;
        }
    
        public void setSelectedScale(float scale) {
            if (scale >= 1.0f) {
                mSelectedScaleValue = scale;
            }
        }
    
        public void setIsAutoProcessFocus(boolean isAuto) {
            mIsAutoProcessFocus = isAuto;
            if (!isAuto) {
                mSelectedScaleValue = 1.0f;
                setChildrenDrawingOrderEnabled(true);
            } else {
                if (mSelectedScaleValue == 1.0f) {
                    mSelectedScaleValue = DEFAULT_SELECT_SCALE;
                }
            }
        }
    
        public void setFocusDrawable(Drawable focusDrawable) {
            mDrawableFocus = focusDrawable;
        }
    
        public void setScrollMode(int mode) {
            mIsFollowScroll = mode == SCROLL_FOLLOW;
        }
    
        /**
         * When call this method, you must ensure that the location of the view has been inflate
         *
         * @param position selected item position
         */
        public void setItemSelected(int position) {
            if (mSelectedPosition == position) {
                return;
            }
    
            mIsSetItemSelected = true;
            if (position >= getAdapter().getItemCount()) {
                position = getAdapter().getItemCount() - 1;
            }
            mSelectedPosition = position;
            requestLayout();
        }
    
        /**
         * the selected item, there are two cases:
         * 1. item is displayed on the screen
         * 2. item is not displayed on the screen
         */
        private void adjustSelectMode() {
            int childCount = getChildCount();
            if (mSelectedPosition < childCount) {
                mSelectedItem = getChildAt(mSelectedPosition);
                adjustSelectOffset(mSelectedItem);
            } else {
                mIsNeedMoveForSelect = true;
                scrollToPosition(mSelectedPosition);
            }
        }
    
        /**
         * adjust the selected item position to half screen location
         */
        private void adjustSelectOffset(View selectView) {
            if (mIsAutoProcessFocus) {
                scrollOffset(selectView);
            } else {
                scrollOffset(selectView);
                selectView.requestFocus();
            }
            if (mItemStateListener != null) {
                mItemStateListener.onItemViewFocusChanged(true, selectView,
                        mSelectedPosition);
            }
        }
    
        private void scrollOffset(View selectView) {
            int dx;
            if (mOrientation == HORIZONTAL) {
                dx = selectView.getLeft() + selectView.getWidth() / 2 - mScreenWidth / 2;
                scrollBy(dx, 0);
            } else {
                dx = selectView.getTop() + selectView.getHeight() / 2 - mScreenHeight / 2;
                scrollBy(0, dx);
            }
        }
    
        @Override
        public boolean isInTouchMode() {
            boolean result = super.isInTouchMode();
            // 解决4.4版本抢焦点的问题
            if (Build.VERSION.SDK_INT == 19) {
                return !(hasFocus() && !result);
            } else {
                return result;
            }
        }
    
        /**
         * fix issue: not have focus box when change focus
         *
         * @param child   child view
         * @param focused the focused view
         */
        @Override
        public void requestChildFocus(View child, View focused) {
            if (mSelectedPosition < 0) {
                mSelectedPosition = getChildAdapterPosition(focused);
            }
            super.requestChildFocus(child, focused);
            if (mIsAutoProcessFocus) {
                requestFocus();
            }
        }
    
        @Override
        protected void onFinishInflate() {
            super.onFinishInflate();
            if (mIsAutoProcessFocus) {
                addFlyBorderView(getContext());
            }
        }
    
        @Override
        protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
            super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
            if (mItemStateListener != null) {
                if (mSelectedItem == null) {
                    mSelectedItem = getChildAt(mSelectedPosition - getFirstVisiblePosition());
                }
                mItemStateListener.onItemViewFocusChanged(gainFocus, mSelectedItem,
                        mSelectedPosition);
            }
            if (mFocusBorderView == null) {
                return;
            }
            mFocusBorderView.setTvRecyclerView(this);
            if (gainFocus) {
                mFocusBorderView.bringToFront();
            }
            if (mSelectedItem != null) {
                if (gainFocus) {
                    mSelectedItem.setSelected(true);
                } else {
                    mSelectedItem.setSelected(false);
                }
                if (gainFocus && !mInLayout) {
                    mFocusBorderView.startFocusAnim();
                }
            }
            if (!gainFocus) {
                mFocusBorderView.dismissFocus();
            }
        }
    
        @Override
        protected int getChildDrawingOrder(int childCount, int i) {
            int focusIndex = indexOfChild(mSelectedItem);
            if (focusIndex < 0) {
                return i;
            }
            if (i < focusIndex) {
                return i;
            } else if (i < childCount - 1) {
                return focusIndex + childCount - 1 - i;
            } else {
                return focusIndex;
            }
        }
    
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            mInLayout = true;
            super.onLayout(changed, l, t, r, b);
            if (mIsSetItemSelected) {
                adjustSelectMode();
                mIsSetItemSelected = false;
            }
    
            // fix issue: when start anim the FocusView location error in AutoProcessFocus mode
            Adapter adapter = getAdapter();
            if (adapter != null && mSelectedPosition >= adapter.getItemCount()) {
                mSelectedPosition = adapter.getItemCount() - 1;
            }
            mSelectedItem = getChildAt(mSelectedPosition - getFirstVisiblePosition());
            mInLayout = false;
        }
    
        @Override
        protected void dispatchDraw(Canvas canvas) {
            super.dispatchDraw(canvas);
            if (mFocusBorderView != null && mFocusBorderView.getTvRecyclerView() != null) {
                mFocusBorderView.invalidate();
            }
        }
    
        @Override
        protected void onRestoreInstanceState(Parcelable state) {
            Bundle bundle = (Bundle) state;
            Parcelable superData = bundle.getParcelable("super_data");
            super.onRestoreInstanceState(superData);
            setItemSelected(bundle.getInt("select_pos", 0));
        }
    
        @Override
        protected Parcelable onSaveInstanceState() {
            Bundle bundle = new Bundle();
            Parcelable superData = super.onSaveInstanceState();
            bundle.putParcelable("super_data", superData);
            bundle.putInt("select_pos", mSelectedPosition);
            return bundle;
        }
    
        @Override
        public boolean dispatchKeyEvent(KeyEvent event) {
            if (event.getAction() == KeyEvent.ACTION_DOWN) {
                int keyCode = event.getKeyCode();
                if (mSelectedItem == null) {
                    mSelectedItem = getChildAt(mSelectedPosition);
                }
                try {
                    if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
                        mNextFocused = FocusFinder.getInstance().findNextFocus(this, mSelectedItem, View.FOCUS_LEFT);
                    } else if (keyCode == KEYCODE_DPAD_RIGHT) {
                        mNextFocused = FocusFinder.getInstance().findNextFocus(this, mSelectedItem, View.FOCUS_RIGHT);
                    } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
                        mNextFocused = FocusFinder.getInstance().findNextFocus(this, mSelectedItem, View.FOCUS_UP);
                    } else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
                        mNextFocused = FocusFinder.getInstance().findNextFocus(this, mSelectedItem, View.FOCUS_DOWN);
                    }
                } catch (Exception e) {
                    Log.i(TAG, "dispatchKeyEvent: get next focus item error: " + e.getMessage());
                    mNextFocused = null;
                }
    
                if (!mIsAutoProcessFocus) {
                    processMoves(event.getKeyCode());
                    if (mNextFocused != null) {
                        mSelectedItem = mNextFocused;
                    } else {
                        mSelectedItem = getFocusedChild();
                    }
                    mSelectedPosition = getChildAdapterPosition(mSelectedItem);
                }
            }
            return super.dispatchKeyEvent(event);
        }
    
        @Override
        public boolean onKeyDown(int keyCode, KeyEvent event) {
            switch (keyCode) {
                case KeyEvent.KEYCODE_DPAD_LEFT:
                case KeyEvent.KEYCODE_DPAD_UP:
                case KEYCODE_DPAD_RIGHT:
                case KeyEvent.KEYCODE_DPAD_DOWN:
                    if (processMoves(keyCode)) {
                        return true;
                    }
                    break;
                case KeyEvent.KEYCODE_DPAD_CENTER:
                case KeyEvent.KEYCODE_ENTER:
                    mReceivedInvokeKeyDown = true;
                    break;
                default:
                    break;
            }
            return super.onKeyDown(keyCode, event);
        }
    
        @Override
        public boolean onKeyUp(int keyCode, KeyEvent event) {
            switch (keyCode) {
                case KeyEvent.KEYCODE_DPAD_CENTER:
                case KeyEvent.KEYCODE_ENTER: {
                    if (mReceivedInvokeKeyDown) {
                        if ((getAdapter() != null) && (mSelectedItem != null)) {
                            if (mItemStateListener != null) {
                                if (mFocusBorderView != null) {
                                    mFocusBorderView.startClickAnim();
                                }
                                mItemStateListener.onItemViewClick(mSelectedItem,
                                        getChildAdapterPosition(mSelectedItem));
                            }
                        }
                    }
                    mReceivedInvokeKeyDown = false;
                    return true;
                }
            }
            return super.onKeyUp(keyCode, event);
        }
    
        @Override
        public void computeScroll() {
            if (mScrollerFocusMoveAnim.computeScrollOffset()) {
                if (mIsDrawFocusMoveAnim) {
                    mFocusMoveAnimScale = ((float) (mScrollerFocusMoveAnim.getCurrX())) / 100;
                }
                postInvalidate();
            } else {
                if (mIsDrawFocusMoveAnim) {
                    if (mNextFocused != null) {
                        mSelectedItem = mNextFocused;
                        mSelectedPosition = getChildAdapterPosition(mSelectedItem);
                    }
                    mIsDrawFocusMoveAnim = false;
                    setLayerType(View.LAYER_TYPE_SOFTWARE, null);
                    postInvalidate();
                    if (mItemStateListener != null) {
                        mItemStateListener.onItemViewFocusChanged(true, mSelectedItem,
                                mSelectedPosition);
                    }
                }
            }
        }
    
        private boolean processMoves(int keycode) {
            if (mNextFocused == null || !hasFocus()) {
                return false;
            } else {
                if (mIsDrawFocusMoveAnim) {
                    return true;
                }
    
                if (!mIsFollowScroll) {
                    boolean isVisible = isVisibleChild(mNextFocused);
                    boolean isHalfVisible = isHalfVisibleChild(mNextFocused);
                    if (isHalfVisible || !isVisible) {
                        smoothScrollView(keycode);
                    }
                } else {
                    boolean isOver = isOverHalfScreen(mNextFocused, keycode);
                    if (isOver) {
                        smoothScrollView(keycode);
                    }
                }
                if (mIsAutoProcessFocus) {
                    startFocusMoveAnim();
                } else {
                    invalidate();
                }
                return true;
            }
        }
    
        private void smoothScrollView(int keycode) {
            int scrollDistance = getScrollDistance(keycode);
            if ((keycode == KEYCODE_DPAD_RIGHT || keycode == KeyEvent.KEYCODE_DPAD_LEFT)
                    && mOrientation == HORIZONTAL) {
                smoothScrollBy(scrollDistance, 0);
            } else if ((keycode == KeyEvent.KEYCODE_DPAD_UP || keycode == KeyEvent.KEYCODE_DPAD_DOWN)
                    && mOrientation == VERTICAL) {
                smoothScrollBy(0, scrollDistance);
            }
        }
    
        private int getScrollDistance(int keyCode) {
            int distance = 0;
            switch (keyCode) {
                case KeyEvent.KEYCODE_DPAD_RIGHT:
                    distance = mNextFocused.getLeft() +
                            mNextFocused.getWidth() / 2 - mScreenWidth / 2;
                    break;
                case KeyEvent.KEYCODE_DPAD_LEFT:
                    distance = mNextFocused.getLeft()
                            - mScreenWidth / 2 + mNextFocused.getWidth() / 2;
                    break;
                case KeyEvent.KEYCODE_DPAD_UP:
                    distance = mNextFocused.getBottom() -
                            mNextFocused.getHeight() / 2 - mScreenHeight / 2;
                    break;
                case KeyEvent.KEYCODE_DPAD_DOWN:
                    distance = mNextFocused.getTop() +
                            mNextFocused.getHeight() / 2 - mScreenHeight / 2;
                    break;
                default:
                    break;
            }
            return distance;
        }
    
        private boolean isHalfVisibleChild(View child) {
            if (child != null) {
                Rect ret = new Rect();
                boolean isVisible = child.getLocalVisibleRect(ret);
                if (mOrientation == HORIZONTAL) {
                    return isVisible && (ret.width() < child.getWidth());
                } else {
                    return isVisible && (ret.height() < child.getHeight());
                }
            }
            return false;
        }
    
        private boolean isVisibleChild(View child) {
            if (child != null) {
                Rect ret = new Rect();
                return child.getLocalVisibleRect(ret);
            }
            return false;
        }
    
        private boolean isOverHalfScreen(View child, int keycode) {
            Rect ret = new Rect();
            boolean visibleRect = child.getGlobalVisibleRect(ret);
            if (visibleRect && keycode == KEYCODE_DPAD_RIGHT) {
                if (ret.right > mScreenWidth / 2) {
                    return true;
                }
            } else if (visibleRect && keycode == KeyEvent.KEYCODE_DPAD_LEFT) {
                if (ret.left < mScreenWidth / 2) {
                    return true;
                }
            } else if (visibleRect && keycode == KeyEvent.KEYCODE_DPAD_UP) {
                if (ret.top < mScreenHeight / 2) {
                    return true;
                }
            } else if (visibleRect && keycode == KeyEvent.KEYCODE_DPAD_DOWN) {
                if (ret.bottom > mScreenHeight / 2) {
                    return true;
                }
            }
            return false;
        }
    
        private void startFocusMoveAnim() {
            setLayerType(View.LAYER_TYPE_NONE, null);
            mIsDrawFocusMoveAnim = true;
            if (mItemStateListener != null) {
                mItemStateListener.onItemViewFocusChanged(false, mSelectedItem,
                        mSelectedPosition);
            }
            mScrollerFocusMoveAnim.startScroll(0, 0, 100, 100, 200);
            invalidate();
        }
    
        /**
         * When the TvRecyclerView width is determined, the returned position is correct
         *
         * @return selected view position
         */
        public int getSelectedPosition() {
            return mSelectedPosition;
        }
    
        View getSelectedView() {
            return mSelectedItem;
        }
    
        public float getSelectedScaleValue() {
            return mSelectedScaleValue;
        }
    
        public Drawable getDrawableFocus() {
            return mDrawableFocus;
        }
    
        public View getNextFocusView() {
            return mNextFocused;
        }
    
        public float getFocusMoveAnimScale() {
            return mFocusMoveAnimScale;
        }
    
        public void setSelectPadding(int left, int top, int right, int bottom) {
            mFocusFrameLeft = left;
            mFocusFrameTop = top;
            mFocusFrameRight = right;
            mFocusFrameBottom = bottom;
    
            if (mFocusBorderView != null) {
                mFocusBorderView.setSelectPadding(mFocusFrameLeft, mFocusFrameTop,
                        mFocusFrameRight, mFocusFrameBottom);
            }
        }
    
        public interface OnItemStateListener {
            void onItemViewClick(View view, int position);
    
            void onItemViewFocusChanged(boolean gainFocus, View view, int position);
        }
    }
    

    xml.文件如下:

        <RecyclerViewEx.TvRecyclerView
                    android:id="@+id/title_list"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:divider="@null"
                    android:fastScrollEnabled="false"
                    android:footerDividersEnabled="false"
                    android:headerDividersEnabled="false"
                    app:focusDrawable="@drawable/bg_item_focused" />
    

    注: app:focusDrawable="@drawable/bg_item_focused" 主要是当前menu中的item聚焦的效果。

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
    
        <solid android:color="@color/bg_btn"></solid>
    
        <stroke
            android:width="1dp"
            android:color="@color/darkgray" />
    
        <corners android:radius="@dimen/x1" />
    
    </shape>
    

    问题 三

    本项目中的右侧item采用原生的recycleview,

    image.png
    项目中最大的问题是左侧menu按下遥控器右键切换到右侧子项item时,(如果当前右侧的数据源不低于4个)会默认选择第二列的第一个数据源,如图标注所示:
    image.png
    项目需求: 当menu切换的时候,按下遥控器右键,切换到右侧子item的时候默认选择第一列的第一个数据源。
    解决方案:在xml文件布局中,recycleview的外边套着一层布局,让当前布局可聚焦,具体如下:
           <LinearLayout
                android:id="@+id/ll_content"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="8"
                android:background="@color/white"
                android:focusable="true"
                android:orientation="vertical"
                android:padding="@dimen/x2">
    
                <android.support.v7.widget.RecyclerView
                    android:id="@+id/content_list"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:divider="@null"
                    android:fastScrollEnabled="false"
                    android:focusable="false"
                    android:footerDividersEnabled="false"
                    android:headerDividersEnabled="false" />
    
            </LinearLayout>
    

    设置外层的LinearLayout的android:focusable="true",RecyclerView的android:focusable="false"
    最后在acticity的设置LinearLayout的聚焦监听器:

     mLayoutContent.setOnFocusChangeListener(new View.OnFocusChangeListener() {
                @Override
                public void onFocusChange(View v, boolean hasFocus) {
                    if (hasFocus) {
                        if (mListViewContent.getChildCount() > 0) {
                            mListViewContent.getChildAt(0).requestFocus();
                        }
                    }
                }
            });
    

    mListViewContent为RecycleView。

    最后每次切换menu的时候,自动让右侧的items滚动到最上面,附上方法:

    mListViewMenu.setOnItemStateListener(new TvRecyclerView.OnItemStateListener() {
                @Override
                public void onItemViewClick(View view, int position) {
    //                mAdapterMenu.setSelectPosition(position);
    //                mAdapterContent.setData(mVideoBeans.get(position).getList(), false);
                }
    
                @Override
                public void onItemViewFocusChanged(boolean gainFocus, View view, int position) {
                    if (position < 0) return;
                    if (gainFocus) {
                        mAdapterMenu.setSelectPosition(position);
    
                        //为右侧recycleview设置数据源
                        mAdapterContent.setData(mVideoBeans.get(position).getList(), false);
                        //
                        if (mCurPosition != position) {
                            if (mListViewContent.getAdapter().getItemCount() > 0) {
                                //设置右侧的recycleview滚动到最上面
                                mListViewContent.scrollToPosition(0);
                                mCurPosition = position;
                            }
                        }
                    }
                }
            });
    

    mListViewMenu为重写的TvRecycleView。

    相关文章

      网友评论

          本文标题:Recycleview在电视端聚焦问题

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