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

相關文章
相關標籤/搜索