自定义ViewGroup

2015-11-12 chenhui 自定义控件

所谓的 ViewGroup,就是一个用来存放 View 的 View,在布局文件中表现为一个标签对中可以继续放其他元素。


要知道,ViewGroup 本质上也是一个 View,只是他能够存放其他的 View。我们自定义一个普通 View 时,只需要重新实现他的 onMeasure() 和 onDraw() 即可,前者用来告诉系统自己的大小,后者用来绘制自己。


实际上,ViewGroup 的自定义,也是干测量自身大小和绘制自身这两件事,只是他的测量方法和绘制方法不太一样,因为 ViewGroup 准确的说不是绘制他自身,而是绘制他的子 View,同样他的高宽也要根据子 View 的数量和大小来进行确定


ViewGroup 的高宽测量函数也是 onMeasure(),我们需要在这个函数里计算 ViewGroup 的尺寸,同时也要调用 measureChildren() 计算子 View 的尺寸。

有时候,ViewGroup 的大小是 wrap_content,那么这个时候我们就要通过测算 View 的大小来决定自己的大小。


一般情况下,我们使用下面这种方法来实现 onMeasure() 即可。


protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    measureChildren(widthMeasureSpec, heightMeasureSpec);
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
}


如果需要指定子 View 的高宽,那么可以用 getChildAt(int) 得到子 View,然后调用他的 getLayoutParams() 方法得到他的 LayoutParams 对象,再通过设置这个对象的 width 和 height 来指定子 View 的高宽。


ViewGroup 不需要实现 onDraw(),与之对应的是 onLayout(),我们需要在 onLayout() 里调用所有子 View 的 layout(),子 View 的 layout() 会导致他们重绘,所有的子 View 都绘制出来了,就相当于 ViewGroup 绘制出来了。

onLayout() 的简单示例如下:


protected void onLayout(boolean changed, int left, int top, int right, int bottom)
{
	int cWidth = 0;
	int cHeight = 0;
		 
	//当前绘制的XY坐标
	int X = 0; 
	int Y = 0;  
	    
	//得到子 View 数量
	int childCount = getChildCount();        
	    
	for ( int i = 0; i < childCount; i++ ) {
	    //遍历绘制所有子 View
	    	
	    //得到当前子 View
	    View childView = getChildAt(i);
	        
	    //得到子 View 的高宽
	    int width  = childView.getMeasuredWidth();
	    int height = childView.getMeasuredHeight();            
	       
	    //绘制子 View
	    childView.layout(X,Y,X+width,Y+height);
	        
	    //绘制完一个后,下一个在他下面绘制,所以把Y直到他屁股后面
	    Y += height;
	}  
}


然后再来使用上面给出的自定义 ViewGroup:


<com.example.example_app.customViewGroup 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#AA333333" >

    <TextView
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:background="#E5ED05"
        android:gravity="center"
        android:text="0"
        android:textColor="#FFFFFF"
        android:textSize="22sp"
        android:textStyle="bold" />

    <TextView
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:background="#00ff00"
        android:gravity="center"
        android:text="1"
        android:textColor="#FFFFFF"
        android:textSize="22sp"
        android:textStyle="bold" />


</com.example.example_app.customViewGroup>


他显示出的效果如下:



有时候我们需要支持 MarginLeft 之类的参数,但上面这个 ViewGroup 是不支持的。

我们下面修改 onLayout() 并重写 generateLayoutParams 来让 ViewGroup 支持 Margin。


public LayoutParams generateLayoutParams(AttributeSet attrs) {  
    return new MarginLayoutParams(getContext(),attrs);  
}	


这样他就支持 Margin 了,然后我们再修改 onLayout:


	protected void onLayout(boolean changed, int left, int top, int right, int bottom)
	{
		int cWidth = 0;
		int cHeight = 0;
		 
		//当前绘制的XY坐标
	    int X = 0; 
	    int Y = 0;  
	    
	    //得到子 View 数量
	    int childCount = getChildCount();   
	    
	    MarginLayoutParams margin;
	    
	    for ( int i = 0; i < childCount; i++ ) {
	        //遍历绘制所有子 View
	    	
	    	//得到当前子 View
	        View childView = getChildAt(i);
	        
	        //得到 margin 参数
	        margin = (MarginLayoutParams) childView.getLayoutParams();
	        
	        //得到子 View 的高宽
	        int width  = childView.getMeasuredWidth();
	        int height = childView.getMeasuredHeight();            
	       
	        //绘制子 View。加上 margin 值
	        childView.layout(
	        	X + margin.leftMargin,Y + margin.topMargin,
	        	X + width + margin.leftMargin,Y + height + margin.topMargin);
	        
	        //绘制完一个后,下一个在他下面绘制,所以把Y直到他屁股后面
	        Y += height + margin.topMargin;
	    }   
	}


OK!这样就成功把 margin 参数作用到 View 上了,下面是示例布局。


<com.example.example_app.customViewGroup 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#AA333333" >
 
    <TextView
        
        android:layout_marginLeft="30dp"
        android:layout_marginTop="30dp"
        
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:background="#E5ED05"
        android:gravity="center"
        android:text="0"
        android:textColor="#FFFFFF"
        android:textSize="22sp"
        android:textStyle="bold" />
 
    <TextView
        
        android:layout_marginLeft="30dp"
        android:layout_marginTop="30dp"
        
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:background="#00ff00"
        android:gravity="center"
        android:text="1"
        android:textColor="#FFFFFF"
        android:textSize="22sp"
        android:textStyle="bold" />

    <TextView
        
        android:layout_marginLeft="30dp"
        android:layout_marginTop="30dp"
        
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:background="#0000ff"
        android:gravity="center"
        android:text="3"
        android:textColor="#FFFFFF"
        android:textSize="22sp"
        android:textStyle="bold" />

</com.example.example_app.customViewGroup>


如果没问题,屏幕上显示的画面如下:





发表评论:

Copyright ©2015-2016 freehui All rights reserved