我需要创建一个滚动效果,如果某项小于某个定义的宽度(对于肖像),该效果将扩大项之间的间隔。这样做是为了使中心项居中后,显示的上一个/下一个项目的数量始终相同。
我通过在回收站视图中添加滚动侦听器来实现这一目的,在该视图中,我更改了所有部分或完全可见的物品(如果是肖像)的宽度。项目之间的最小间距是通过在项目上设置左右边距来实现的,除了第一个和最后一个项目(在最后一个边上具有较大的边距,以便它们保持居中)之外,项目之间不会发生变化。
如果我缓慢滚动,下面的实现效果很好,但是当我猛扑时,它会产生抖动。我猜这是因为fling根据fling时对象的宽度来计算它需要滚动的像素数...但是由于宽度的变化,fling运动计算是错误的,所以它需要进行调整,我猜这会产生抖动,问题是我不知道如何解决。
这是慢滚动的样子(很好地工作):
和紧张的逃跑:
使用的代码(我跳过了我认为什么都没改变的部分): 在MainActivity onCreate中:
recyclerView.setHasFixedSize(true);
layoutManager = new LinearLayoutManager(getApplicationContext(), LinearLayoutManager.HORIZONTAL, false);
((LinearLayoutManager) layoutManager).setInitialPrefetchItemCount(5);
recyclerView.setLayoutManager(layoutManager);
adapter = new MyAdapter(getApplicationContext(), thumbnails, recyclerView, 8, 24);
recyclerView.setAdapter(adapter);
SnapHelper snapHelper = new LinearSnapHelper();
snapHelper.attachToRecyclerView(recyclerView);
MyAdapter:
public MyAdapter(Context context, ArrayList<Thumbnail> galleryList, final RecyclerView recyclerView, float spacingDp, float sideItemsVisibleDp) {
this.galleryList = galleryList;
this.context = context;
this.spacingDp = spacingDp;
this.sideItemsVisibleDp = sideItemsVisibleDp;
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
if (layoutManager != null && windowWidth != 0) {
int firstPosition = layoutManager.findFirstVisibleItemPosition();
int lastPosition = layoutManager.findLastVisibleItemPosition();
if (firstPosition == RecyclerView.NO_POSITION || lastPosition == RecyclerView.NO_POSITION) {
Log.e(TAG, String.format(Locale.US, "onScrolled firstPosition %d lastPosition %d", firstPosition, lastPosition));
return;
}
for (int pos = firstPosition; pos <= lastPosition; pos++) {
View child = layoutManager.findViewByPosition(pos);
if (child != null) {
ViewHolder viewHolder = (ViewHolder) recyclerView.getChildViewHolder(child);
viewHolder.computeAndSetWidth();
} else {
Log.e(TAG, String.format(Locale.US, "onScrolled pos %d child == null", pos));
}
}
} else {
Log.e(TAG, String.format(Locale.US, "onScrolled layoutManager %s windowWidth %d", layoutManager, windowWidth));
}
}
});
}
@Override
public void onBindViewHolder(@NonNull final MyAdapter.ViewHolder viewHolder, final int position) {
Log.d(TAG, "onBindViewHolder position " + position);
// Set the text of the position
viewHolder.text.setText(String.format(Locale.US, "%d", position));
// Reset the container
ViewGroup.LayoutParams layoutParams = viewHolder.container.getLayoutParams();
layoutParams.height = itemMaxHeight;
layoutParams.width = itemMaxWidth;
viewHolder.container.setLayoutParams(layoutParams);
// Set the image
viewHolder.img.setScaleType(ImageView.ScaleType.FIT_CENTER);
viewHolder.img.setImageResource(galleryList.get(position).getImageId());
int startEndSpace = (windowWidth - itemMaxWidth) / 2;
int leftSpace = itemMinSpacing;
int rightSpace = itemMinSpacing;
if (position == 0) { // leftmost
leftSpace = startEndSpace;
}
if (position == (galleryList.size() - 1)) { // rightmost
rightSpace = startEndSpace;
}
((RecyclerView.LayoutParams) layoutParams).setMargins(leftSpace, 0, rightSpace, 0);
}
ViewHolder:
public class ViewHolder extends RecyclerView.ViewHolder {
private View container;
private ImageView img;
private TextView text;
public ViewHolder(View view) {
super(view);
container = view;
img = (ImageView) view.findViewById(R.id.img);
text = (TextView) view.findViewById(R.id.textview);
}
public boolean isPortrait() {
return img.getMeasuredHeight() > img.getMeasuredWidth();
}
public void computeAndSetWidth() {
int[] locationOnScreen = new int[2];
container.getLocationOnScreen(locationOnScreen);
int imgWidth = img.getMeasuredWidth();
int centerX = locationOnScreen[0] + container.getWidth() / 2; // location on screen of the center of image on X axis
float percentFromEdge;
if (centerX == (windowWidth/2)) {
percentFromEdge = 1.0f; // is in center
} else if (centerX > 0 && centerX < (windowWidth/2)) { // left side of screen, but center is on screen
percentFromEdge = (2f*centerX) / windowWidth;
} else if (centerX > (windowWidth/2) && centerX < windowWidth) {
percentFromEdge = 2f * (windowWidth - centerX) / windowWidth;
} else { // off screen
percentFromEdge = 0.0f;
}
int width = (int) ((itemMaxWidth - imgWidth) * percentFromEdge + imgWidth);
setWidth(width);
}
private void setWidth(final int width) {
ViewGroup.LayoutParams layoutParams = container.getLayoutParams();
layoutParams.width = width;
layoutParams.height = itemMaxHeight;
container.setLayoutParams(layoutParams);
}
}
cell_layout.xml:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/img"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
android:scaleType="fitCenter"
android:adjustViewBounds="true" />
<TextView
android:id="@+id/textview"
android:layout_gravity="center"
android:textColor="@color/colorAccent"
android:textSize="40sp"
android:text="0"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</FrameLayout>
RecyclerView被扩展了,在这里我覆盖了fling以减慢它的速度,但是就是这样:
@Override
public boolean fling(int velocityX, int velocityY) {
return super.fling((int)(velocityX * flingFactor), (int)(velocityY * flingFactor));
}
答案 0 :(得分:0)
好吧,对于有相同问题的任何人,这是我的解决方案:
我使所有的recyclerView项目具有相同的宽度,这是允许的最大宽度。我将ImageView移至项目容器的左/中/右,以便当容器位于中心时它位于中心,当容器位于右侧时则将其移到容器的左侧,反之亦然。完成此计算,而不是调用viewHolder.computeAndSetWidth();。在recyclerView.OnScrollListener()中。
最后一件事是将LinearSmoothScroller添加到扩展的recyclerView中,如下所示:
smoothScroller = new LinearSmoothScroller(context) {
@Override
protected int getHorizontalSnapPreference() {
return LinearSmoothScroller.SNAP_TO_ANY;
}
@Override
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return scrollMillisecondsPerInch / displayMetrics.densityDpi;
}
};