ScrollView嵌套ListView,GridView,RecyclerView以及RecyclerView嵌套RecyclerView显示不全的解决方法

摘要:开发中经常遇到ScrollView嵌套ListView,GridView,或者RecyclerView嵌套RecyclerView的情况,常常会出现显示不全的现象,下面提供几种不同的解决方法


1.在不是很复杂的布局的情况下,尽量不嵌套,使用添加头布局尾布局的方式进行实现;RecyclerView的添加方式可以参考张鸿洋的博客


2.网上比较流行的是自定义ListView和RecyclerView的LayoutManager

(1)自定义ListView,GridView,重写onMeasure方法

ListView:

public class MyListView extends ListView {
    public MyListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int mExpandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, mExpandSpec);
    }

}

这种方法是将ListView的允许高度设置为极大值,比较简单,但是有一个后果是会造成adapter的getView方法调用多次,也就是多次重绘计算高度.导致的结果就是加载卡顿,如果item中有EditText的话可能会造成数据显示混乱

GridView也类似:

public class MyGridView extends GridView {
    public MyGridView(Context context) {
        super(context);
    }

    public MyGridView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyGridView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE>>2,MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec,expandSpec);
    }
}

(2)自定义RecyclerView的LayoutManager

LinearLayoutManager:

public class MyLinearLayoutManager extends LinearLayoutManager {

    private static final String TAG = MyLinearLayoutManager.class.getSimpleName();

    public MyLinearLayoutManager(Context context) {
        super(context);
    }

    public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    private int[] mMeasuredDimension = new int[2];

    @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 < getItemCount(); i++) {
            measureScrapChild(recycler, i,
                    View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                    View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                    mMeasuredDimension);

            if (getOrientation() == HORIZONTAL) {
                width = width + mMeasuredDimension[0];
                if (i == 0) {
                    height = mMeasuredDimension[1];
                }
            } else {
                height = height + mMeasuredDimension[1];
                if (i == 0) {
                    width = mMeasuredDimension[0];
                }
            }
        }
        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);
    }

    private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
                                   int heightSpec, int[] measuredDimension) {
        try {
            View view = recycler.getViewForPosition(0);

            if (view != null) {
                RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();

                int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
                        getPaddingLeft() + getPaddingRight(), p.width);

                int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
                        getPaddingTop() + getPaddingBottom(), p.height);

                view.measure(childWidthSpec, childHeightSpec);
                measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
                measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
                recycler.recycleView(view);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        }
    }
}
GridLayoutManager:
public class MyGridLayoutManager extends GridLayoutManager {

	private Context context;
	
	public MyGridLayoutManager(Context context, int spanCount) {
		super(context, spanCount);
		this.context = context;
	}

	public MyGridLayoutManager(Context context, int spanCount,
							   int orientation, boolean reverseLayout) {
		super(context, spanCount, orientation, reverseLayout);
		this.context = context;
	}

	private int[] mMeasuredDimension = new int[2];

	@SuppressLint("NewApi")
	@Override
	public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
		final int heightMode = View.MeasureSpec.getMode(heightSpec);
		final int widthSize = View.MeasureSpec.getSize(widthSpec);
		final int heightSize = View.MeasureSpec.getSize(heightSpec);

		int height = 0;
		int count = getItemCount();
		int span = getSpanCount();
		
		for (int i = 0; i < count; i++) {
			measureScrapChild(recycler, i, View.MeasureSpec.makeMeasureSpec(i,
					View.MeasureSpec.UNSPECIFIED),
					View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), mMeasuredDimension);
			
