如何在Android5.1系统上实现RecyclerView高度自适应

之前在5.1系统上做的一个项目,碰到过RecyclerView高度自适应的问题,在布局文件中写的是android:height="wrap_content",但是实际显示的高度会填满整个父容器。
参考文章http://www.jianshu.com/p/4b8d6e5004d5,翻了一下源码,发现onMeasure里面多了mLayout.mAutoMeasure这个boolean值。在谷歌的官方文档里面可以看到,这个值是在23.2.0版本也就是6.0系统之后才加入的。然后我在6.0系统上测试,RecyclerView可以用wrap_content来实现高度自适应的,代码内可以在初始化的时候调用LayoutManager.setAutoMeasureEnabled(true)。如果无效的话,还可以给RecyclerView外面再套一层父容器,用父容器来限制RecyclerView的宽高。

大家都知道Android是在22版本引入的RecyclerView这个组件,而根据上文所述,自适应只有在23版本之后才能实现,那么如何在5.1系统上也实现RecyclerView的高度自适应呢?
参考文章:http://blog.csdn.net/singleton1900/article/details/48369635
具体思路就是重写LayoutManager的onMeasure方法,根据子view的个数和LayoutParams来计算RecyclerView的宽高,最后调用setMeasuredDimension()

在实际应用当中还需要对上面的代码做一些修改

    private int getCount() {
        //可以通过修改MAX_LENGTH的值来限制需要显示的item的最大个数
        if(getItemCount() > MAX_LENGTH) {
            return MAX_LENGTH;
        } else {
            return getItemCount();
        }
    }

    @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 < getCount(); i++) {
            //计算第i个item的宽高
            measureScrapChild(recycler, i,
                    View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                    View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                    mMeasuredDimension);
            //根据gravity来选择增加RecyclerView的宽度或高度
            if (getOrientation() == HORIZONTAL) {
                width = width + mMeasuredDimension[0];
                if (i == 0) {
                    height = mMeasuredDimension[1];
                }
            } else {
                height = height + mMeasuredDimension[1];
                if (i == 0) {
                    width = mMeasuredDimension[0];
                }
            }
            //如果子view的宽度或高度已经超过了父容器的宽度或高度,那么就不用继续计算了,跳出循环
            //防止因为itemCount个数过大(例如Integer.MAX_VALUE)导致出现anr问题
            //因为在measureScrapChild方法中,会调用item的measure方法和recycler.getViewForPosition
            //后者再深入跟踪可以看到调用了adapter的onBindViewHolder回调
            //如果在这个onBindViewHolder中进行绘制、网络请求等耗时操作(通常在实际开发中都会有)
            //那么多次循环累积之后就会导致程序卡顿,最后进程被系统杀死
            if (height >= heightSize || width >= widthSize) {
                break;
            }
        }

        //如果子view的宽度或高度已经超过了父容器的宽度或高度,就不需要做自适应了,直接调用父类的onMeasure即可
        if (height < heightSize && width < widthSize) {

            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);
        } else {
            super.onMeasure(recycler, state, widthSpec, heightSpec);
        }
    }

你可能感兴趣的:(Android)