自定义ViewGroup

122 阅读5分钟

\

\

\

LayoutParams是什么?

   LayoutParams相当于一个Layout的信息包,它封装了Layout的位置、高、宽等信息。假设在屏幕上一块区域是由一个Layout占领的,如果将一个View添加到一个Layout中,最好告诉Layout用户期望的布局方式,也就是将一个认可的layoutParams传递进去。
可以这样去形容LayoutParams,在象棋的棋盘上,每个棋子都占据一个位置,也就是每个棋子都有一个位置的信息,如这个棋子在4行4列,这里的“4行4列”就是棋子的LayoutParams。

   但LayoutParams类也只是简单的描述了宽高,宽和高都可以设置成三种值:
**** 1,一个确定的值;
2,FILL_PARENT,即填满(和父容器一样大小);
3,WRAP_CONTENT,即包裹住组件就好。

决定ViewGroup的LayoutParams,在本例中能够支持margin

使用系统的MarginLayoutParams 

<span style="font-size:18px;"> 	@Override
	public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs)
	{
		return new MarginLayoutParams(getContext(), attrs);
	}</span>
 

onMeasure() 测量:

在ViewGroup中, 是根据测量childview的高度和宽度,以及margin

通过measureChildren( 父容器建议的宽度,父容器建议的高度)测量childView

的高和宽

/**
* 计算所有的chlseView的高度和宽度
* ,通过ViewGroup的measureChildren方法为其所有的孩子设置宽和高,
* 此行执行完成后,childView的宽和高都已经正确的计算过了
*/
measureChildren(widthMeasureSpec, heightMeasureSpec);

根据ViewGroup的模式,以及childView的高度和宽度, 

以及margin 来设置自己的高度和宽度

主要考虑的是mode为wrap_content  也就是MeasureSpec.At_most

根据childview来设置自己的高度和宽度, 也就是width, height.

否则直接是自己测量的父容器建议的值

	<span style="white-space:pre">	</span><span style="font-family:Times New Roman;">/**
				 * 设置父容器的宽度和高度
				 * 如果是wrap_content设置为我们的值  EXCATLY
				 * 否则: 直接设置为父容器计算的值   At_MOST
				 */
			<span style="font-size:18px;">	setMeasuredDimension( 
						(widthMode == MeasureSpec.EXACTLY) ? widthSpec: width,  
						(heightMode == MeasureSpec.EXACTLY) ? heightSpec : height
						);</span></span>


onLayout设置chidView的位置,(设置childView绘制的区域)\

通过 测量的高度和宽度,以及margin,来确定childeview的位置。

\

\

\

\

\

自定义ViewGroup:

package com.example.viewgroupdemo;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.LinearLayout;

