ScrollView嵌套ListView,GridView,RecyclerView以及RecyclerView嵌套RecyclerView显示不全的解决方法

摘要:开发中经常遇到ScrollView嵌套ListView,GridView,或者RecyclerView嵌套RecyclerView的情况,常常会出现显示不全的现象,下面提供几种不同的解决方法

1.在不是很复杂的布局的情况下,尽量不嵌套,使用添加头布局尾布局的方式进行实现;RecyclerView的添加方式可以参考张鸿洋的博客

2.网上比较流行的是自定义ListView和RecyclerView的LayoutManager
(1)自定义ListView,GridView,重写onMeasure方法
ListView:

public class MyListView extends ListView {  
    public MyListView(Context context, AttributeSet attrs) {  
        super(context, attrs);  
    }  
  
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
        int mExpandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);  
        super.onMeasure(widthMeasureSpec, mExpandSpec);  
    }  
  
}  

这种方法是将ListView的允许高度设置为极大值,比较简单,但是有一个后果是会造成adapter的getView方法调用多次,也就是多次重绘计算高度.导致的结果就是加载卡顿,如果item中有EditText的话可能会造成数据显示混乱
GridView也类似:

public class MyGridView extends GridView {  
    public MyGridView(Context context) {  
        super(context);  
    }  
  
    public MyGridView(Context context, AttributeSet attrs) {  
        super(context, attrs);  
    }  
  
    public MyGridView(Context context, AttributeSet attrs, int defStyleAttr) {  
        super(context, attrs, defStyleAttr);  
    }  
  
    @Override  
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE>>2,MeasureSpec.AT_MOST);  
        super.onMeasure(widthMeasureSpec,expandSpec);  
    }  
}  

(2)自定义RecyclerView的LayoutManager
LinearLayoutManager:

public class MyLinearLayoutManager extends LinearLayoutManager {  
  
    private static final String TAG = MyLinearLayoutManager.class.getSimpleName();  
  
    public MyLinearLayoutManager(Context context) {  
        super(context);  
    }  
  
    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) {  
        try {  
            View view = recycler.getViewForPosition(0);  
  
            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 {  
        }  
    }  
}  

GridLayoutManager:**[java]** [view plain](http://blog.csdn.net/kinly1993/article/details/54616348#) [copy](http://blog.csdn.net/kinly1993/article/details/54616348#)

public class MyGridLayoutManager extends GridLayoutManager {  
  
    private Context context;  
      
    public MyGridLayoutManager(Context context, int spanCount) {  
        super(context, spanCount);  
        this.context = context;  
    }  
  
    public MyGridLayoutManager(Context context, int spanCount,  
                               int orientation, boolean reverseLayout) {  
        super(context, spanCount, orientation, reverseLayout);  
        this.context = context;  
    }  
  
    private int[] mMeasuredDimension = new int[2];  
  
    @SuppressLint("NewApi")  
    @Override  
    public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {  
        final int heightMode = View.MeasureSpec.getMode(heightSpec);  
        final int widthSize = View.MeasureSpec.getSize(widthSpec);  
        final int heightSize = View.MeasureSpec.getSize(heightSpec);  
  
        int height = 0;  
        int count = getItemCount();  
        int span = getSpanCount();  
          
        for (int i = 0; i < count; i++) {  
            measureScrapChild(recycler, i, View.MeasureSpec.makeMeasureSpec(i,  
                    View.MeasureSpec.UNSPECIFIED),  
                    View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), mMeasuredDimension);  
              
            if (getOrientation() == HORIZONTAL) {  
                if (i == 0) {  
                    height = mMeasuredDimension[1];  
                }  
            } else {  
                if (i % span == 0) {  
                    height = (int) (height + mMeasuredDimension[1]);  
                }  
            }  
        }  
  
        switch (heightMode) {  
        case View.MeasureSpec.EXACTLY:  
            height = heightSize;  
        case View.MeasureSpec.AT_MOST:  
        case View.MeasureSpec.UNSPECIFIED:  
        }  
        setMeasuredDimension(widthSize, height);  
    }  
  
    private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec, int heightSpec, int[] measuredDimension) {  
        if (position < getItemCount()) {  
            try {  
                View view = recycler.getViewForPosition(position);// 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();  
            }  
        }  
    }  
}  

通过LayoutManager去测量条目的宽高然后设置出高度.但是这种方法我在4.1的手机上运行正常,但是在7.0手机上依然显示不全(具体不知道哪个版本以上就不能完全显示);解决方法就是在RecyclerView外面包裹一层RealativeLayout;下面是代码修改前后的对比.
修改之前,4.1能显示完整,而7.0不能显示完整:


