最近公司项目中用到了ScrollView去嵌套recycleView, 最开始我天真的把recycleView直接放入scrollView中,结果可想而知,什么都不显示,瞬间懵逼,我心想应该是和嵌套ListView差不多吧,看来需要重写recycleView中onMeasure()方法,

像这样:

 @Override
protected void onMeasure(int widthSpec, int heightSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,MeasureSpec.AT_MOST);
super.onMeasure(widthSpec, expandSpec);
}

结果:运行还是什么都没有。

   没办法上网搜索解决方案,一搜看来网友很多跟我有一样的需求,网上也有大神提供的解决方案

例如这样:

 <RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants">
<android.support.v7.widget.RecyclerView
android:id="@+id/menuRv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/margin_16"
android:layout_marginRight="@dimen/margin_16"/>
</RelativeLayout>

我的结果: 运行还是原来一样没有任何反应.

注意: 你们可以试试,说是解决在Android6.0显示不全的问题,如果成功了那祝贺你们,如果没有成功的话就跟着我往下看。

例如这样: 重写LinearLayoutManager,来计算每个item进行显示

public class FullyLinearLayoutManager extends LinearLayoutManager {

    private static final String TAG = FullyLinearLayoutManager.class.getSimpleName();

    public FullyLinearLayoutManager(Context context) {
super(context);
} public FullyLinearLayoutManager(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); Log.i(TAG, "onMeasure called. \nwidthMode " + widthMode + " \nheightMode " + heightSpec + " \nwidthSize "
+ widthSize + " \nheightSize " + heightSize + " \ngetItemCount() " + getItemCount()); 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) {
try {
View view = recycler.getViewForPosition(0);// fix
// 动态添加时报IndexOutOfBoundsException 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);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
}

结果:运行成功 好开心,但是发现显示不完全,item越多这个就无法显示完全,不是最后一个被挡了,就是明明已经是最后一个item了,可是滚动条还没有结束,再继续往下滚,下面没有数据而是空了一大片白。没办法这是算失败了。

注意: 你们可以试试,说是可以解决item不显示问题和显示不全问题,结果我是失败了,所以你们如果用这个方法成功了那么恭喜,如果没有请看接下来。

  国内目前大多数都是上面几种解决方案,很不幸我逐一试了都不行,最后一个最有希望结果还是失败了,没办法,只能国外找了终于在国外的找到了解决方法,不知道你们是不是用了这个,反正我是用了这个了,结果成功了,很满意。

接下来进入正题:

1.废话不多说先上个xml看看

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F2F2F2"
android:orientation="vertical" > <include layout="@layout/include_header_view" /> <ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
>
<LinearLayout
android:id="@+id/li_show_recyclerView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" > <TextView
android:id="@+id/tvHeaderStormTitle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_margin="1dp"
android:background="@color/white"
android:gravity="center_vertical"
android:minHeight="40dp"
android:paddingLeft="15dp"
android:paddingRight="10dp"
android:textColor="#808080"
android:textSize="16sp" /> <TextView
android:id="@+id/tvHeaderStormEditContent"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_margin="1dp"
android:background="@color/white"
android:padding="5dp"
android:textColor="#404040"
android:textSize="17sp" /> <android.support.v7.widget.RecyclerView
android:id="@+id/id_recyclerview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp" />
</LinearLayout>
</ScrollView> </LinearLayout>

看完xml后是不是很失望发现和你们的并没有什么不一样 ,但我既然贴了代码就要说明白,这里1.最好为ScrollView加上android:fillViewport="true"这个属性。 2.最好将RecyclerView的高度属性 设成android:layout_height="wrap_content",这2点是建议,

我不知道这两点会不会影响接下来的事,但最好这样设置吧,我没有试过你们可以试试。

2.接下来看java代码(这里只是截取了用到recycleView的代码)

FullyLinearLayoutManager mLayoutManager = new FullyLinearLayoutManager(this);
@Override
public void initView() {
mReplyList = new ArrayList<BeanHeaderStorm>();
mRecyclerView = (RecyclerView) findViewById(R.id.id_recyclerview);// 设置item动画
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
if (null == mRecyclerAdapter) {
       //设置布局样式
       mRecyclerView.setLayoutManager(mLayoutManager);
     //设置分割线
HeaderStormItemDiratcion diraction = new HeaderStormItemDiratcion(1);
mRecyclerView.addItemDecoration(diraction);
        //设置适配器
mRecyclerAdapter = new AdapterHeaderStorm(ActivityHeaderStormShow.this, mReplyList);
       //设置item点击事件
mRecyclerAdapter.setOnItemClickLitener(this);
mRecyclerView.setAdapter(mRecyclerAdapter); } else {
      //刷新,这里的适配器是我自己单独写的别直接复制这段代码
mRecyclerAdapter.notifyView(mReplyList);
} }

这里看完是不是发现好像还是没有什么区别,的确是没有区别,但注意里面红色代码标注的地方,没错用了名字和前面重写LinearLayoutManager用的名字一样,但内部是不一样的,接下来就来看看这里面到底改变了什么

  完全重新重写的FullyLinearLayoutManager如下:

import java.lang.reflect.Field;
import com.loopj.android.http.BuildConfig;
import android.content.Context;
import android.graphics.Rect;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
/**
* 重写 LinearLayoutManager 为了ScrollView可以显示RecyclerView 垂直布局
*
* @author M.Z
*/
public class FullyLinearLayoutManager extends 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(); public FullyLinearLayoutManager(Context context) {
super(context);
this.view = null;
} public FullyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
this.view = null;
} public FullyLinearLayoutManager(RecyclerView view) {
super(view.getContext());
this.view = view;
this.overScrollMode = ViewCompat.getOverScrollMode(view);
} public FullyLinearLayoutManager(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("LinearLayoutManager", "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("LinearLayoutManager", "LinearLayoutManager 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("LinearLayoutManager", "Can't make LayoutParams insets dirty, decorations measurements might be incorrect");
}
}
}