public class MyViewGroup extends ViewGroup {
	public MyViewGroup(Context context) {
		this(context, null);
	}
	public MyViewGroup(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public MyViewGroup(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		try {

			/**
			 * TODO: 对其所有的 childView进行定位(设置childView的绘制区域)
			 * 
			 */
			int cCount = getChildCount();
			int cWidth = 0;
			int cHeight = 0;
			MarginLayoutParams marginLayoutParams = null;
			/***
			 * 遍历所有的childView 根据其高宽,以及margin进行布局
			 * 
			 */
			for (int i = 0; i < cCount; i++) {
				View  childView  = getChildAt(i);
				cWidth = childView.getMeasuredWidth();
				cHeight = childView.getMeasuredHeight();
				marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams();
				//childView的四个边
				int cl = 0, cr = 0, ct = 0, cb = 0;
				switch (i) {
				case 0:
					cl = marginLayoutParams.leftMargin;
					ct = marginLayoutParams.topMargin;
					break;
				case 1:
					cl = getWidth() - cWidth - marginLayoutParams.leftMargin - marginLayoutParams.rightMargin;
					ct = marginLayoutParams.topMargin;
					break;
				case 2:
					cl = marginLayoutParams.leftMargin;
					ct = getHeight() - marginLayoutParams.bottomMargin - cHeight;
					break;
				case 3:
					cl = getWidth() - cWidth - marginLayoutParams.rightMargin- marginLayoutParams.rightMargin;
					ct = getHeight() - cHeight - marginLayoutParams.bottomMargin;
					break;
				default:
					break;
				}
				cr = cl + cWidth;
				cb = ct + cHeight;
				childView.layout(cl, ct, cr, cb);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

 	@Override
	public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs)
	{
		return new MarginLayoutParams(getContext(), attrs);
	}
 


	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		try {

			/**
			 *  TODO:计算所有的childView的测量,获取高度和宽度,
			 *  并根据childView的计算结果, 设置自己的 宽度和高度
			 */
			//获取ViewGroup上级容器为其推荐的高度和宽度, 以及设计模式
			int widthSpec = MeasureSpec.getSize(widthMeasureSpec);
			int widthMode = MeasureSpec.getMode(widthMeasureSpec);
			int heightSpec = MeasureSpec.getSize(heightMeasureSpec);
			int heightMode = MeasureSpec.getMode(heightMeasureSpec);

			/**
			 * 计算所有的chlseView的高度和宽度
			 * ,通过ViewGroup的measureChildren方法为其所有的孩子设置宽和高,
			 * 此行执行完成后,childView的宽和高都已经正确的计算过了
			 */
			measureChildren(widthMeasureSpec, heightMeasureSpec);

			int width = 0;
			int height = 0;
			//子view的个数,
			int mCount = getChildCount();

			int cWidth = 0;
			int cHeight = 0;
			MarginLayoutParams marginLayoutParams = null;

			//计算左边两个childView的高度
			int lHeight = 0;
			//计算右边两个childView的高度, 最终高度是去lHeight和 rHeight最大值决定;
			int rHeight = 0;

			//计算上边两个childView的宽度
			int tWidth = 0;
			//计算下边两个childView的宽度, 最终宽度由tWidth和bWidth最大值决定
			int bWidth = 0;

			/**
			 * 根据childView 计算出的宽和高,以及设置的margin计算容器的宽度和高度, 
			 * 主要用于容器是wrap_content
			 */
			for (int i = 0; i < mCount; i++) {
				//获取子view 
				View childView = getChildAt(i);
				//获取childView测量的宽度和高度
				cWidth = childView.getMeasuredWidth();
				cHeight = childView.getMeasuredHeight();

				//通过子view 获取MarginlayoutParams
				marginLayoutParams  =  (MarginLayoutParams) childView.getLayoutParams();
				//遍历四个childView

				//上边两个宽度
				if(i==0 || i==1){
					tWidth += cWidth +  
							marginLayoutParams.leftMargin + 
							marginLayoutParams.rightMargin; 
				}

				//下边两个宽度
				if(i==2 || i==3){
					bWidth += cWidth +  
							marginLayoutParams.leftMargin + 
							marginLayoutParams.rightMargin; 
				}

				//左边两个的高度
				if(i == 0 || i == 2){
					lHeight += cHeight + 
							marginLayoutParams.topMargin
							+marginLayoutParams.bottomMargin;
				}

				//右边两个的高度
				if(i == 1 || i == 3){
					rHeight += cHeight + 
							marginLayoutParams.topMargin
							+marginLayoutParams.bottomMargin;
				}

				//计算ViewGroup的高度和宽度
				width = Math.max(tWidth, bWidth);
				height = Math.max(lHeight, rHeight);


				/**
				 * 设置父容器的宽度和高度
				 * 如果是wrap_content设置为我们的值  EXCATLY
				 * 否则: 直接设置为父容器计算的值   At_MOST
				 */
				setMeasuredDimension( 
						(widthMode == MeasureSpec.EXACTLY) ? widthSpec: width,  
						(heightMode == MeasureSpec.EXACTLY) ? heightSpec : height
						);
			} 
		} catch (Exception e) {
			e.printStackTrace();
		}
	}


}

布局:

<com.example.viewgroupdemo.MyViewGroup
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="#AA333333" >

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

    <TextView
        android:layout_width="50dp"
        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_width="50dp"
        android:layout_height="50dp"
        android:background="#ff0000"
        android:gravity="center"
        android:text="2"
        android:textColor="#FFFFFF"
        android:textSize="22sp"
        android:textStyle="bold" />

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

</com.example.viewgroupdemo.MyViewGroup>

\

 

package com.example.viewgroupdemo;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.LinearLayout;
/**
 * 自定义ViewGroup , 添加四个TextView(childview)
 * 四个角
 * 
 * onMeasure  测量, 
 *      首先获取viewgroup 父容器建议的高度和宽度, 以及模式
 * 
 *      通过此方法, 为childView设置高度和宽度, 
 *      此方法执行完成后, childview的高度和宽度已经计算好了,
 *      measureChildren(widthMeasureSpec, heightMeasureSpec);
 *      遍历childview ,计算高度和宽度, 通过 MarginLayoutParams 获取margin
 * 
 *      //通过子view 获取MarginlayoutParams
		marginLayoutParams  =  (MarginLayoutParams) childView.getLayoutParams();
 * 
 *      //获取了childview的高度和宽度以及margin完成
 *      根据viewgroup的mode 设置viewgroup的高度和宽度
 *      setMeasuredDemension(
 *          (widthMode == MeasureSpec.EXACTLY) ? widthSpec: width,  
			(heightMode == MeasureSpec.EXACTLY) ? heightSpec : height
 * 
 *       )
 *      
 */ 
public class MyViewGroup extends ViewGroup {
	public MyViewGroup(Context context) {
		this(context, null);
	}
	public MyViewGroup(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public MyViewGroup(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		try {

			/**
			 * TODO: 对其所有的 childView进行定位(设置childView的绘制区域)
			 * 
			 */
			int cCount = getChildCount();
			int cWidth = 0;
			int cHeight = 0;
			MarginLayoutParams marginLayoutParams = null;
			/***
			 * 遍历所有的childView 根据其高宽,以及margin进行布局
			 * 
			 */
			for (int i = 0; i < cCount; i++) {
				View  childView  = getChildAt(i);
				cWidth = childView.getMeasuredWidth();
				cHeight = childView.getMeasuredHeight();
				marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams();
				//childView的四个边
				int cl = 0, cr = 0, ct = 0, cb = 0;
				switch (i) {
				case 0:
					cl = marginLayoutParams.leftMargin;
					ct = marginLayoutParams.topMargin;
					break;
				case 1:
					cl = getWidth() - cWidth - marginLayoutParams.leftMargin - marginLayoutParams.rightMargin;
					ct = marginLayoutParams.topMargin;
					break;
				case 2:
					cl = marginLayoutParams.leftMargin;
					ct = getHeight() - marginLayoutParams.bottomMargin - cHeight;
					break;
				case 3:
					cl = getWidth() - cWidth - marginLayoutParams.rightMargin- marginLayoutParams.rightMargin;
					ct = getHeight() - cHeight - marginLayoutParams.bottomMargin;
					break;
				default:
					break;
				}
				cr = cl + cWidth;
				cb = ct + cHeight;
				childView.layout(cl, ct, cr, cb);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

 	@Override
	public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs)
	{
		return new MarginLayoutParams(getContext(), attrs);
	}
 


	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		try {
			/**
			 *  TODO:计算所有的childView的测量,获取高度和宽度,
			 *  并根据childView的计算结果, 设置自己的 宽度和高度
			 */
			//获取ViewGroup上级容器为其推荐的高度和宽度, 以及设计模式
			int widthSpec = MeasureSpec.getSize(widthMeasureSpec);
			int widthMode = MeasureSpec.getMode(widthMeasureSpec);
			int heightSpec = MeasureSpec.getSize(heightMeasureSpec);
			int heightMode = MeasureSpec.getMode(heightMeasureSpec);

			/**
			 * 计算所有的chlseView的高度和宽度
			 * ,通过ViewGroup的measureChildren方法为其所有的孩子设置宽和高,
			 * 此行执行完成后,childView的宽和高都已经正确的计算过了
			 */
			measureChildren(widthMeasureSpec, heightMeasureSpec);

			int width = 0;
			int height = 0;
			//子view的个数,
			int mCount = getChildCount();

			int cWidth = 0;
			int cHeight = 0;
			MarginLayoutParams marginLayoutParams = null;

			//计算左边两个childView的高度
			int lHeight = 0;
			//计算右边两个childView的高度, 最终高度是去lHeight和 rHeight最大值决定;
			int rHeight = 0;

			//计算上边两个childView的宽度
			int tWidth = 0;
			//计算下边两个childView的宽度, 最终宽度由tWidth和bWidth最大值决定
			int bWidth = 0;

			/**
			 * 根据childView 计算出的宽和高,以及设置的margin计算容器的宽度和高度, 
			 * 主要用于容器是wrap_content
			 */
			for (int i = 0; i < mCount; i++) {
				//获取子view 
				View childView = getChildAt(i);
				//获取childView测量的宽度和高度
				cWidth = childView.getMeasuredWidth();
				cHeight = childView.getMeasuredHeight();

				//通过子view 获取MarginlayoutParams
				marginLayoutParams  =  (MarginLayoutParams) childView.getLayoutParams();
				//遍历四个childView

				//上边两个宽度
				if(i==0 || i==1){
					tWidth += cWidth +  
							marginLayoutParams.leftMargin + 
							marginLayoutParams.rightMargin; 
				}

				//下边两个宽度
				if(i==2 || i==3){
					bWidth += cWidth +  
							marginLayoutParams.leftMargin + 
							marginLayoutParams.rightMargin; 
				}

				//左边两个的高度
				if(i == 0 || i == 2){
					lHeight += cHeight + 
							marginLayoutParams.topMargin
							+marginLayoutParams.bottomMargin;
				}

				//右边两个的高度
				if(i == 1 || i == 3){
					rHeight += cHeight + 
							marginLayoutParams.topMargin
							+marginLayoutParams.bottomMargin;
				}

				//计算ViewGroup的高度和宽度
				width = Math.max(tWidth, bWidth);
				height = Math.max(lHeight, rHeight);


				/**
				 * 设置父容器的宽度和高度
				 * 如果是wrap_content设置为我们的值  EXCATLY
				 * 否则: 直接设置为父容器计算的值   At_MOST
				 */
				setMeasuredDimension( 
						(widthMode == MeasureSpec.EXACTLY) ? widthSpec: width,  
						(heightMode == MeasureSpec.EXACTLY) ? heightSpec : height
						);
			} 
		} catch (Exception e) {
			e.printStackTrace();
		}
	} 
}




    

    
     

    
     

    
     

    
     


    

\

\

\

\

\


\