Android RecyclerView水平滚动管理单元位于中间 我试图用ReaveReVIEW制作一个类似旋转木马的视图,我希望这个项目在屏幕滚动时一个一个条目滚动。我尝试过使用recyclerView.setScrollingtouchlop(recyclerView.TOUCH\u SLOP\u分页)
但视图仍在平滑滚动,我还尝试使用scroll listener实现我自己的逻辑,如下所示:Android RecyclerView水平滚动管理单元位于中间 我试图用ReaveReVIEW制作一个类似旋转木马的视图,我希望这个项目在屏幕滚动时一个一个条目滚动。我尝试过使用recyclerView.setScrollingtouchlop(recyclerView.TOUCH\u SLOP\u分页),android,horizontal-scrolling,android-recyclerview,Android,Horizontal Scrolling,Android Recyclerview,但视图仍在平滑滚动,我还尝试使用scroll listener实现我自己的逻辑,如下所示: recyclerView.setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.on
recyclerView.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
Log.v("Offset ", recyclerView.getWidth() + "");
if (newState == 0) {
try {
recyclerView.smoothScrollToPosition(layoutManager.findLastVisibleItemPosition());
recyclerView.scrollBy(20,0);
if (layoutManager.findLastVisibleItemPosition() >= recyclerView.getAdapter().getItemCount() - 1) {
Beam refresh = new Beam();
refresh.execute(createUrl());
}
} catch (Exception e) {
e.printStackTrace();
}
}
从右向左的滑动现在可以正常工作了,但不是相反,我在这里缺少了什么?您需要使用findFirstVisibleItemPosition来反向移动。要想检测出扫射的方向,你需要得到投掷速度或者x的变化。我从一个与你略有不同的角度来处理这个问题 创建一个扩展RecyclerView类的新类,然后重写RecyclerView的fling方法,如下所示:
@Override
public boolean fling(int velocityX, int velocityY) {
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) getLayoutManager();
//these four variables identify the views you see on screen.
int lastVisibleView = linearLayoutManager.findLastVisibleItemPosition();
int firstVisibleView = linearLayoutManager.findFirstVisibleItemPosition();
View firstView = linearLayoutManager.findViewByPosition(firstVisibleView);
View lastView = linearLayoutManager.findViewByPosition(lastVisibleView);
//these variables get the distance you need to scroll in order to center your views.
//my views have variable sizes, so I need to calculate side margins separately.
//note the subtle difference in how right and left margins are calculated, as well as
//the resulting scroll distances.
int leftMargin = (screenWidth - lastView.getWidth()) / 2;
int rightMargin = (screenWidth - firstView.getWidth()) / 2 + firstView.getWidth();
int leftEdge = lastView.getLeft();
int rightEdge = firstView.getRight();
int scrollDistanceLeft = leftEdge - leftMargin;
int scrollDistanceRight = rightMargin - rightEdge;
//if(user swipes to the left)
if(velocityX > 0) smoothScrollBy(scrollDistanceLeft, 0);
else smoothScrollBy(-scrollDistanceRight, 0);
return true;
}
我的解决方案:
/**
* Horizontal linear layout manager whose smoothScrollToPosition() centers
* on the target item
*/
class ItemLayoutManager extends LinearLayoutManager {
private int centeredItemOffset;
public ItemLayoutManager(Context context) {
super(context, LinearLayoutManager.HORIZONTAL, false);
}
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
LinearSmoothScroller linearSmoothScroller = new Scroller(recyclerView.getContext());
linearSmoothScroller.setTargetPosition(position);
startSmoothScroll(linearSmoothScroller);
}
public void setCenteredItemOffset(int centeredItemOffset) {
this.centeredItemOffset = centeredItemOffset;
}
/**
* ********** Inner Classes **********
*/
private class Scroller extends LinearSmoothScroller {
public Scroller(Context context) {
super(context);
}
@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
return ItemLayoutManager.this.computeScrollVectorForPosition(targetPosition);
}
@Override
public int calculateDxToMakeVisible(View view, int snapPreference) {
return super.calculateDxToMakeVisible(view, SNAP_TO_START) + centeredItemOffset;
}
}
}
我将此布局管理器传递给RecycledView,并设置项目居中所需的偏移量。所有我的项目都具有相同的宽度,因此恒定偏移量是可以的Google I/O更新
在这里
谷歌在“安卓系统的新功能”(又名“安卓基调”)演讲中表示,他们正在开发基于RecyclerView的新ViewPager
从幻灯片中:
像ViewPager,但更好
- 从ViewPager轻松迁移
- 基于RecyclerView
- 从右到左模式支持
- 允许垂直分页
- 改进的数据集更改通知
PagerSnapHelper
时遇到了很多问题-请参阅我已打开
新答案(2016) 你现在只需要使用一个 如果希望中心对齐的捕捉行为类似于,请使用: 还有一个问题。我已经试过了,如果你用能量投掷,那么它会用1次投掷滚动2个项目。就我个人而言,我不喜欢它,但你自己决定吧——尝试它只需要几秒钟
原始答复(2016年) 经过几个小时的尝试,在中找到了3种不同的解决方案,因此我最终构建了一个与
ViewPager
中的行为非常相似的解决方案
这个解决方案是基于@eDizzle的,我相信我已经做了足够的改进,可以说它的工作原理几乎像一个ViewPager
重要提示:我的回收视图
项目宽度与屏幕完全相同。我没有试过其他尺寸的。我还将其与水平线性布局管理器一起使用。我认为,如果需要垂直滚动,则需要调整代码。
这里有代码:
public class SnappyRecyclerView extends RecyclerView {
// Use it with a horizontal LinearLayoutManager
// Based on https://stackoverflow.com/a/29171652/4034572
public SnappyRecyclerView(Context context) {
super(context);
}
public SnappyRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public SnappyRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean fling(int velocityX, int velocityY) {
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) getLayoutManager();
int screenWidth = Resources.getSystem().getDisplayMetrics().widthPixels;
// views on the screen
int lastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition();
View lastView = linearLayoutManager.findViewByPosition(lastVisibleItemPosition);
int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
View firstView = linearLayoutManager.findViewByPosition(firstVisibleItemPosition);
// distance we need to scroll
int leftMargin = (screenWidth - lastView.getWidth()) / 2;
int rightMargin = (screenWidth - firstView.getWidth()) / 2 + firstView.getWidth();
int leftEdge = lastView.getLeft();
int rightEdge = firstView.getRight();
int scrollDistanceLeft = leftEdge - leftMargin;
int scrollDistanceRight = rightMargin - rightEdge;
if (Math.abs(velocityX) < 1000) {
// The fling is slow -> stay at the current page if we are less than half through,
// or go to the next page if more than half through
if (leftEdge > screenWidth / 2) {
// go to next page
smoothScrollBy(-scrollDistanceRight, 0);
} else if (rightEdge < screenWidth / 2) {
// go to next page
smoothScrollBy(scrollDistanceLeft, 0);
} else {
// stay at current page
if (velocityX > 0) {
smoothScrollBy(-scrollDistanceRight, 0);
} else {
smoothScrollBy(scrollDistanceLeft, 0);
}
}
return true;
} else {
// The fling is fast -> go to next page
if (velocityX > 0) {
smoothScrollBy(scrollDistanceLeft, 0);
} else {
smoothScrollBy(-scrollDistanceRight, 0);
}
return true;
}
}
@Override
public void onScrollStateChanged(int state) {
super.onScrollStateChanged(state);
// If you tap on the phone while the RecyclerView is scrolling it will stop in the middle.
// This code fixes this. This code is not strictly necessary but it improves the behaviour.
if (state == SCROLL_STATE_IDLE) {
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) getLayoutManager();
int screenWidth = Resources.getSystem().getDisplayMetrics().widthPixels;
// views on the screen
int lastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition();
View lastView = linearLayoutManager.findViewByPosition(lastVisibleItemPosition);
int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
View firstView = linearLayoutManager.findViewByPosition(firstVisibleItemPosition);
// distance we need to scroll
int leftMargin = (screenWidth - lastView.getWidth()) / 2;
int rightMargin = (screenWidth - firstView.getWidth()) / 2 + firstView.getWidth();
int leftEdge = lastView.getLeft();
int rightEdge = firstView.getRight();
int scrollDistanceLeft = leftEdge - leftMargin;
int scrollDistanceRight = rightMargin - rightEdge;
if (leftEdge > screenWidth / 2) {
smoothScrollBy(-scrollDistanceRight, 0);
} else if (rightEdge < screenWidth / 2) {
smoothScrollBy(scrollDistanceLeft, 0);
}
}
}
}
公共类SnappyRecyclerView扩展了RecyclerView{
//将其与水平线性布局管理器一起使用
//基于https://stackoverflow.com/a/29171652/4034572
公共SnappyRecyclerView(上下文){
超级(上下文);
}
公共SnappyRecyclerView(上下文上下文,@Nullable AttributeSet attrs){
超级(上下文,attrs);
}
公共SnappyRecyclerView(上下文上下文,@Nullable AttributeSet attrs,int-defStyle){
超级(上下文、属性、定义样式);
}
@凌驾
公共布尔触发器(int-velocityX,int-velocityY){
LinearLayoutManager LinearLayoutManager=(LinearLayoutManager)getLayoutManager();
int screenWidth=Resources.getSystem().getDisplayMetrics().widthPixels;
//屏幕上的视图
int lastVisibleItemPosition=linearLayoutManager.findLastVisibleItemPosition();
View lastView=linearLayoutManager.findViewByPosition(lastVisibleItemPosition);
int firstVisibleItemPosition=linearLayoutManager.findFirstVisibleItemPosition();
View firstView=linearLayoutManager.findViewByPosition(firstVisibleItemPosition);
//我们需要滚动的距离
int leftMargin=(屏幕宽度-lastView.getWidth())/2;
int rightMargin=(屏幕宽度-firstView.getWidth())/2+firstView.getWidth();
int leftEdge=lastView.getLeft();
int rightEdge=firstView.getRight();
int scrollDistanceLeft=leftEdge-leftMargin;
int scrollDistanceRight=右边距-右边距;
if(数学绝对值(速度x)<1000){
//掷骰速度慢->如果未完成一半,请停留在当前页面,
//如果超过一半,请转到下一页
如果(左边缘>屏幕宽度/2){
//转到下一页
smoothScrollBy(-scrollDistanceRight,0);
}否则如果(右边缘<屏幕宽度/2){
//转到下一页
smoothScrollBy(scrollDistanceLeft,0);
}否则{
//停留在当前页面
如果(速度x>0){
smoothScrollBy(-scrollDistanceRight,0);
}否则{
smoothScrollBy(scrollDistanceLeft,0);
}
}
返回true;
}否则{
//快速投掷->进入下一页
如果(速度x>0){
smoothScrollBy(scrollDistanceLeft,0);
}否则{
smoothScrollBy(-scrollDistanceRight,0);
}
返回true;
}
}
@凌驾
CrollStateChanged上的公共无效(int状态){
super.onScrollStateChanged(state);
如果你在手机上滚动,而ReaveVIEW正在滚动,它会停在中间。
//此代码修复了此问题。此代码不是严格必需的,但它改进了行为。
如果(状态==滚动\状态\空闲){
LinearLayoutManager LinearLayoutManager=(LinearLayoutManager)getLayoutManager();
int screenWidth=Resources.getSystem().getDisplayMetrics().widthPixels;
//屏幕上的视图
最后一个整数
public class SnappyRecyclerView extends RecyclerView {
// Use it with a horizontal LinearLayoutManager
// Based on https://stackoverflow.com/a/29171652/4034572
public SnappyRecyclerView(Context context) {
super(context);
}
public SnappyRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public SnappyRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean fling(int velocityX, int velocityY) {
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) getLayoutManager();
int screenWidth = Resources.getSystem().getDisplayMetrics().widthPixels;
// views on the screen
int lastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition();
View lastView = linearLayoutManager.findViewByPosition(lastVisibleItemPosition);
int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
View firstView = linearLayoutManager.findViewByPosition(firstVisibleItemPosition);
// distance we need to scroll
int leftMargin = (screenWidth - lastView.getWidth()) / 2;
int rightMargin = (screenWidth - firstView.getWidth()) / 2 + firstView.getWidth();
int leftEdge = lastView.getLeft();
int rightEdge = firstView.getRight();
int scrollDistanceLeft = leftEdge - leftMargin;
int scrollDistanceRight = rightMargin - rightEdge;
if (Math.abs(velocityX) < 1000) {
// The fling is slow -> stay at the current page if we are less than half through,
// or go to the next page if more than half through
if (leftEdge > screenWidth / 2) {
// go to next page
smoothScrollBy(-scrollDistanceRight, 0);
} else if (rightEdge < screenWidth / 2) {
// go to next page
smoothScrollBy(scrollDistanceLeft, 0);
} else {
// stay at current page
if (velocityX > 0) {
smoothScrollBy(-scrollDistanceRight, 0);
} else {
smoothScrollBy(scrollDistanceLeft, 0);
}
}
return true;
} else {
// The fling is fast -> go to next page
if (velocityX > 0) {
smoothScrollBy(scrollDistanceLeft, 0);
} else {
smoothScrollBy(-scrollDistanceRight, 0);
}
return true;
}
}
@Override
public void onScrollStateChanged(int state) {
super.onScrollStateChanged(state);
// If you tap on the phone while the RecyclerView is scrolling it will stop in the middle.
// This code fixes this. This code is not strictly necessary but it improves the behaviour.
if (state == SCROLL_STATE_IDLE) {
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) getLayoutManager();
int screenWidth = Resources.getSystem().getDisplayMetrics().widthPixels;
// views on the screen
int lastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition();
View lastView = linearLayoutManager.findViewByPosition(lastVisibleItemPosition);
int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
View firstView = linearLayoutManager.findViewByPosition(firstVisibleItemPosition);
// distance we need to scroll
int leftMargin = (screenWidth - lastView.getWidth()) / 2;
int rightMargin = (screenWidth - firstView.getWidth()) / 2 + firstView.getWidth();
int leftEdge = lastView.getLeft();
int rightEdge = firstView.getRight();
int scrollDistanceLeft = leftEdge - leftMargin;
int scrollDistanceRight = rightMargin - rightEdge;
if (leftEdge > screenWidth / 2) {
smoothScrollBy(-scrollDistanceRight, 0);
} else if (rightEdge < screenWidth / 2) {
smoothScrollBy(scrollDistanceLeft, 0);
}
}
}
}
SnapHelper helper = new LinearSnapHelper();
helper.attachToRecyclerView(recyclerView);
LinearSnapHelper snapHelper = new LinearSnapHelper() {
@Override
public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX, int velocityY) {
View centerView = findSnapView(layoutManager);
if (centerView == null)
return RecyclerView.NO_POSITION;
int position = layoutManager.getPosition(centerView);
int targetPosition = -1;
if (layoutManager.canScrollHorizontally()) {
if (velocityX < 0) {
targetPosition = position - 1;
} else {
targetPosition = position + 1;
}
}
if (layoutManager.canScrollVertically()) {
if (velocityY < 0) {
targetPosition = position - 1;
} else {
targetPosition = position + 1;
}
}
final int firstItem = 0;
final int lastItem = layoutManager.getItemCount() - 1;
targetPosition = Math.min(lastItem, Math.max(targetPosition, firstItem));
return targetPosition;
}
};
snapHelper.attachToRecyclerView(recyclerView);
compile "com.android.support:recyclerview-v7:24.2.0"
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
SnapHelper snapHelper = new PagerSnapHelper();
recyclerView.setLayoutManager(layoutManager);
snapHelper.attachToRecyclerView(mRecyclerView);
int displayWidth = Resources.getSystem().getDisplayMetrics().widthPixels;
parentLayout.getLayoutParams().width = displayWidth - Utils.dpToPx(16) * 4;
SnapHelper snapHelper = new PagerSnapHelper();
snapHelper.attachToRecyclerView(recyclerView);
public static int dpToPx(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics());
}