忽然一看,我的天是不是改变太大了,对没错,这就是终极的解决方案,最上面的国内的重写代码思路是对的,但还是处理的不够全面,而这个应该是一个外国的大牛写的,写的很详细全面,我拿来试了试,果然很完美的解决了我的问题.

我用了评论区2楼的方案写的demo :DEMO.ZIP

总结:自身的技术还是太嫩了,完全不够用,自身解决问题能力不行,作为一个伸手党我们还是别忘了还要真正的去学习别人的思路想法和技术构思,

   这里我贴出写这个代码的地址:https://github.com/serso/android-linear-layout-manager/blob/master/lib/src/main/java/org/solovyev/android/views/llm/LinearLayoutManager.java

   提问的出处的地址:http://stackoverflow.com/questions/27083091/recyclerview-inside-scrollview-is-not-working

ScrollView中嵌套recycleView 出现的不显示,显示不全,终极解决方案的更多相关文章

  1. ScrollView中嵌套ListView时,listview高度显示的问题

    方法一:直接更改listview的控件高度,动态获取(根据条目和每个条目的高度获取) 前几天因为项目的需要,要在一个ListView中放入另一个ListView,也即在一个ListView的每个Lis ...

  2. ScrollView中嵌套ListView显示

    想要ScrollView中嵌套显示ListView 需要自定义ListView 并重写onMeasure方法 重新计算  heightMeasureSpec的高度 int newHeight = Me ...

  3. scrollview 中嵌套多个listview的最好解决办法

    在scrollview中嵌套多个listview的显示问题. 只需要调用如下的方法传入listview和adapter数据即可. /** * 动态设置ListView组建的高度 */ public s ...

  4. Android 如何在ScrollView中嵌套ListView

    前几天因为项目的需要,要在一个ListView中放入另一个ListView,也即在一个ListView的每个ListItem中放入另外一个ListView.但刚开始的时候,会发现放入的小ListVie ...

  5. 解决ScrollView中嵌套ListView滚动效果冲突问题

    在ScrollView中嵌套使用ListView,ListView只会显示一行到两行的数据.起初我以为是样式的问题,一直在对XML文件的样 式进行尝试性设置,但始终得不到想要的效果.后来在网上查了查, ...

  6. RecyclerFullyManagerDemo【ScrollView里嵌套Recycleview的自适应高度功能】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 对于Recyclerview自己的LinearLayoutManager和GridLayoutManager,在版本23.2.0之后 ...

  7. Android实战技巧:如何在ScrollView中嵌套ListView

    前几天因为项目的需要,要在一个ListView中放入另一个ListView,也即在一个ListView的每个ListItem中放入另外一个ListView.但刚开始的时候,会发现放入的小ListVie ...

  8. Android -- 在ScrollView中嵌套ListView

    在做一个工程,这个工程的布局可以相当的复杂,最外面是ScrollView,在ScrollView里面有两个Listview,这下好了,布局出来了,放在机子上跑,卡得想死有木有,信息乱跑乱出现,表示非常 ...

  9. ScrollView中嵌套ListView的问题

    网上关于怎样在ScrollView中嵌套ListView的讨论有很多,我大概是搜索了一下,简单总结如下: 1.不要在ScrollView中嵌套ListView a.用一个LinearLayout来代替 ...

