我想使用RecyclerView
来模拟MultiViewPager
的行为,特别是我希望将所选项目放在屏幕的中心,包括第一个和最后一个元素。
正如您在此图片中看到的那样,第一个项目居中,这将是我预期的结果。
我所做的是设置一个水平RecyclerView
和LinearLayoutManager
的{{1}}。此解决方案的问题是第一个和最后一个项永远不会作为选择水平居中。我应该切换我的代码以使其使用LinearSnapHelper
,还是可以利用MultiViewPager
来获得类似的结果?
答案 0 :(得分:8)
您可以使用getItemOffsets()
中的RecyclerView.ItemDecoration
来实现此功能,以适当地抵消第一个和最后一个项目。
检索给定项目的任何偏移量。
outRect
的每个字段指定项目视图应插入的像素数,类似于填充或边距。默认实现将outRect的边界设置为0并返回。如果您需要访问Adapter以获取其他数据,可以调用
getChildAdapterPosition(View)
以获取View的适配器位置。
您可能需要使用项目的大小和RecyclerView
。但无论如何都可以使用这些信息。
答案 1 :(得分:1)
此解决方案的问题在于第一项和最后一项 永远不会以选择为中心。
这可能是因为您的RecycleView负责在其布局范围内显示数据集内部的项目数。
在您提供的示例图像中,您可以通过添加"占位符"来实现该效果。数据集的第一个和最后一个位置的项目。这样,你可以让一个不可见的物品占据第一个槽,从而抵消你想要居中的物品。
此占位符项不应响应触摸事件,也不应干扰处理其他项目上的点击事件(特别是位置处理)。
您必须修改适配器getItemCount
和getItemType
。
答案 2 :(得分:1)
只需在RecyclerView
添加填充并添加clipToPadding=false
,它就会影响两端的项目。
答案 3 :(得分:1)
我最终在我的项目中实现了这个实现。您可以在构造函数中传递不同的维度来设置项目之间的间距。正如我在课程的 KDoc 中所写的那样,它会在第一个项目的左侧和最后一个项目的右侧添加 (total parent space - child width) / 2
,以便将第一个和最后一个项目居中。
import android.graphics.Rect
import android.view.View
import androidx.annotation.DimenRes
import androidx.recyclerview.widget.OrientationHelper
import androidx.recyclerview.widget.RecyclerView
/**
* Adds (total parent space - child width) / 2 to the left of first and to the right of last item (in order to center first and last items),
* and [spacing] between items.
*/
internal class OffsetItemDecoration constructor(
@DimenRes private val spacing: Int,
) : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State,
) {
val itemPosition: Int = parent.getChildAdapterPosition(view)
if (itemPosition == RecyclerView.NO_POSITION) return
val spacingPixelSize: Int = parent.context.resources.getDimensionPixelSize(spacing)
when (itemPosition) {
0 ->
outRect.set(getOffsetPixelSize(parent, view), 0, spacingPixelSize / 2, 0)
parent.adapter!!.itemCount - 1 ->
outRect.set(spacingPixelSize / 2, 0, getOffsetPixelSize(parent, view), 0)
else ->
outRect.set(spacingPixelSize / 2, 0, spacingPixelSize / 2, 0)
}
}
private fun getOffsetPixelSize(parent: RecyclerView, view: View): Int {
val orientationHelper = OrientationHelper.createHorizontalHelper(parent.layoutManager)
return (orientationHelper.totalSpace - view.layoutParams.width) / 2
}
}
答案 4 :(得分:0)
@ I.S答案的一项改进,可以100%地起作用,并且非常容易实现,而不会出现任何故障动画。首先,我们必须使用PagerSnapHelper()
才能像滚动一样查看寻呼机。要使项目居中,您需要在recyclerView上添加一个较大的填充,然后剪辑到该填充。然后使用自定义的LinearSmoothScroller
平滑地使您的项目居中。要使项目在加载时居中,只需使用平滑滚动到位置0。下面是代码
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_selection"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginLeft="30dp"
android:layout_marginRight="30dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:paddingLeft="150dp"
android:paddingRight="150dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_selection_alert"
app:layout_constraintBottom_toBottomOf="@+id/guideline_1"
android:clipToPadding="false"
android:background="@drawable/bg_stat"/>
在代码中(在C#中)
RecyclerView.LayoutManager lm = new LinearLayoutManager(Context, LinearLayoutManager.Horizontal, false);
recycler_selection = view.FindViewById<RecyclerView>(Resource.Id.recycler_selection);
recycler_selection.SetLayoutManager(new LinearLayoutManager(Context, LinearLayoutManager.Horizontal, false));
// <Set Adapter to the Recycler>
RecyclerView.SmoothScroller smoothScroller = new CenterScroller(recycler_selection.Context);
SnapHelper helper = new PagerSnapHelper();
helper.AttachToRecyclerView(recycler_selection);
smoothScroller.TargetPosition = 0;
lm.StartSmoothScroll(smoothScroller);
public class CenterScroller : LinearSmoothScroller
{
float MILLISECONDS_PER_INCH = 350f;
public CenterScroller(Context context) : base(context)
{
}
public override int CalculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int snapPreference)
{
return (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2);
}
protected override float CalculateSpeedPerPixel(DisplayMetrics displayMetrics)
{
return MILLISECONDS_PER_INCH / displayMetrics.Xdpi;
}
}