自定义viewgroup实现自动换行的布局,同时解决自定义布局在wrapcontent下高度不能自适应的问题,plus一些关于Component重写的基础知识

一.基础知识
在这里首先介绍一下Android下自定义Component的一些基础知识,已经了解或者心急的同学可以直接跳到第二章看代码。
说到自定义Component,我想在没有什么比Google自家的SDK document解释的来的全面和准确的了,下面我们就来看看官方是怎么说的:
在SDK->API Guides->UserInterface->Custom Components一章中官方是这么说的
Android offers a sophisticated and powerful componentized model for building your UI, based on the fundamental layout classes: View andViewGroup. To start with, the platform includes a variety of prebuilt View and ViewGroup subclasses — called widgets and layouts, respectively — that you can use to construct your UI.
也就是说,基本所有我们使用的UI控件都是继承自View和ViewGroup这两个基类的,比如我们常用的布局(Linerlayout,RelativeLayout等),和一些widgets(Button,TextView等)。我们自己想要自定义控件也必须继承这两个基类(当然,你也可以直接继承已有的Layouts和widgets)。

重写ViewGroup需要注意的事项:
1.必须要重写onLayout方法,并且需要实现三个默认的构造函数,不然会加载布局时出错。
2.重写onMeasure方法。

onMeasure方法,该方法指定控件在屏幕上的大小,方法的原型:
@Override   protectedvoid onMeasure(int widthMeasureSpec,int heightMeasureSpec){
其中widthMeasureSpec和heightMeasureSpec为父布局的宽高,这两个参数是由上一层控件传入的大小,不过onMeasure传入的widthMeasureSpec和heightMeasureSpec不是一般的尺寸数值,而是将模式和尺寸组合在一起的数值。我们需要通过int mode = MeasureSpec.getMode(widthMeasureSpec)得到模式,用int size = MeasureSpec.getSize(widthMeasureSpec)得到尺寸。
mode共有三种情况,取值分别为MeasureSpec.UNSPECIFIED, MeasureSpec.EXACTLY, MeasureSpec.AT_MOST。
MeasureSpec.EXACTLY是精确尺寸,当我们将控件的layout_width或layout_height指定为具体数值时如andorid:layout_width="50dip",或者为FILL_PARENT是,都是控件大小已经确定的情况,都是精确尺寸。
MeasureSpec.AT_MOST是最大尺寸,当控件的layout_width或layout_height指定为WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。
     MeasureSpec.UNSPECIFIED是未指定尺寸,这种情况不多,一般都是父控件是AdapterView,通过measure方法传入的模式。 

     如果要想让ViewGroup在wrap_content下自适应高度。需要在onMeasure中调用setMeasuredDimension(measuredWidth,measuredHeight)把计算好的高度和宽度进行附值。

   
onLayout方法,该方法通过拿到childView的getMeasuredWidth() andgetMeasuredHeight(),用来布局所有的childView。

二.自动换行的布局(通过自定义ViewGroup实现)
废话不多说,直接上代码:

package com.example.hyydatalist.view;

import com.example.hyydatalist.constants.HyyConstants;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

public class AutoExtViewGroup extends ViewGroup {

	public AutoExtViewGroup(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		// TODO Auto-generated constructor stub
	}

	public AutoExtViewGroup(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
	}

	public AutoExtViewGroup(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
	}

	private final static int VIEW_MARGIN = 10;

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		// TODO Auto-generated method stub

		Log.d(HyyConstants.HYY_TAG, "widthMeasureSpec=" + widthMeasureSpec
				+ "heightMeasureSpec=" + heightMeasureSpec);

		int stages = 1;
		int stageHeight = 0;
		int stageWidth = 0;

		int wholeWidth = MeasureSpec.getSize(widthMeasureSpec);

		for (int i = 0; i < getChildCount(); i++) {
			final View child = getChildAt(i);
			// measure
			measureChild(child, widthMeasureSpec, heightMeasureSpec);
			stageWidth += (child.getMeasuredWidth() + VIEW_MARGIN);
			stageHeight = child.getMeasuredHeight();
			if (stageWidth >= wholeWidth) {
				stages++;
				//reset stageWidth
				stageWidth = child.getMeasuredWidth();
			}

			Log.i(HyyConstants.HYY_TAG, "i:" + i + ",wholeWidth:" + wholeWidth
					+ ",stageWidth:" + stageWidth+",stageHeight:"+stageHeight+",stage:"+stages);
		}

		Log.i(HyyConstants.HYY_TAG, "stages:" + stages);

		int wholeHeight = (stageHeight + VIEW_MARGIN) * stages;

		// report this final dimension
		setMeasuredDimension(resolveSize(wholeWidth, widthMeasureSpec),
				resolveSize(wholeHeight, heightMeasureSpec));

	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		// TODO Auto-generated method stub
		final int count = getChildCount();
		int row = 0; // which row lay your view relative to parent
		int lengthX = l; // right position of child relative to parent
		int lengthY = t; // bottom position of child relative to parent

		for (int i = 0; i < count; i++) {
			final View child = this.getChildAt(i);
			int width = child.getMeasuredWidth();
			int height = child.getMeasuredHeight();

			lengthX += width + VIEW_MARGIN;
			lengthY = row * (height + VIEW_MARGIN) + VIEW_MARGIN + height + t;

			// if it cant't draw in a same line ,skip it to next line
			if (lengthX > r) {
				lengthX = width + VIEW_MARGIN + l;
				row++;
				lengthY = row * (height + VIEW_MARGIN) + VIEW_MARGIN + height
						+ t;

			}

			child.layout(lengthX - width, lengthY - height, lengthX, lengthY);
		}

	}

}


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值