我有 DialogFragment
,其中包含 RecyclerView
(卡片列表) .
在这个 RecyclerView
中有一个或多个 CardViews
可以有任何高度 .
我想根据其中包含的 CardViews
给出 DialogFragment
正确的高度 .
通常情况下这很简单,我会像这样在 RecyclerView
上设置 wrap_content
.
<android.support.v7.widget.RecyclerView ...
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:scrollbars="vertical" >
</android.support.v7.widget.RecyclerView>
因为我使用 RecyclerView
这不起作用,请参阅:
https://issuetracker.google.com/issues/37001674
和
Nested Recycler view height doesn't wrap its content
在这两个页面上,人们建议扩展 LinearLayoutManager
并覆盖 onMeasure()
我首先使用了第一个链接中提供的 LayoutManager :
public static class WrappingLayoutManager extends LinearLayoutManager {
public WrappingLayoutManager(Context context) {
super(context);
}
private int[] mMeasuredDimension = new int[2];
@Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
int widthSpec, int heightSpec) {
final int widthMode = View.MeasureSpec.getMode(widthSpec);
final int heightMode = View.MeasureSpec.getMode(heightSpec);
final int widthSize = View.MeasureSpec.getSize(widthSpec);
final int heightSize = View.MeasureSpec.getSize(heightSpec);
measureScrapChild(recycler, 0,
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
mMeasuredDimension);
int width = mMeasuredDimension[0];
int height = mMeasuredDimension[1];
switch (widthMode) {
case View.MeasureSpec.EXACTLY:
case View.MeasureSpec.AT_MOST:
width = widthSize;
break;
case View.MeasureSpec.UNSPECIFIED:
}
switch (heightMode) {
case View.MeasureSpec.EXACTLY:
case View.MeasureSpec.AT_MOST:
height = heightSize;
break;
case View.MeasureSpec.UNSPECIFIED:
}
setMeasuredDimension(width, height);
}
private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
int heightSpec, int[] measuredDimension) {
View view = recycler.getViewForPosition(position);
if (view != null) {
RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
getPaddingLeft() + getPaddingRight(), p.width);
int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
getPaddingTop() + getPaddingBottom(), p.height);
view.measure(childWidthSpec, childHeightSpec);
measuredDimension[0] = view.getMeasuredWidth();
measuredDimension[1] = view.getMeasuredHeight();
recycler.recycleView(view);
}
}
}
但是 this did not work 因为
heightSize = View.MeasureSpec.getSize(heightSpec);
返回一个非常大的值,看起来与 match_parent
有关 .
通过评论 height = heightSize;
(在第二个开关的情况下)我设法使高度工作,但只有 CardView
内的 TextView
孩子不包装自己的文本(一个长句子) .
一旦 TextView
包裹它's own text the height SHOULD increase but it doesn' t . 它将该长句的高度计算为单行,而不是包裹行(2或更多) .
关于我应该如何改进这个 LayoutManager
的任何建议所以我的 RecyclerView
适用于 WRAP_CONTENT
?
Edit: This layout manager might work for most people, but it still has problems with scrolling and calculating heights of wrapping textviews
public class MyLinearLayoutManager extends LinearLayoutManager {
public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
private int[] mMeasuredDimension = new int[2];
@Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
int widthSpec, int heightSpec) {
final int widthMode = View.MeasureSpec.getMode(widthSpec);
final int heightMode = View.MeasureSpec.getMode(heightSpec);
final int widthSize = View.MeasureSpec.getSize(widthSpec);
final int heightSize = View.MeasureSpec.getSize(heightSpec);
int width = 0;
int height = 0;
for (int i = 0; i < getItemCount(); i++) {
measureScrapChild(recycler, i,
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
mMeasuredDimension);
if (getOrientation() == HORIZONTAL) {
width = width + mMeasuredDimension[0];
if (i == 0) {
height = mMeasuredDimension[1];
}
} else {
height = height + mMeasuredDimension[1];
if (i == 0) {
width = mMeasuredDimension[0];
}
}
}
switch (widthMode) {
case View.MeasureSpec.EXACTLY:
width = widthSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
switch (heightMode) {
case View.MeasureSpec.EXACTLY:
height = heightSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
setMeasuredDimension(width, height);
}
private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
int heightSpec, int[] measuredDimension) {
View view = recycler.getViewForPosition(position);
if (view != null) {
RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
getPaddingLeft() + getPaddingRight(), p.width);
int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
getPaddingTop() + getPaddingBottom(), p.height);
view.measure(childWidthSpec, childHeightSpec);
measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
recycler.recycleView(view);
}
}
}
17 回答
从Android Support Library 23.2.1更新,所有 WRAP_CONTENT 应该正常工作 .
请更新
gradle
文件 OR 中的库版本以进一步:解决了一些与各种测量规范方法相关的固定错误问题
检查http://developer.android.com/tools/support-library/features.html#v7-recyclerview
你可以查一下Support Library revision history
以下是该类的精炼版本,似乎有效,并且缺少其他解决方案的问题:
这也是library . 链接到relevant class .
终于找到了解决这个问题的方法 .
您需要做的就是将
RecyclerView
包装在RelativeLayout
中 . 也许还有其他观点可能也有效 .UPDATE
通过Android支持库23.2更新,所有WRAP_CONTENT都应该正常工作 .
请在gradle文件中更新库的版本 .
原始答案
正如其他问题所解答的,当您的回收站视图高度大于屏幕高度时,您需要使用原始的onMeasure()方法 . 此布局管理器可以计算ItemDecoration并可以滚动更多 .
原始答案:https://stackoverflow.com/a/28510031/1577792
这是 c# version for mono android
RecyclerView
在23.2.0
中添加了对wrap_content
的支持,这是有缺陷的,23.2.1只是稳定的,所以你可以使用:您可以在此处查看修订历史记录:
https://developer.android.com/topic/libraries/support-library/revisions.html
Note:
另请注意,更新支持库后
RecyclerView
将尊重wrap_content
以及match_parent
所以如果您将RecyclerView
的项目视图设置为match_parent
,则单个视图将填满整个屏幕滚动和文本换行的问题是此代码假设宽度和高度都设置为
wrap_content
. 但是,LayoutManager
需要知道水平宽度受到约束 . 因此,不要为每个子视图创建自己的widthSpec
,只需使用原始widthSpec
:试试这个(这是一个令人讨厌的解决方案,但它可能有效):在
Activity
的onCreate
方法或片段的onViewCreated
方法中 . 设置一个回调准备好在RecyclerView
首次渲染时被触发,如下所示:在
calculeRecyclerViewFullHeight
中根据其子项的高度计算RecyclerView
全高 .在我的情况下,我的
RecyclerView
包含在SwipeRefreshLayout
中,因为我将高度设置为SwipeRefreshView
而不是RecyclerView
但是如果你没有任何SwipeRefreshView
那么你可以将高度设置为RecyclerView
.如果这对你有帮助,请告诉我 .
这现在可以正常运行,因为他们已经在版本23.2中发布了一个版本,如post中所述 . 引用官方blogpost
而不是使用任何库,最简单的解决方案,直到新版本出来是打开b.android.com/74772 . 您可以轻松找到迄今为止已知的最佳解决方案 .
PS:b.android.com/74772#c50为我工作
我使用了上面的一些解决方案,但它适用于
width
但height
.compileSdkVersion
大于 23 ,你可以直接使用 RecyclerView 在各自的Recycler视图支持库中提供,就像 23 那样'com.android.support:recyclerview-v7:23.2.1'
. 这些 support libraries 支持wrap_content
属性的宽度和高度 .您必须将其添加到依赖项中
compileSdkVersion
小于 23 ,你可以使用下面提到的解决方案 .关于这个问题我找到了Google thread . 在这个帖子中,有一个贡献导致LinearLayoutManager的实施 .
I have tested it for both height and width and it worked fine for me in both cases.
This comment helped me.
Put the recyclerview in any other layout (Relative layout is preferable). Then change recyclerview's height/width as match parent to that layout and set the parent layout's height/width as wrap content.
替换
measureScrapChild
以遵循代码:我使用xamarin,所以这是c#代码 . 我认为这可以很容易地“翻译”为Java .
在Adapter viewholder onCreateViewHolder方法中使用null值而不是父视图组更新视图 .
您必须将FrameLayout作为主视图,然后使用ScrollView和至少您的RecyclerView放入RelativeLayout,它适用于我 .
这里的真正诀窍是RelativeLayout ......
乐于帮助 .
我建议你将recyclerview放在任何其他布局中(相对布局更可取) . 然后将recyclerview的高度/宽度更改为与该布局匹配的父级,并将父级布局的高度/宽度设置为换行内容 . 这个对我有用
我没有处理我的答案,但我知道它的方式StaggridLayoutManager没有 . 网格1可以解决您的问题,因为StaggridLayout会根据内容的大小自动调整其高度和宽度 . 如果它的工作不要忘记检查它是一个正确的答案 . 欢呼..