Android 如何设置RecyclerView项相对于父项的最大高度
通过遵循这个问题中的建议,我几乎可以做到这一点 但是,这会为所有视图设置一个高度,无论内容如何,高度都相同。在我的例子中,我只想限制Android 如何设置RecyclerView项相对于父项的最大高度,android,android-recyclerview,android-constraintlayout,Android,Android Recyclerview,Android Constraintlayout,通过遵循这个问题中的建议,我几乎可以做到这一点 但是,这会为所有视图设置一个高度,无论内容如何,高度都相同。在我的例子中,我只想限制RecyclerView中的一个项目与它的高度相关的高度 在我的房车中,我有不同高度的物品,其中一些物品甚至可以比房车更高 我的目标是告诉您,该项目的最大高度可以达到RecyclerView高度的60%,到目前为止,我发现实现这一点的唯一方法是在使用以下逻辑将新项目绑定到视图保持架时手动设置最大高度: constraintSet.constrainMaxHeigh
RecyclerView
中的一个项目与它的高度相关的高度
在我的房车中,我有不同高度的物品,其中一些物品甚至可以比房车更高
我的目标是告诉您,该项目的最大高度可以达到RecyclerView
高度的60%,到目前为止,我发现实现这一点的唯一方法是在使用以下逻辑将新项目绑定到视图保持架时手动设置最大高度:
constraintSet.constrainMaxHeight(viewId, (int) (recyclerView.getMeasuredHeight() * 0.6));
其中,recyclerView
是对托管recyclerView
的引用,我使用适配器的方法onAttachedToRecyclerView
传递给适配器,viewId
是RV项目ConstraintLayout
根视图中的主视图,我可以用它来控制它的最大高度
尽管这能满足我的需要,但我想找到一个更简单/更优化的解决方案,在简单性方面与此类似:
这意味着不允许使用ViewTreeObserver
有什么想法吗?用边距覆盖
measureChildWithMargins
是一种方法,但是要让它真正工作,您需要自定义LayoutParams
,这样您就可以从适配器“结转”数据,或者直接从XML中放大所需的百分比
此布局管理器将处理它:
open class PercentLinearLayoutManager : LinearLayoutManager {
constructor(context: Context?) : super(context)
constructor(context: Context?, orientation: Int, reverseLayout: Boolean) : super(context, orientation, reverseLayout)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes)
// where we actually force view to be measured based on custom layout params
override fun measureChildWithMargins(child: View, widthUsed: Int, heightUsed: Int) {
val pp = child.layoutParams as PercentParams
if (pp.maxPercentHeight <= 0.0f)
super.measureChildWithMargins(child, widthUsed, heightUsed)
else {
val widthSpec = getChildMeasureSpec(width, widthMode,
paddingLeft + paddingRight + pp.leftMargin + pp.rightMargin + widthUsed, pp.width,
canScrollHorizontally())
val maxHeight = (height * pp.maxPercentHeight).toInt()
val heightSpec = when (pp.height) {
ViewGroup.LayoutParams.MATCH_PARENT -> View.MeasureSpec.makeMeasureSpec(maxHeight, View.MeasureSpec.EXACTLY)
ViewGroup.LayoutParams.WRAP_CONTENT -> View.MeasureSpec.makeMeasureSpec(maxHeight, View.MeasureSpec.AT_MOST)
else -> View.MeasureSpec.makeMeasureSpec(min(pp.height, maxHeight), View.MeasureSpec.AT_MOST)
}
child.measure(widthSpec, heightSpec)
}
}
// everything below is needed to generate custom params
override fun checkLayoutParams(lp: RecyclerView.LayoutParams) = lp is PercentParams
override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams {
return PercentParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
}
override fun generateLayoutParams(lp: ViewGroup.LayoutParams): RecyclerView.LayoutParams {
return PercentParams(lp)
}
override fun generateLayoutParams(c: Context, attrs: AttributeSet): RecyclerView.LayoutParams {
return PercentParams(c, attrs)
}
class PercentParams : RecyclerView.LayoutParams {
/** Max percent height of recyclerview this item can have. If height is `match_parent` this size is enforced. */
var maxPercentHeight = 0.0f
constructor(c: Context, attrs: AttributeSet) : super(c, attrs){
val t = c.obtainStyledAttributes(attrs, R.styleable.PercentLinearLayoutManager_Layout)
maxPercentHeight = t.getFloat(R.styleable.PercentLinearLayoutManager_Layout_maxPercentHeight, 0f)
t.recycle()
}
constructor(width: Int, height: Int) : super(width, height)
constructor(source: MarginLayoutParams?) : super(source)
constructor(source: ViewGroup.LayoutParams?) : super(source)
constructor(source: RecyclerView.LayoutParams?) : super(source)
}
}
那么您有两个选择:
1-将onBindViewHolder中的自定义参数更改为具有每项控件:
val lp = holder.itemView.layoutParams as PercentLinearLayoutManager.PercentParams
if(modifyThisViewHolderHeight) {
lp.maxPercentHeight = 0.6f
} else {
lp.maxPercentHeight = 0.0f // clear percentage in case viewholder is reused
}
2-在viewholder布局中使用自定义属性:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:maxPercentHeight ="0.6">
<!-- rest of layout-->
</FrameLayout>
我建议使用我在链接答案中使用的近似策略:在创建
视图保持架时设置最大高度。但是,genericView
不支持maxHeight
属性,因此我们将利用ConstraintLayout
实现我们想要的功能
如果项目视图的根视图已经是一个ConstraintLayout
,那就太好了,否则您可以包装其中的任何内容。在这个精心设计的示例中,我使用了这个布局(FrameLayout在XML中使用了0dp
width和wrap\u content
height):
为什么Ben P不为你工作?代码将在创建视图持有者时运行,而不是每次重新绑定时运行,这更简单。@Cheticamp我在第二段中提到了它不适用于我的原因;当我只想设置最大高度时,他的答案为所有项目设置了相同的固定高度。i、 e.我不希望所有项目都具有相同的高度,它们可以变化,我只想通过设置最大高度来限制它们的变化程度。我知道,对于RecyclerView的项目,没有“最大高度”设置,这正是我所想的。太糟糕了。您可能可以使用来强制执行最大高度。但我不确定这是否更简单。我不确定你是否真的读了我在问题中解释的内容,或者你是否只是忽略了它,但我明确表示,我希望找到一个比我已经使用的更简单/更优化的解决方案。您的建议正好相反。@阴影默认布局管理器不支持百分比大小,这就是为什么您必须编写自定义布局管理器来正确处理它。任何在绑定viewholder时依赖于快照recyclerViews高度的解决方案都是有缺陷的,因为它无法正确处理recyclerViews自身被调整大小的情况。我理解你的意思,但正如我所提到的,这与我所要求的正好相反。但是,我相信这对于想要这种功能级别的人来说是非常方便的。你能编辑你的答案并在开头提到这一点吗?@Pawel请告诉我是否(修改ThisViewHolderHeight){我在这里写的是“修改ThisViewHolderHeight”@Meerz这只是一个伪代码,适用于您可能需要检查的任何条件,以防您在每个项目的基础上更改百分比。如果您想将其应用于所有项目,您可以只使用XML属性。这感觉就像我已经在做的,但在项目的根视图中有一个额外的层,但它可能比我在t他说,“更好”是我想要的。我会试试看,谢谢!是的,它在整体策略上非常相似。基本上,它只是从onBind移动到onCreate。注意一些视图(如TextView
)本机支持最大高度;在这些情况下,您可以避免使用ConstraintLayout
包装器,只需在onCreateViewHolder()
中调用setMaxHeight()
。了解,幸运的是,我正在输入的项目的所有根视图都是ConstraintLayout
,因此目前没有问题。
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:maxPercentHeight ="0.6">
<!-- rest of layout-->
</FrameLayout>
<androidx.constraintlayout.widget.ConstraintLayout ...>
<FrameLayout ...>
<TextView .../>
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val inflater = LayoutInflater.from(parent.context)
val itemView = inflater.inflate(R.layout.recycler_item, parent, false)
val holder = MyViewHolder(itemView)
val params = holder.frame.layoutParams as ConstraintLayout.LayoutParams
params.matchConstraintMaxHeight = (parent.height * 0.6).toInt()
holder.frame.layoutParams = params
return holder
}