幾個問題,簡單搞定高度。
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