RecyclerView在TV使用中碰到的問題和解決方案

一、判斷recyclerview是否滑到最後一行

在使用種如果界面上要顯示一個標識,來告知用戶已經滑到了最後一行,那麼可以通過推recyclerview的滑動監聽,來進行設置:

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                if (dy == 0) {
                    initMoreVisibility();
                }
                if (mLayoutManager != null) {
                    int totalItemCount = mLayoutManager.getItemCount();
                    int lastVisibleItem = mLayoutManager.findLastCompletelyVisibleItemPosition();
                    if (dy > 0) {
                        int visibleThreshold = 1;
                        if (totalItemCount <= (lastVisibleItem + visibleThreshold)) {
                            more.setVisibility(View.INVISIBLE);
                        } else {
                            more.setVisibility(View.VISIBLE);
                        }
                    } else if (dy < 0) {
                        int visibleThreshold = 0;
                        if (totalItemCount <= (lastVisibleItem + visibleThreshold)) {
                            more.setVisibility(View.INVISIBLE);
                        } else {
                            more.setVisibility(View.VISIBLE);
                        }
                    }
                }
            }
        });

二、在TV中使用recyclerview,快速按遙控(長按),焦點跑飛的的問題

在長按或者快速按遙控器是,recyclerview的焦點跑飛。主要原因是在滑動的過程中,下一個要獲取焦點的view,還處於繪製渲染階段(PFLAG_INVALIDATED,PFLAG_DIRTY_MASK),這個階段的view是無法獲取焦點的,所以可以通過重寫recyclerview中的dispatchKeyEvent ,在這邊進行對按鍵進行過濾,取消一些view還處於不能獲取焦點的按鍵事件。(在這邊也一併進行了邊界的判斷

 protected boolean isBorder(KeyEvent event) {
        int focusDirection = event.getKeyCode();
        View view = this.getFocusedChild();
        LayoutManager layoutManager = this.getLayoutManager();
        int focusPos = layoutManager.getPosition(view);
        if (layoutManager instanceof GridLayoutManager) {
            GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
            int spanCount = gridLayoutManager.getSpanCount();
            int itemCount = layoutManager.getItemCount();
            int rowCount;
            int row;
            int span;
            if (isHeader && itemCount != 1) {
                rowCount = (int) (Math.ceil((double) (itemCount - 1) / spanCount) + 1);
                if (focusPos != 0) {
                    row = (focusPos - 1) / spanCount + 2;
                } else {
                    row = (focusPos - 1) / spanCount + 1;
                }
                span = (focusPos - 1) % spanCount + 1;
            } else {
                rowCount = (int) Math.ceil((double) itemCount / spanCount);
                row = focusPos / spanCount + 1;
                span = focusPos % spanCount + 1;
            }
            if (event.hasNoModifiers()) {
                switch (focusDirection) {
                    case KeyEvent.KEYCODE_DPAD_DOWN:
                        if (row == rowCount) {
                            return borderListener.onKeyBottomDown();
                        } else {
                            //處理長按焦點錯誤問題;
                            View nextView = view.focusSearch(View.FOCUS_DOWN);
                            if (nextView != null) {
                                //過慮還沒繪製完成的view和正在修改的view
                                if (!nextView.willNotDraw() || nextView.isDirty()) {
                                    return true;
                                }
                                // 這個根據佈局的需求進行調整,用於解決長按遙控器,焦點跑掉的問題
                                if (nextView.getTop() > ScreenUtil.dip2px(getContext(), 680)) {
                                    return true;
                                }
                            }
                        }
                        break;
                    case KeyEvent.KEYCODE_DPAD_UP:
                        if (row == 1) {
                            return borderListener.onKeyTopUp();
                        } else if (row == 2 && headerIsNull) {
                            return borderListener.onKeyTopUp();
                        } else {
                            //處理長按焦點錯誤問題
                            View nextView = view.focusSearch(View.FOCUS_UP);
                            if (nextView != null) {
                                if (!nextView.willNotDraw()) {
                                    return true;
                                }
                            }
                        }
                        break;
                    case KeyEvent.KEYCODE_DPAD_RIGHT:
                        if (span == spanCount) {
                            return borderListener.onKeyRightEnd();
                        }
                        break;
                    case KeyEvent.KEYCODE_DPAD_LEFT:
                        if (span == 1) {
                            return borderListener.onKeyLeftEnd();
                        }
                        break;
                }
            }
        }

        return super.dispatchKeyEvent(event);
    }

三、當界面使用activity + fragment+viewpager 多個界面切換時,焦點的處理

recyclerview在這種情況時,界面切換獲取的焦點是recyclerview緩存中的最後一個view,而一般我們希望的是最靠近左邊或右邊的View.
左邊滑到recyclerview:

int childCount = mRecyclerView.getChildCount();
        int row = (int) (Math.ceil((double) (childCount - 1) / mLayoutManager.getSpanCount()) + 1);
        int position = mRecyclerView.getChildViewHolder(mRecyclerView.getChildAt(0)).getLayoutPosition();
        int cvPosition = mLayoutManager.findLastCompletelyVisibleItemPosition();
        int vPosition = mLayoutManager.findLastVisibleItemPosition();
        int effect = (cvPosition == vPosition ? 0 : 1);
        row = row - effect;
        if (mRecyclerView.isHeaderIsNull()) {
            int focusPosition;
            if (row <= 2) {
                focusPosition = 0;
            } else {
                int effect2 = position == 0 ? 1 : 2;
                focusPosition = effect2 * mLayoutManager.getSpanCount();
            }
            if (position == 0) {
                mRecyclerView.getChildAt(1).requestFocus();
            } else {
                mRecyclerView.getChildAt(focusPosition - 6).requestFocus();
            }

        } else {
            if (row <= 2) {
                header.positionRequestFocus(0);
            } else {
                int effect2 = position == 0 ? 1 : 2;
                int focusPosition = effect2 * mLayoutManager.getSpanCount();
                if (position == 0) {
                    mRecyclerView.getChildAt(1).requestFocus();
                } else {
                    mRecyclerView.getChildAt(focusPosition - 6).requestFocus();
                }
            }
        }

右邊滑到recyclerview

int childCount = mRecyclerView.getChildCount();
        int row = (int) (Math.ceil((double) (childCount - 1) / mLayoutManager.getSpanCount()) + 1);
        int position = mRecyclerView.getChildViewHolder(mRecyclerView.getChildAt(0)).getLayoutPosition();
        int cvPosition = mLayoutManager.findLastCompletelyVisibleItemPosition();
        int vPosition = mLayoutManager.findLastVisibleItemPosition();
        int effect = (cvPosition == vPosition ? 0 : 1);
        row = row - effect;
        if (mRecyclerView.isHeaderIsNull()) {
            int focusPosition;
            if (row <= 2) {
                focusPosition = childCount <= 7 ? (childCount - 1) : 6;
            } else {
                int effect2 = position == 0 ? 1 : 2;
                focusPosition = effect2 * mLayoutManager.getSpanCount();
            }
            if (position == 0) {
                mRecyclerView.getChildAt(focusPosition).requestFocus();
            } else {
                mRecyclerView.getChildAt(focusPosition - 1).requestFocus();
            }

        } else {
            if (row <= 2) {
                header.positionRequestFocus(5);
            } else {
                int effect2 = position == 0 ? 1 : 2;
                int focusPosition = effect2 * mLayoutManager.getSpanCount();
                if (position == 0) {
                    mRecyclerView.getChildAt(focusPosition).requestFocus();
                } else {
                    mRecyclerView.getChildAt(focusPosition - 1).requestFocus();
                }
            }
        }

控制recyclerview滑動的距離
有時候需要在recyclerview中添加不需要獲取焦點的view用於對內容的說明,那麼就需要在滑動的過程中多滑動一些,把這個描述內容和顯示內容一起顯示在屏幕中,可以通過重寫Manager中的scrollVerticallyBy或者scrollHorizontallyBy (根據滑動方向):

 @Override
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        double speedRatio;
        if (dy > 0) {
            speedRatio = speedDownRatio;
        } else {
            speedRatio = speedUpRatio;
        }
        int a = super.scrollVerticallyBy((int) (speedRatio * dy), recycler, state);
        if (a == (int) (speedRatio * dy)) {
            return dy;
        }
        return a;

    }

這邊附上一個demo,上面有使用中的效果圖

RecyclerViewTvdemo

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章