随机推荐

  1. Java导入的项目乱码怎么解决?(Ⅱ)

    1.首先 打开  >>  Eclipse或Myeclipse.(我用的是Myeclipse) 2.打开  >>  Window  >>  Preferences  ...

  2. (转)Div和Table的区别

    原文:http://www.cnblogs.com/lovebear/archive/2012/04/18/2456081.html Div与table的区别 1:速度和加载方式方面的区别 div 和 ...

  3. PostgreSQL-角色、库、模式、表

    由于不了解postgresql的psql工具,安装完数据库后就直接用pgadmin或navicat来连接操作,在确认初始化后的库中默认有些什么东西后竟然一直无处下手,在还没有了解pg大致体系的情况下搞 ...

  4. WPF 自定义雷达图

    自定义雷达图表如下: Git下载地址:https://github.com/Kybs0/RadarChartControl 1.创建UserControl,名为“RadarChartControl” ...

  5. 使用GDB调试程序

    GDB(GNU Debuger)是Linux中的一个功能强大的调试器.GDB可以让你查看程序的内部结构,打印变量值,设置断点,单步调试源码,运行时修改变量值,路径跟踪,线程切换,显示汇编代码等. 编译 ...

  6. netty4虚拟内存不断飙升

    去年升级过一个老的netty3的程序到netty4,近期突然注意到一个问题,就是这个程序随着时间虚拟内存会不断升高.之前升级的时候担心存在内存泄露,所以还特意用jstate跟踪过gc回收的情况,并没有 ...

  7. spring类型自动转换——@InitBinder和Converter

    spring有2种类型转换器,一种是propertyEditor,一种是Converter.虽然都是类型转换,但是还是有细微差别. 所以这里以一个例子的形式来分析一下这2种类型转换的使用场景和差别. ...

  8. JavaScript 基础回顾——对象

    JavaScript是基于对象的解释性语言,全部数据都是对象.在 JavaScript 中并没有 class 的概念,但是可以通过对象和类的模拟来实现面向对象编程. 1.对象 在JavaScript中 ...

  9. 第39课 Qt中的事件处理(下)

    1. 事件的传递过程 (1)操作系统检测到用户动作时,会产生一条系统消息,该消息被发送到Qt应用程序 (2)Qt应用程序收到系统消息后,将其转化为一个对应的QEvent事件对象,并调用QObject: ...

  10. Hibernate延迟加载、三种状态、脏检查 缓存

    一.持久化对象的唯一标识 java中按内存地址不同区分同一个类的不同对象,关系数据库用主键区分同一条记录,Hibernate使用OID来建立内存中的对象和数据库中记录的对应关系 什么是OID? 解析: ...