roy*_*oyB 240 android android-layout android-scrollview android-cardview android-recyclerview
我正在尝试在相同的布局中实现包含RecyclerView和ScrollView的布局.
布局模板:
<RelativeLayout>
<ScrollView android:id="@+id/myScrollView">
<unrelated data>...</unrealated data>
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/my_recycler_view"
/>
</ScrollView>
</RelativeLayout>
Run Code Online (Sandbox Code Playgroud)
问题:我可以滚动直到最后一个元素 ScrollView
我试过的事情:
ScrollView(现在ScrollView包含RecyclerView) - 可以看到卡片直到RecyclerViewviewGroup,RecyclerView而不是ScrollView其中一个视图类型是,CardView但我得到了与完全相同的结果ScrollViewYan*_*ong 591
用NestedScrollView而不是ScrollView
有关更多信息,请参阅NestedScrollView参考文档.
并添加recyclerView.setNestedScrollingEnabled(false);到您的RecyclerView
Hos*_*han 92
我知道我在游戏中已经迟到了,但即使谷歌已经修复了这个问题,这个问题仍然存在 android.support.v7.widget.RecyclerView
我现在得到的问题是RecyclerView有layout_height=wrap_content没有把所有的项目问题的高度内ScrollView即只发生在棉花糖和牛轧糖+(API 23,24,25)版本.
(UPDATE:更换ScrollView用android.support.v4.widget.NestedScrollView.在所有版本的作品我种种原因错过了测试接受的解决方案在我的GitHub项目作为演示添加这个.)
尝试不同的东西后,我找到了修复此问题的解决方法.
简而言之,这是我的布局结构:
<ScrollView>
<LinearLayout> (vertical - this is the only child of scrollview)
<SomeViews>
<RecyclerView> (layout_height=wrap_content)
<SomeOtherViews>
Run Code Online (Sandbox Code Playgroud)
解决办法是在包装的RecyclerView用RelativeLayout.不要问我怎么找到这个解决方法!¯\_(?)_/¯
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
Run Code Online (Sandbox Code Playgroud)
GitHub项目提供了完整的示例- https://github.com/amardeshbd/android-recycler-view-wrap-content
这是一个演示截屏视频,显示了修复操作:

