Skip to content
Fredlxy edited this page Feb 17, 2017 · 6 revisions

这是一个在TV端使用RecyclerView的一个Demo,焦点效果是通过设置背景的方式实现的,非常简单,体验也非常好。

下面总结下在TV端使用使用RecyclerView时踩过的坑:

(一)解决RecyclerView刷新数据的时候,焦点错乱问题

(1) 允许adapter设置item id;

     setHasStableIds(true)

(2)adapter中复写getItemId方法;

      @Override
    public long getItemId(int position) {
        return position;
    }

(3)RecyclerView不允许item使用动画;

   setItemAnimator(null);

(二)解决长按遥控器,让RecyclerView快速滑动,焦点错乱问题

   (1)重写RecyclerView的dispatchKeyEvent方法,屏蔽RecyclerView的onKeyUp事件;
   (2)通过FocusFind类找到view,并让view请求焦点;
   (3)focusFind焦点失败之后,如果是让其滑动item的宽度(左后滑动)或者高度(上下滑动),然后return true;
 @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        if (mInterceptLister != null && mInterceptLister.onIntercept(event)) {
            return true;
        }
        boolean result = super.dispatchKeyEvent(event);
        View focusView = this.getFocusedChild();
        if (focusView == null) {
            return result;
        } else {
            if (event.getAction() == KeyEvent.ACTION_UP) {
                return true;
            } else {
                switch (event.getKeyCode()) {
                    case KeyEvent.KEYCODE_DPAD_RIGHT:
                        View rightView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_RIGHT);
                        mLogger.i("rightView is null:" + (rightView == null));
                        if (rightView != null) {
                            rightView.requestFocus();
                            return true;
                        } else {
                            return true;
                        }
                    case KeyEvent.KEYCODE_DPAD_LEFT:
                        View leftView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_LEFT);
                        mLogger.i("leftView is null:" + (leftView == null));
                        if (leftView != null) {
                            leftView.requestFocus();
                            return true;
                        } else {
                            return true;
                        }
                    case KeyEvent.KEYCODE_DPAD_DOWN:
                        View downView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_DOWN);
                        mLogger.i(" downView is null:" + (downView == null));
                        if (downView != null) {
                            downView.requestFocus();
                            return true;
                        } else {
                            return true;
                        }
                    case KeyEvent.KEYCODE_DPAD_UP:
                        View upView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_UP);
                        mLogger.i("upView is null:" + (upView == null));
                        if (event.getAction() == KeyEvent.ACTION_UP) {
                            return true;
                        } else {
                            if (upView != null) {
                                upView.requestFocus();
                                return true;
                            } else {
                                return true;
                            }
                        }
                }
            }
        }
        return result;
    }

(三)解决RecyclerView 定位到某个item不获取焦点问题

     RecyclerView定位到特定的child后,让其该view请求焦点

public class TvGridLayoutManager extends GridLayoutManager {

    public TvGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }
    public TvGridLayoutManager(Context context, int spanCount) {
        super(context, spanCount);
    }
    public TvGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
        super(context, spanCount, orientation, reverseLayout);
    }
    /**
     * Base class which scrolls to selected view in onStop().
     */
    abstract class GridLinearSmoothScroller extends LinearSmoothScroller {
        public GridLinearSmoothScroller(Context context) {
            super(context);
        }
        /**
         * 滑动完成后,让该targetPosition 处的item获取焦点
         */
        @Override
        protected void onStop() {
            super.onStop();
            View targetView = findViewByPosition(getTargetPosition());
            if (targetView != null) {
                targetView.requestFocus();
            }
            super.onStop();
        }
    }
    /**
     * RecyclerView的smoothScrollToPosition方法最终会执行smoothScrollToPosition
     * @param recyclerView
     * @param state
     * @param position
     */
    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
                                       int position) {
        GridLinearSmoothScroller linearSmoothScroller =
                new GridLinearSmoothScroller(recyclerView.getContext()) {
                    @Override
                    public PointF computeScrollVectorForPosition(int targetPosition) {
                        return computeVectorForPosition(targetPosition);
                    }
                };
        linearSmoothScroller.setTargetPosition(position);
        startSmoothScroll(linearSmoothScroller);
    }
    public PointF computeVectorForPosition(int targetPosition) {
        return super.computeScrollVectorForPosition(targetPosition);
    }

}

Clone this wiki locally