几个问题,简单搞定高度。
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