Joa*_*rom 53
虽然建议说
你永远不应该在另一个可滚动的视图中放置一个可滚动的视图
这是一个合理的建议,但是如果你在Recycler视图上设置一个固定的高度,它应该可以正常工作.
如果您知道适配器项目布局的高度,则可以计算RecyclerView的高度.
int viewHeight = adapterItemSize * adapterData.size();
recyclerView.getLayoutParams().height = viewHeight;
Run Code Online (Sandbox Code Playgroud)
小智 27
如果为RecyclerView设置固定高度对某人(比如我)不起作用,这就是我在固定高度解决方案中添加的内容:
mRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
int action = e.getAction();
switch (action) {
case MotionEvent.ACTION_MOVE:
rv.getParent().requestDisallowInterceptTouchEvent(true);
break;
}
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
});
Run Code Online (Sandbox Code Playgroud)
jca*_*ady 15
RecyclerViews可以放入ScrollViews,只要它们不滚动自己.在这种情况下,使其成为固定高度是有意义的.
正确的解决方案是wrap_content在RecyclerView高度上使用,然后实现可以正确处理包装的自定义LinearLayoutManager.
将此LinearLayoutManager复制到您的项目中:https://github.com/serso/android-linear-layout-manager/blob/master/lib/src/main/java/org/solovyev/android/views/llm/LinearLayoutManager.java
然后包装RecyclerView:
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
Run Code Online (Sandbox Code Playgroud)
并设置如下:
RecyclerView list = (RecyclerView)findViewById(R.id.list);
list.setHasFixedSize(true);
list.setLayoutManager(new com.example.myapp.LinearLayoutManager(list.getContext()));
list.setAdapter(new MyViewAdapter(data));
Run Code Online (Sandbox Code Playgroud)
编辑:这可能导致滚动的复杂化,因为RecyclerView可以窃取ScrollView的触摸事件.我的解决方案就是放弃所有的RecyclerView并使用LinearLayout,以编程方式夸大子视图,并将它们添加到布局中.
mus*_*taq 14
因为ScrollView,您可以使用fillViewport=true和制作layout_height="match_parent"如下,并将Recycler视图放入其中:
<ScrollView
android:fillViewport="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/llOptions">
<android.support.v7.widget.RecyclerView
android:id="@+id/rvList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</ScrollView>
Run Code Online (Sandbox Code Playgroud)
无需通过代码进一步调整高度.
Aru*_*ney 13
RecyclerView手动计算高度不好,更好的是使用自定义LayoutManager.
原因上面的问题是它有它的滚动(任何观点ListView,GridView,RecyclerView)未能计算出它的高度时,作为另一种观点认为一个孩子增加了滚动.因此,重写其onMeasure方法将解决问题.
请使用以下内容替换默认布局管理器:
public class MyLinearLayoutManager extends android.support.v7.widget.LinearLayoutManager {
private static boolean canMakeInsetsDirty = true;
private static Field insetsDirtyField = null;
private static final int CHILD_WIDTH = 0;
private static final int CHILD_HEIGHT = 1;
private static final int DEFAULT_CHILD_SIZE = 100;
private final int[] childDimensions = new int[2];
private final RecyclerView view;
private int childSize = DEFAULT_CHILD_SIZE;
private boolean hasChildSize;
private int overScrollMode = ViewCompat.OVER_SCROLL_ALWAYS;
private final Rect tmpRect = new Rect();
@SuppressWarnings("UnusedDeclaration")
public MyLinearLayoutManager(Context context) {
super(context);
this.view = null;
}
@SuppressWarnings("UnusedDeclaration")
public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
this.view = null;
}
@SuppressWarnings("UnusedDeclaration")
public MyLinearLayoutManager(RecyclerView view) {
super(view.getContext());
this.view = view;
this.overScrollMode = ViewCompat.getOverScrollMode(view);
}
@SuppressWarnings("UnusedDeclaration")
public MyLinearLayoutManager(RecyclerView view, int orientation, boolean reverseLayout) {
super(view.getContext(), orientation, reverseLayout);
this.view = view;
this.overScrollMode = ViewCompat.getOverScrollMode(view);
}
public void setOverScrollMode(int overScrollMode) {
if (overScrollMode < ViewCompat.OVER_SCROLL_ALWAYS || overScrollMode > ViewCompat.OVER_SCROLL_NEVER)
throw new IllegalArgumentException("Unknown overscroll mode: " + overScrollMode);
if (this.view == null) throw new IllegalStateException("view == null");
this.overScrollMode = overScrollMode;
ViewCompat.setOverScrollMode(view, overScrollMode);
}
public static int makeUnspecifiedSpec() {
return View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
}
@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);
final boolean hasWidthSize = widthMode != View.MeasureSpec.UNSPECIFIED;
final boolean hasHeightSize = heightMode != View.MeasureSpec.UNSPECIFIED;
final boolean exactWidth = widthMode == View.MeasureSpec.EXACTLY;
final boolean exactHeight = heightMode == View.MeasureSpec.EXACTLY;
final int unspecified = makeUnspecifiedSpec();
if (exactWidth && exactHeight) {
// in case of exact calculations for both dimensions let's use default "onMeasure" implementation
super.onMeasure(recycler, state, widthSpec, heightSpec);
return;
}
final boolean vertical = getOrientation() == VERTICAL;
initChildDimensions(widthSize, heightSize, vertical);
int width = 0;
int height = 0;
// it's possible to get scrap views in recycler which are bound to old (invalid) adapter entities. This
// happens because their invalidation happens after "onMeasure" method. As a workaround let's clear the
// recycler now (it should not cause any performance issues while scrolling as "onMeasure" is never
// called whiles scrolling)
recycler.clear();
final int stateItemCount = state.getItemCount();
final int adapterItemCount = getItemCount();
// adapter always contains actual data while state might contain old data (f.e. data before the animation is
// done). As we want to measure the view with actual data we must use data from the adapter and not from the
// state
for (int i = 0; i < adapterItemCount; i++) {
if (vertical) {
if (!hasChildSize) {
if (i < stateItemCount) {
// we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items
// we will use previously calculated dimensions
measureChild(recycler, i, widthSize, unspecified, childDimensions);
} else {
logMeasureWarning(i);
}
}
height += childDimensions[CHILD_HEIGHT];
if (i == 0) {
width = childDimensions[CHILD_WIDTH];
}
if (hasHeightSize && height >= heightSize) {
break;
}
} else {
if (!hasChildSize) {
if (i < stateItemCount) {
// we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items
// we will use previously calculated dimensions
measureChild(recycler, i, unspecified, heightSize, childDimensions);
} else {
logMeasureWarning(i);
}
}
width += childDimensions[CHILD_WIDTH];
if (i == 0) {
height = childDimensions[CHILD_HEIGHT];
}
if (hasWidthSize && width >= widthSize) {
break;
}
}
}
if (exactWidth) {
width = widthSize;
} else {
width += getPaddingLeft() + getPaddingRight();
if (hasWidthSize) {
width = Math.min(width, widthSize);
}
}
if (exactHeight) {
height = heightSize;
} else {
height += getPaddingTop() + getPaddingBottom();
if (hasHeightSize) {
height = Math.min(height, heightSize);
}
}
setMeasuredDimension(width, height);
if (view != null && overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS) {
final boolean fit = (vertical && (!hasHeightSize || height < heightSize))
|| (!vertical && (!hasWidthSize || width < widthSize));
ViewCompat.setOverScrollMode(view, fit ? ViewCompat.OVER_SCROLL_NEVER : ViewCompat.OVER_SCROLL_ALWAYS);
}
}
private void logMeasureWarning(int child) {
if (BuildConfig.DEBUG) {
Log.w("MyLinearLayoutManager", "Can't measure child #" + child + ", previously used dimensions will be reused." +
"To remove this message either use #setChildSize() method or don't run RecyclerView animations");
}
}
private void initChildDimensions(int width, int height, boolean vertical) {
if (childDimensions[CHILD_WIDTH] != 0 || childDimensions[CHILD_HEIGHT] != 0) {
// already initialized, skipping
return;
}
if (vertical) {
childDimensions[CHILD_WIDTH] = width;
childDimensions[CHILD_HEIGHT] = childSize;
} else {
childDimensions[CHILD_WIDTH] = childSize;
childDimensions[CHILD_HEIGHT] = height;
}
}
@Override
public void setOrientation(int orientation) {
// might be called before the constructor of this class is called
//noinspection ConstantConditions
if (childDimensions != null) {
if (getOrientation() != orientation) {
childDimensions[CHILD_WIDTH] = 0;
childDimensions[CHILD_HEIGHT] = 0;
}
}
super.setOrientation(orientation);
}
public void clearChildSize() {
hasChildSize = false;
setChildSize(DEFAULT_CHILD_SIZE);
}
public void setChildSize(int childSize) {
hasChildSize = true;
if (this.childSize != childSize) {
this.childSize = childSize;
requestLayout();
}
}
private void measureChild(RecyclerView.Recycler recycler, int position, int widthSize, int heightSize, int[] dimensions) {
final View child;
try {
child = recycler.getViewForPosition(position);
} catch (IndexOutOfBoundsException e) {
if (BuildConfig.DEBUG) {
Log.w("MyLinearLayoutManager", "MyLinearLayoutManager doesn't work well with animations. Consider switching them off", e);
}
return;
}
final RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) child.getLayoutParams();
final int hPadding = getPaddingLeft() + getPaddingRight();
final int vPadding = getPaddingTop() + getPaddingBottom();
final int hMargin = p.leftMargin + p.rightMargin;
final int vMargin = p.topMargin + p.bottomMargin;
// we must make insets dirty in order calculateItemDecorationsForChild to work
makeInsetsDirty(p);
// this method should be called before any getXxxDecorationXxx() methods
calculateItemDecorationsForChild(child, tmpRect);
final int hDecoration = getRightDecorationWidth(child) + getLeftDecorationWidth(child);
final int vDecoration = getTopDecorationHeight(child) + getBottomDecorationHeight(child);
final int childWidthSpec = getChildMeasureSpec(widthSize, hPadding + hMargin + hDecoration, p.width, canScrollHorizontally());
final int childHeightSpec = getChildMeasureSpec(heightSize, vPadding + vMargin + vDecoration, p.height, canScrollVertically());
child.measure(childWidthSpec, childHeightSpec);
dimensions[CHILD_WIDTH] = getDecoratedMeasuredWidth(child) + p.leftMargin + p.rightMargin;
dimensions[CHILD_HEIGHT] = getDecoratedMeasuredHeight(child) + p.bottomMargin + p.topMargin;
// as view is recycled let's not keep old measured values
makeInsetsDirty(p);
recycler.recycleView(child);
}
private static void makeInsetsDirty(RecyclerView.LayoutParams p) {
if (!canMakeInsetsDirty) {
return;
}
try {
if (insetsDirtyField == null) {
insetsDirtyField = RecyclerView.LayoutParams.class.getDeclaredField("mInsetsDirty");
insetsDirtyField.setAccessible(true);
}
insetsDirtyField.set(p, true);
} catch (NoSuchFieldException e) {
onMakeInsertDirtyFailed();
} catch (IllegalAccessException e) {
onMakeInsertDirtyFailed();
}
}
private static void onMakeInsertDirtyFailed() {
canMakeInsetsDirty = false;
if (BuildConfig.DEBUG) {
Log.w("MyLinearLayoutManager", "Can't make LayoutParams insets dirty, decorations measurements might be incorrect");
}
}
}
Run Code Online (Sandbox Code Playgroud)
Ran*_*mar 11
试试这个.答案很晚.但未来肯定会帮助任何人.
将Scrollview设置为NestedScrollView
<android.support.v4.widget.NestedScrollView>
<android.support.v7.widget.RecyclerView>
</android.support.v7.widget.RecyclerView>
</android.support.v4.widget.NestedScrollView>
Run Code Online (Sandbox Code Playgroud)
在您的Recyclerview中
recyclerView.setNestedScrollingEnabled(false);
recyclerView.setHasFixedSize(false);
Run Code Online (Sandbox Code Playgroud)
将此行添加到您的RecyclerViewxml 视图中:
android:nestedScrollingEnabled="false"
Run Code Online (Sandbox Code Playgroud)
并且您的RecyclerView将以灵活的高度平滑滚动。
希望能帮助到你。
如果你把RecyclerView里面NestedScrollView并启用recyclerView.setNestedScrollingEnabled(false);,滚动将运作良好.
但是,有一个问题
RecyclerView不回收
例如,您的RecyclerView(内部NestedScrollView或ScrollView)有100项.
当Activity启动,100项将创建(onCreateViewHolder和onBindViewHolder100项目都将在同一时间调用).
例如,对于每个项目,您将从API =>活动创建加载大图像 - >将加载100个图像.
它使启动活动缓慢和滞后.
可能的解决方案:
- 考虑使用RecyclerView多种类型.
但是,如果在你的情况下,只有少数项目RecyclerView和回收或不回收不会影响性能很多,你可以使用RecyclerView内部ScrollView简单
看来确实NestedScrollView解决了问题。
我已经使用此布局进行了测试:
<android.support.v4.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dummy_text" />
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.v7.widget.CardView>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
Run Code Online (Sandbox Code Playgroud)
而且它的工作没有问题。
| 归档时间: |
|
| 查看次数: |
165711 次 |
| 最近记录: |