RecyclerView自适应item高度

几个问题,简单搞定高度。
java

首先,须要自定义一个LinearLayoutManager,这里RecyclerView在onMeasure回调中会调用LinearLayoutManager的onMeasure方法,因此须要在LinearLayoutManager的onMeasure中作一些高度设置的处理,大体内容:app

@Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec,int heightSpec) {
    View view = recycler.getViewForPosition(0);
    measureChild(view, widthSpec, heightSpec);
    int measuredWidth = View.MeasureSpec.getSize(widthSpec);
    int measuredHeight = view.getMeasuredHeight();
    setMeasuredDimension(measuredWidth, measuredHeight);
}

两个注意点:这里获取了view的高度,也就是item布局的高度,因此item的布局须要设定固定的高度,不然获取为0。其次,ide

mLayoutManager.setAutoMeasureEnabled(false)
mList.setHasFixedSize(false)

这两个设置不能少,不然报错,这里和RecyclerView.onMeasure中的调用顺序有关,源码:布局

protected void onMeasure(int widthSpec, int heightSpec) {
    if (mLayout == null) {
        defaultOnMeasure(widthSpec, heightSpec);
        return;
    }
    if (mLayout.mAutoMeasure) {     //这里为true会进入该分支
        final int widthMode = MeasureSpec.getMode(widthSpec);
        final int heightMode = MeasureSpec.getMode(heightSpec);
        final boolean skipMeasure = widthMode == MeasureSpec.EXACTLY
                && heightMode == MeasureSpec.EXACTLY;
        mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);   //在调用mLayout的onMeasure方法时(被自定义复写的方法),mState.mItemCount为0,形成越界异常
        if (skipMeasure || mAdapter == null) {
            return;
        }
        if (mState.mLayoutStep == State.STEP_START) {
            dispatchLayoutStep1();
        }
        // set dimensions in 2nd step. Pre-layout should happen with old dimensions for
        // consistency
        mLayout.setMeasureSpecs(widthSpec, heightSpec);
        mState.mIsMeasuring = true;
        dispatchLayoutStep2();

        // now we can get the width and height from the children.
        mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);

        // if RecyclerView has non-exact width and height and if there is at least one child
        // which also has non-exact width & height, we have to re-measure.
        if (mLayout.shouldMeasureTwice()) {
            mLayout.setMeasureSpecs(
                    MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
                    MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
            mState.mIsMeasuring = true;
            dispatchLayoutStep2();
            // now we can get the width and height from the children.
            mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
        }
    } else {
        if (mHasFixedSize) {   //这里不设置为false会形成与上面相同的问题
            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
            return;
        }
        // custom onMeasure
        if (mAdapterUpdateDuringMeasure) {
            eatRequestLayout();
            processAdapterUpdatesAndSetAnimationFlags();

            if (mState.mRunPredictiveAnimations) {
                mState.mInPreLayout = true;
            } else {
                // consume remaining updates to provide a consistent state with the layout pass.
                mAdapterHelper.consumeUpdatesInOnePass();
                mState.mInPreLayout = false;
            }
            mAdapterUpdateDuringMeasure = false;
            resumeRequestLayout(false);
        }

        if (mAdapter != null) {
            mState.mItemCount = mAdapter.getItemCount();
        } else {
            mState.mItemCount = 0;
        }
        eatRequestLayout();
        mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);   //在这里调用时mState.mItemCount才会有值,这与mLayout中获取当前item布局的方式有关:View view = recycler.getViewForPosition(0);
        resumeRequestLayout(false);
        mState.mInPreLayout = false; // clear
    }
}

见注释。spa

相关文章
相关标签/搜索