recyclerview 的 scrollstate 有三种 0 -> idle 1 -> dragging 2 -> setting recyclerview 的原始滚动状态变化是 1.拖动松手: 0 -> 1 -> 0 2.滑动松手: 0 -> 1 -> 2 -> 0 要处理 recyclerview 嵌套 recyclerview 的整屏滚动并且关联页码的更新,采取操作:松手时执行 smoothScrollToPosition 方法 即在 onScrollStateChanged 方法中监听: A. 0 -> 1 说明用户的手触碰到了屏幕,执行以下计算 int position = ((LinearLayoutManager)rv.getLayoutManager()).findFirstVisibleItemPosition(); preX = rv.getChildAt(0).getX() - position * SCREEN_WIDTH; -> rv 滑动的初始值 B. 1 -> !1 说明用户松手了,执行以下计算 int position = ((LinearLayoutManager)rv.getLayoutManager()).findFirstVisibleItemPosition(); float x = rv.getChildAt(0).getX() - position * SCREEN_WIDTH; -> rv 滑动的当前值 float scrolledX = x - preX; -> 此次滑动距离 int newPosition = -10; -> newPosition 代表应该把屏幕定位到哪一页 if(scrolledX < 0){ -> 方向向右 if(scrolledX <= 0 - scrollDistance2NextPage){-> scrollDistance2NextPage 滑动多远后即视为要进入下一页 newPosition = position + 1; }else { newPosition = position; } }else if(scrolledX > 0){ -> 方向向左 if(scrolledX < scrollDistance2NextPage){ newPosition = position + 1; }else { newPosition = position; } } if(newPosition != -10){ rv.smoothScrollToPosition(newPosition); } if(preState != 0 && newState == 0){ -> 滑动结束后更新页码 updatePageIndex(newPosition); } 执行A和B两步即可实现整屏滚动。 然而 updatePageIndex 的操作会在滑动结束后再次触发滑动(原因待查),因此改成以下模式(计算出要滑动到哪一页时就把页码更新) if(newPosition != -10){ rv.smoothScrollToPosition(newPosition); updatePageIndex(newPosition); -> 更新页码 } 至此整屏滑动的触摸滑动解决。然而还有遥控器的整屏滑动。 给 内层 recyclerview 的 item 设置 onkeylistener,onKey 逻辑如下 int innerItemPosition = (int) v.getTag(); -> 内层 recyclerview item 的 tag 是它在父 recyclerview 的 position if(event.getKeyCode() == KeyEvent.KEYCODE_PAGE_UP){ prePage(innerItemPosition); return true; }else if(event.getKeyCode() == KeyEvent.KEYCODE_PAGE_DOWN){ nextPage(innerItemPosition); return true; }else if(event.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT){ boolean atLeft = innerItemPosition % SceneInfo.COL == 0; if(atLeft){ int rowIndex = innerItemPosition / SceneInfo.COL; prePage(rowIndex * SceneInfo.COL + SceneInfo.COL - 1); return true; }else if(event.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT){ boolean atRight = (innerItemPosition + 1) % SceneInfo.COL == 0; if(atRight){ int rowIndex = innerItemPosition / SceneInfo.COL; nextPage(rowIndex * SceneInfo.COL); return true; } 然后再 prePage 和 nextPage 方法中执行如下逻辑,即可实现遥控器的整屏滑动 rv.smoothScrollToPosition(position + 1); updatePageIndex(position + 1); 再次然而,使用遥控器按以上逻辑翻页后,下一页的焦点不尽如人意,还需要专门调整一下焦点:在 onScrollStateChanged 方法中执行如下逻辑 if(changingPageByKey && state == 2 && newState == 0){-> 遥控器翻页结束时 changingPageByKey = false; requestFocusAfterChangePage(); -> 在此方法中为目标item请求焦点 } 这里有个很严重的问题是:smoothScrollToPosition 结束后, recyclerview.getChildAt(0) 与 ((LinearLayoutManager)recyclerview.getAdapter()).findFirstVisibleItemPosition()代表的子 recyclerview 不是同一个!!! 所以在 requestFocusAfterChangePage 方法中这样查找子 recyclerview : int aimPosition = ((LinearLayoutManager)rv.getLayoutManager()).findFirstVisibleItemPosition(); int childCount = rv.getChildCount(); RecyclerView itemRv = null; for (int i = 0; i < childCount; i++) { Object obj = rv.getChildAt(i).getTag(); -> 外层 recyclerview 的 adapter 给 子recyclerview绑定的tag if(obj instanceof Integer){ if(aimPosition == (int)obj){ itemRv = (RecyclerView) rv.getChildAt(i); } } }
完整代码如下:
@BindView(R.id.rv)
RecyclerView rv;
@BindView(R.id.pageIndexTv)
TextView pageIndexTv;
private long lastKeyDownTime = 0;
private final long keyGapTime = 100;//间隔100毫秒响应按键
private boolean changingPageByKey = false;
private int nextItemPosition = 0;
private final int scrollDistance2NextPage = 50;
public void updatePageIndex(int page) {
curPage = page;
int totalPage = getTotalPage();
if(totalPage <= 0){
pageIndexTv.setVisibility(View.GONE);
}else {
pageIndexTv.setVisibility(View.VISIBLE);
CommonUtil.loadPageIndexText(pageIndexTv, curPage + 1, totalPage);
}
}
@Override
protected void initEventAndData() {
curClass = getArguments().getInt("curClass");
manage = getArguments().getBoolean("manage");
refreshData();
initRv();
updatePageIndex(0);
}
private int getTotalPage(){
int totalPage = (rawDataHolder.size() - 1) / SceneInfo.PAGE_SIZE + 1;
if(rawDataHolder.size() == 0){
totalPage = 0;
}
return totalPage;
}
private void refreshData(){
...
}
private void initRv() {
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
rv.setLayoutManager(layoutManager);
...
rv.setAdapter(new CrossRvAdapter(getContext(), rawDataHolder, manage, new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if(event.getAction() == KeyEvent.ACTION_DOWN){
long now = System.currentTimeMillis();
if(now - lastKeyDownTime < keyGapTime){
return true;
}
lastKeyDownTime = now;
int innerItemPosition = (int) v.getTag();
if(event.getKeyCode() == KeyEvent.KEYCODE_PAGE_UP){
prePage(innerItemPosition);
return true;
}else if(event.getKeyCode() == KeyEvent.KEYCODE_PAGE_DOWN){
nextPage(innerItemPosition);
return true;
}else if(event.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT){
boolean atLeft = innerItemPosition % SceneInfo.COL == 0;
if(atLeft){
int rowIndex = innerItemPosition / SceneInfo.COL;
prePage(rowIndex * SceneInfo.COL + SceneInfo.COL - 1);
return true;
}
}else if(event.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT){
boolean atRight = (innerItemPosition + 1) % SceneInfo.COL == 0;
if(atRight){
int rowIndex = innerItemPosition / SceneInfo.COL;
nextPage(rowIndex * SceneInfo.COL);
return true;
}
}
}
return false;
}
}));
rv.addOnScrollListener( new CrossRvScrollListener());
}
private void prePage(int nextInnerItemPosition){
if(changingPageByKey){//处于遥控器翻页状态中时不处理
return;
}
int position = ((LinearLayoutManager)rv.getLayoutManager()).findFirstVisibleItemPosition();
if(position == 0){
return;
}
changingPageByKey = true;
nextItemPosition = nextInnerItemPosition;
rv.smoothScrollToPosition(position - 1);
updatePageIndex(position - 1);
}
private void nextPage(int nextInnerItemPosition){
if(changingPageByKey){//处于遥控器翻页状态中时不处理
return;
}
int position = ((LinearLayoutManager)rv.getLayoutManager()).findFirstVisibleItemPosition();
if(position == rv.getAdapter().getItemCount() - 1){
return;
}
changingPageByKey = true;
nextItemPosition = nextInnerItemPosition;
rv.smoothScrollToPosition(position + 1);
updatePageIndex(position + 1);
}
private void requestFocusAfterChangePage(){
int nextInnerItemPosition = nextItemPosition;
if(rv == null || rv.getChildCount() == 0){
return;
}
int aimPosition = ((LinearLayoutManager)rv.getLayoutManager()).findFirstVisibleItemPosition();
int childCount = rv.getChildCount();
RecyclerView itemRv = null;
for (int i = 0; i < childCount; i++) {
Object obj = rv.getChildAt(i).getTag();
if(obj instanceof Integer){
if(aimPosition == (int)obj){
itemRv = (RecyclerView) rv.getChildAt(i);
}
}
}
if(itemRv == null){
return;
}
int itemRvChild = itemRv.getChildCount();
if(itemRvChild == 0){
return;
}
if(itemRvChild > nextInnerItemPosition){
itemRv.getChildAt(nextInnerItemPosition).requestFocus();
}else{
itemRv.getChildAt(itemRvChild - 1).requestFocus();
}
}
class CrossRvScrollListener extends RecyclerView.OnScrollListener {
int preScrollState = 0;
float preX = 0;
final int SCREEN_WIDTH = 1280;
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
int state = preScrollState;
preScrollState = newState;
if(newState == RecyclerView.SCROLL_STATE_DRAGGING){
changingPageByKey = false;
}
if(state == RecyclerView.SCROLL_STATE_IDLE && newState == RecyclerView.SCROLL_STATE_DRAGGING){
int position = ((LinearLayoutManager)rv.getLayoutManager()).findFirstVisibleItemPosition();
preX = rv.getChildAt(0).getX() - position * SCREEN_WIDTH;
}else if(state == RecyclerView.SCROLL_STATE_DRAGGING && newState != RecyclerView.SCROLL_STATE_DRAGGING){
int position = ((LinearLayoutManager)rv.getLayoutManager()).findFirstVisibleItemPosition();
float x = rv.getChildAt(0).getX() - position * SCREEN_WIDTH;
float scrolledX = x - preX;
int newPosition = -10;
if(scrolledX < 0){//向右
if(scrolledX <= 0 - scrollDistance2NextPage){
newPosition = position + 1;
}else {
newPosition = position;
}
}if(scrolledX > 0){//向左
if(scrolledX < scrollDistance2NextPage){
newPosition = position + 1;
}else {
newPosition = position;
}
}
if(newPosition != -10){
rv.smoothScrollToPosition(newPosition);
updatePageIndex(newPosition);
}
}
if(changingPageByKey && state == RecyclerView.SCROLL_STATE_SETTLING && newState == RecyclerView.SCROLL_STATE_IDLE){
changingPageByKey = false;
requestFocusAfterChangePage();
}
}
}