			if (getOrientation() == HORIZONTAL) {
				if (i == 0) {
					height = mMeasuredDimension[1];
				}
			} else {
				if (i % span == 0) {
					height = (int) (height + mMeasuredDimension[1]);
				}
			}
		}

		switch (heightMode) {
		case View.MeasureSpec.EXACTLY:
			height = heightSize;
		case View.MeasureSpec.AT_MOST:
		case View.MeasureSpec.UNSPECIFIED:
		}
		setMeasuredDimension(widthSize, height);
	}

	private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec, int heightSpec, int[] measuredDimension) {
		if (position < getItemCount()) {
			try {
				View view = recycler.getViewForPosition(position);// fix // 动态添加时报IndexOutOfBoundsException
				if (view != null) {
					RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
					int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, getPaddingLeft() + getPaddingRight(), p.width);
					int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, getPaddingTop() + getPaddingBottom(), p.height);
					view.measure(childWidthSpec, childHeightSpec);
					measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
					measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
					recycler.recycleView(view);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}

通过LayoutManager去测量条目的宽高然后设置出高度.但是这种方法我在4.1的手机上运行正常,但是在7.0手机上依然显示不全(具体不知道哪个版本以上就不能完全显示);解决方法就是在RecyclerView外面包裹一层RealativeLayout;下面是代码修改前后的对比.

修改之前,4.1能显示完整,而7.0不能显示完整:

<ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
   <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">
                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="100dp"
                    android:text="文本"/>
                <android.support.v7.widget.RecyclerView
                    android:id="@+id/rv"
                    android:layout_width="wrap_content"
                    android:layout_height="0dp"
                    android:layout_weight="1">
                </android.support.v7.widget.RecyclerView>
            </LinearLayout>
</ScrollView>
修改之后完整显示:

<ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">
                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="100dp"
                    android:text="我是文本"/>
                <android.support.v7.widget.RecyclerView
                    android:id="@+id/rv"
                    android:layout_width="wrap_content"
                    android:layout_height="0dp"
                    android:layout_weight="1">
                </android.support.v7.widget.RecyclerView>
            </LinearLayout>
        </RelativeLayout>
</ScrollView>

3.手动设置LayoutParams,适用于题目上的任何一种情况

ListView和RecyclerView都能去获取item,然后测量出高度进行累加得到总高度再设置给自己,如果每条的高度已知并且高度都一致,那可以直接计算(注意加上分割线高度);如果
是GridView或者多列RecyclerView.需要除以每行的条目数

listview:


LinearLayout.LayoutParams params = (LayoutParams) listview.getLayoutParams();
int totalHeight = 0;
for (int i = 0; i < list.size(); i++) {//list为数据集合,也可以通过adapter.getCount()得到
	View listItem = buyAdapter.getView(i, null, listview);
	listItem.measure(0, 0);
	int perHeight = listItem.getMeasuredHeight();
	totalHeight += perHeight;
}
float dividerHeight = listview.getDividerHeight();
params.height = (int) ((list.size()-1)*dividerHeight+totalHeight);
listview.setLayoutParams(params);

RecyclerView:


int h = 0;
for (int i = 0; i < list.size(); i++) {
	View view = recyclerView.getLayoutManager().getChildAt(0);
	view.measure(0, 0);
	h += view.getMeasuredHeight();
}
ViewGroup.LayoutParams layoutParams = recyclerView.getLayoutParams();
layoutParams.height = h;
recyclerView.setLayoutParams(layoutParams);

注意:当视图未初始化完成时直接设置会报空指针,比如在onCreate方法中去设置,因为itemView还没有完全初始化,这时候可以通过设置view渲染完成的监听实现,以RecyclerView为例:


rootview.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {//rootView为根ViewGroup
	@Override
	public void onGlobalLayout() {
		rootview.getViewTreeObserver().removeGlobalOnLayoutListener(this);
		int h = 0;
		for (int i = 0; i < list.size(); i++) {
			View view = rv.getLayoutManager().getChildAt(0);
			view.measure(0, 0);
			h += view.getMeasuredHeight();
		}
		ViewGroup.LayoutParams layoutParams = rv.getLayoutParams();
		layoutParams.height = h;
		rv.setLayoutParams(layoutParams);
	}
}); 










评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值