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