在RecyclerView项中,如何以百分比的方式获取Android屏幕宽度

55
我该如何让RecyclerView的item高度为屏幕高度的30%?不能简单地使用ConstraintLayout或PercentFrameLayout/PercentRelativeLayout,因为这些布局是相对于父布局定位子布局的,而父布局的大小可能与屏幕不匹配。最好能够在纯XML中实现(而不必使用任何Runnables来动态获取屏幕高度)。
编辑:澄清一下,因为很多解决方案建议限制RecyclerView的高度,但这并不是本意。RecyclerView仍应填满屏幕。这是我用屏幕宽度(而不是高度)来说明的:

enter image description here

这个想法是使卡片的宽度为屏幕宽度的33%,同时在RecyclerView水平滚动时保持其宽度不变。


如果这个解释不清楚,请告诉我:我知道你可以根据包含PercentLayout或ConstraintLayout的大小设置百分比。然而,由于我正在处理RecyclerView中的项目,所以封闭布局既不是PercentLayout也不是ConstraintLayout。 - davidchuyaya
我认为你应该阅读这篇文章 不要在屏幕方向改变时重新加载应用程序 - Mithun Halmare
那个问题与这个有什么关系? - davidchuyaya
9个回答

125

您可以重写一个LayoutManager方法,以强制指定RecyclerView的特定大小:

recyclerView.setLayoutManager(new LinearLayoutManager(this){
    @Override
    public boolean checkLayoutParams(RecyclerView.LayoutParams lp) {
        // force height of viewHolder here, this will override layout_height from xml
        lp.height = getHeight() / 3;
        return true;
    }
});

假设您的RecyclerView适合屏幕。

如果您想要更复杂的解决方案(使用自定义布局管理器,可以控制每个项目和来自XML的百分比膨胀),请查看此答案


27
被严重低估的答案。 - Luke Needham
14
我爱你。不是一种泛泛的“好回答”的方式,而是真正的爱。我正在送你花。 - Psest328
3
漂亮的解决方案! - sebasira
3
这非常优雅。我更喜欢这个选项,而不是所选的答案。 - rexar5
2
这个解决方案比在RecyclerView中传递上下文并使用WindowManager计算宽度或高度更好。 - Vishal Naikawadi
显示剩余3条评论

52

据我所知,XML 中没有这样做的方法。但是,在 Java 中可以完成此操作(无需任何Runnable等),只需在适配器的onCreateViewHolder()方法中返回ViewHolder之前设置您的ViewHolderitemViewLayoutParams

@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    LayoutInflater inflater = LayoutInflater.from(parent.getContext());
    View itemView = inflater.inflate(R.layout.itemview, parent, false);

    ViewGroup.LayoutParams layoutParams = itemView.getLayoutParams();
    layoutParams.height = (int) (parent.getHeight() * 0.3);
    itemView.setLayoutParams(layoutParams);

    return new MyViewHolder(itemView);
}

我该如何将这个应用到我的宽度上?我想在屏幕上显示卡片的80%,并在其旁边展示下一张卡片内容的预览。是否可以使用Recycler View实现类似走马灯的效果? - user11455292
1
你有没有想过 parent.getHeight() 或者 parent.getWidth() 会返回0,尤其是当你把 RecyclerView 放在 ConstraintLayout 里面时: <androidx.recyclerview.widget.RecyclerView android:layout_width="0dp" android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"/> - user924
声明layout_width="0dp",然后设置约束来定义宽度,将导致getWidth()返回一个实际值,而不是0。 - Ben P.
@BenP。你自己试试吧,当然它会是一个真正的值,但不会很快,当onCreateViewHolder被调用时,它将为0,但稍后它将成为一个真正的值。 - user924
这不是一个有效的答案。这不适用于水平滚动行为。它会减小视图宽度。 - Shaon
@user924,你找到解决方案了吗? - Cilenco

9

您可以使用显示度量

在构造函数中定义显示度量,或使用任何其他静态方法初始化。

DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);

现在你可以在 onCreateViewHolder() 中使用它。
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);

//setting the height programmatically
itemView.getLayoutParams().height = metrics.heightPixels/3;
itemView.requestLayout();

或者您可以在父布局的onBindViewHolder()中使用。

4
您无法仅通过XML设置项目的百分比高度。原因是它是在运行时膨胀的项目,而不是像布局一样写在XML中的活动。因此,现在我告诉您两种实现百分比/权重的方法。
首选的第一种方法是在Adapter中的onCreateViewHolder()方法中覆盖高度。
private int height = 0; // declared height globally in Adapter class to reuse it.

@Override
public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
    if (height == 0)
        height = (int) (parent.getMeasuredHeight() * .3);
    itemView.setMinimumHeight(height);
    return new ItemViewHolder(itemView);
}

第二个技巧是使用SDP库。

在XML中使用sdp的项目高度,例如_160sdp。它可能无法精确地给出30%的结果,但可以根据屏幕进行调整。


不,这只是在成千上万的用户界面过程中添加了一些逻辑。 - Khemraj Sharma
如果你仍然多次提到我们正在获取 measuredHeight,那么你可以将第一个保存在全局变量中。然后重复使用它。 - Khemraj Sharma
您提出的修改并没有什么用,如果您不检查空值并重复使用它的话。我会进行编辑。 - Khemraj Sharma
@salman已经进行了改进,还有一个修复parent.getContext()的问题。 - Khemraj Sharma

2
    DisplayMetrics displayMetrics = new DisplayMetrics();
    ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
    int devicewidth = (int) (displayMetrics.widthPixels * 0.8);
    view.getLayoutParams().width = devicewidth;
    return new YourAdapter.ViewHolder(view);

2

我通过在自定义的 RecyclerView 项布局中执行以下操作来实现这一点:

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    val newMeasuredWidth =
        (MeasureSpec.getSize(widthMeasureSpec) * WIDTH_ADJUSTMENT_RATIO).toInt()
    super.onMeasure(
        MeasureSpec.makeMeasureSpec(newMeasuredWidth, MeasureSpec.EXACTLY),
        heightMeasureSpec
    )
}

0

如果您不介意精确的百分比,您可以通过设置 android:paddingLeftandroid:paddingRight,并将 android:clipToPadding 设置为 false 来实现所需的视图。 这还使得第一个和最后一个项目居中对齐。


0

试一下这个方式。仅仅一次

inner class MyFeatureProductViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
    val tvName = v.findViewById<TextView>(R.id.tvName)
    val tvPrice = v.findViewById<TextView>(R.id.tvPrice)
    val tvItemUploadTime = v.findViewById<TextView>(R.id.tvItemUploadTime)
    val ivItem = v.findViewById<ImageView>(R.id.ivItem)

    init {
        v.layoutParams.width =  itemView.measuredWidth - 20
        v.requestLayout()
    }
}

-2

我刚试图解决你遇到的问题。 我尝试了几个方法,步骤如下:

  1. MainActivity视图中,RecyclerView的高度恰好为屏幕的30%。 enter image description here
  2. Recycler View项目的高度和宽度必须匹配父级。 enter image description here

那里发生了什么?

  1. recyclerHolder在那里确实有屏幕高度的30%,毫无疑问。

  2. recyclerHolder内部,我们有一个RecyclerView,它占据了所有父级的30%高度并占据了整个宽度。

  3. ListItem Layout占据父级的全高和全宽,其中父级是具有屏幕高度的30%的RecyclerView

我尝试了这个方法,它完美地运行了。你可以找到两个空视图来测试它们是否完美地适配在顶部和底部,结果是肯定的。无论在列表项中放置什么内容,它都会占据屏幕高度的30%。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接