一、判斷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,上面有使用中的效果圖