<ScrollView  
        android:layout_width="match_parent"  
        android:layout_height="match_parent">  
   <LinearLayout  
                android:layout_width="match_parent"  
                android:layout_height="match_parent"  
                android:orientation="vertical">  
                <TextView  
                    android:layout_width="match_parent"  
                    android:layout_height="100dp"  
                    android:text="文本"/>  
                <android.support.v7.widget.RecyclerView  
                    android:id="@+id/rv"  
                    android:layout_width="wrap_content"  
                    android:layout_height="0dp"  
                    android:layout_weight="1">  
                </android.support.v7.widget.RecyclerView>  
            </LinearLayout>  
</ScrollView>  

修改之后完整显示:

**[java]** [view plain](http://blog.csdn.net/kinly1993/article/details/54616348#) [copy](http://blog.csdn.net/kinly1993/article/details/54616348#)

<ScrollView  
        android:layout_width="match_parent"  
        android:layout_height="match_parent">  
        <RelativeLayout  
            android:layout_width="match_parent"  
            android:layout_height="wrap_content">  
            <LinearLayout  
                android:layout_width="match_parent"  
                android:layout_height="match_parent"  
                android:orientation="vertical">  
                <TextView  
                    android:layout_width="match_parent"  
                    android:layout_height="100dp"  
                    android:text="我是文本"/>  
                <android.support.v7.widget.RecyclerView  
                    android:id="@+id/rv"  
                    android:layout_width="wrap_content"  
                    android:layout_height="0dp"  
                    android:layout_weight="1">  
                </android.support.v7.widget.RecyclerView>  
            </LinearLayout>  
        </RelativeLayout>  
</ScrollView>  

3.手动设置LayoutParams,适用于题目上的任何一种情况
ListView和RecyclerView都能去获取item,然后测量出高度进行累加得到总高度再设置给自己,如果每条的高度已知并且高度都一致,那可以直接计算(注意加上分割线高度);如果是GridView或者多列RecyclerView.需要除以每行的条目数
listview:

LinearLayout.LayoutParams params = (LayoutParams) listview.getLayoutParams();  
int totalHeight = 0;  
for (int i = 0; i < list.size(); i++) {//list为数据集合,也可以通过adapter.getCount()得到  
    View listItem = buyAdapter.getView(i, null, listview);  
    listItem.measure(0, 0);  
    int perHeight = listItem.getMeasuredHeight();  
    totalHeight += perHeight;  
}  
float dividerHeight = listview.getDividerHeight();  
params.height = (int) ((list.size()-1)*dividerHeight+totalHeight);  
listview.setLayoutParams(params);  

RecyclerView:

**[java]** [view plain](http://blog.csdn.net/kinly1993/article/details/54616348#) [copy](http://blog.csdn.net/kinly1993/article/details/54616348#)

int h = 0;  
for (int i = 0; i < list.size(); i++) {  
    View view = recyclerView.getLayoutManager().getChildAt(0);  
    view.measure(0, 0);  
    h += view.getMeasuredHeight();  
}  
ViewGroup.LayoutParams layoutParams = recyclerView.getLayoutParams();  
layoutParams.height = h;  
recyclerView.setLayoutParams(layoutParams);  

注意:当视图未初始化完成时直接设置会报空指针,比如在onCreate方法中去设置,因为itemView还没有完全初始化,这时候可以通过设置view渲染完成的监听实现,以RecyclerView为例:

rootview.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {//rootView为根ViewGroup  
    @Override  
    public void onGlobalLayout() {  
        rootview.getViewTreeObserver().removeGlobalOnLayoutListener(this);  
        int h = 0;  
        for (int i = 0; i < list.size(); i++) {  
            View view = rv.getLayoutManager().getChildAt(0);  
            view.measure(0, 0);  
            h += view.getMeasuredHeight();  
        }  
        ViewGroup.LayoutParams layoutParams = rv.getLayoutParams();  
        layoutParams.height = h;  
        rv.setLayoutParams(layoutParams);  
    }  
});   
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,832评论 25 707
  • 这篇文章分三个部分,简单跟大家讲一下 RecyclerView 的常用方法与奇葩用法;工作原理与ListView比...
    LucasAdam阅读 4,379评论 0 27
  • 又到了更新博文的时间了,最近在看一本很不错的心理学书籍,名字叫做 《拖延心理学》,封面长下面这样子 书的内容主要是...
    ec95b5891948阅读 57,453评论 38 472
  • 我喜欢大一点的外套 冬天,裹得像只大号企鹅 迈着筷子似的腿走过摇晃的街头 身后的目光看不到 前方,侧方的脸也冰冻在...
    柳雾阅读 200评论 0 0
  • 论“二十岁”的我
    奔三小青年阅读 681评论 0 3