【UI】【View】自定义布局方法与注意事项(一)

7 篇文章 0 订阅
2 篇文章 0 订阅

本篇博客主要记录自定义布局的方法和注意事项。

(一直对自定义View感兴趣,学习后怕忘记,特此总结记录。学习View过程中,主要参考了鸿洋_大神的博客。)

【张鸿洋的博客】http://blog.csdn.net/lmj623565791/article/details/38339817 

         

一、自定义布局需要实现的方法

1. 首先要重写onMeasure()方法:onMeasure方法主要完成对此自定义布局尺寸的测量。

2. 然后要重写onLayout()方法:onLayout方法完成此自定义布局中childView位置的指定。

3. 要定义一个内部类,返回LayoutParams,用于确定childView支持哪些属性。


二、方法详解

1. onMeasure():

:在定义布局XML文件时,我们要对布局控件定义两个属性。一个是android:layout_width,另一个是 android:layout_height。这些属性值可以选择match_parent、npx、或者wrap_content。

:父控件会传递给子控件一个MeasureSpec,可以获得父控件对子控件宽高的测量模式和测量值。

       当子控件属性是match_parent和npx时,测量模式是EXACTLY。

       当子控件属性是wrap_content时,测量模式是AT_MOST。此时子控件的尺寸是由子控件的内容决定。宽高值不是父控件传入        的测量值,而需要在自己的onMeasure()方法中确定。

在系统测量和绘制View时,主要将布局解析成View树。通过getChildAt(i)可以获得对应的子控件。

       onMeasure()方法中必须调用setMeasuredDimension()方法设置本自定义控件尺寸。下面是onMeasure():


 

        //主要进行测量和确定CircleMenu的尺寸,判断是根据父控件测量的值设置,还是根据子控件的大小设置尺寸
	//必须调用setMeasuredDimension()方法
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
		
		//获得父控件传递给CircleMenu的测量值和测量模式
		int widthMode= MeasureSpec.getMode(widthMeasureSpec);
		int widthSize= MeasureSpec.getSize(widthMeasureSpec);
		int heightMode= MeasureSpec.getMode(heightMeasureSpec);
		int heightSize= MeasureSpec.getSize(heightMeasureSpec);
		Log.d(CRICLEMENU_TAG, widthMode+","+widthSize+","+heightMode+","+heightSize);
		
		//测量出所有子view的尺寸
		measureChildren(widthMeasureSpec, heightMeasureSpec);
		//测量出的子view宽,高
		int cWidth;
		int cHeight;
		//布局上面两个view的宽度,下面两个view的宽度,左边两个view的高度,右边两个view的高度
		int tWidth=0;
		int bWidth=0;
		int lHeight=0;
		int rHeight=0;
		//tWidth,bWidth中的最大宽度;lHeight,rHeight中的最大高度
		int maxWidth=0;
		int maxHeight=0;
		
		//若是 wrap_content,获得CircleMenu的大小
		//获得布局中子view的数量
		int cCount= getChildCount();
		Log.d(CRICLEMENU_TAG, "cCount="+cCount);
		
		for(int i=0; i<cCount; i++){
			View childview= getChildAt(i);
			cWidth= childview.getMeasuredWidth();
			cHeight= childview.getMeasuredHeight();
			
			if(i==0){
				tWidth+=cWidth;
				lHeight+=cHeight;
				Log.d(CRICLEMENU_TAG, "i="+i+", tWidth="+tWidth+", lHeight="+lHeight);
			}
			
			if (i==1) {
				tWidth+=cWidth;
				rHeight+=cHeight;
				Log.d(CRICLEMENU_TAG, "i="+i+", tWidth="+tWidth+", rHeight="+rHeight);
			}
			
			if(i==2){
				bWidth+=cWidth;
				lHeight+=cHeight;	
				Log.d(CRICLEMENU_TAG, "i="+i+", bWidth="+bWidth+", lHeight="+lHeight);
			}
			
			if(i==3){
				bWidth+=cWidth;
				rHeight+=cHeight;
				Log.d(CRICLEMENU_TAG, "i="+i+", bWidth="+bWidth+", rHeight="+rHeight);
			}	
		}
		//获取最大宽度,最大高度
		maxWidth= Math.max(tWidth, bWidth);
		maxHeight= Math.max(lHeight, rHeight);
		
		setMeasuredDimension((widthMode==MeasureSpec.EXACTLY)?widthSize:maxWidth, 
				(heightMode==MeasureSpec.EXACTLY)?heightSize:maxHeight);
		
		Log.d(CRICLEMENU_TAG, ((widthMode==MeasureSpec.EXACTLY)?widthSize:maxWidth)+","
		+((heightMode==MeasureSpec.EXACTLY)?heightSize:maxHeight));
		
	} //end测量布局尺寸结束


2. onLayout():

此方法中必须调用childView.layout(cl,ct,cr,cb),其中四个参数是子控件相对父控件上下左右四个边的距离。

//为子view安排位置,必须调用layout()方法
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		//获得子view的个数
		int cCount= getChildCount();
		//定义子view的长宽,和margin
		int mChildWidth=0;
		int mChildHeight=0;
		MarginLayoutParams mChildParams=null;
		
		
		//获得子view的长宽
		for(int i=0;i<cCount;i++){
			View childview=getChildAt(i);
			mChildWidth=childview.getMeasuredWidth();
			mChildHeight=childview.getMeasuredHeight();
			mChildParams=(MarginLayoutParams) childview.getLayoutParams();
			
			//定义子view四条边与父控件的距离
			int cl=0;
			int ct=0;
			int cr=0;
			int cb=0;
			
			switch (i) {
			case 0:
				cl=mChildParams.leftMargin;
				ct=mChildParams.topMargin;
				Log.v(CRICLEMENU_TAG, cl+","+ct);
				break;
				
			case 1:
				cl=mChildParams.leftMargin+mChildWidth;
				ct=mChildParams.topMargin;
				Log.v(CRICLEMENU_TAG, cl+","+ct);

			case 2:
				cl=mChildParams.leftMargin;
				ct=mChildParams.topMargin+mChildHeight;
				Log.v(CRICLEMENU_TAG, cl+","+ct);
				
			case 3:
				cl=getWidth()-mChildParams.rightMargin;
				ct=mChildParams.topMargin+mChildHeight;
				Log.v(CRICLEMENU_TAG, cl+","+ct);
				
			default:
				break;
			}
			
			cr=cl+mChildWidth;
			cb=ct+mChildHeight;
			//使用layout进行子view的布局
			childview.layout(cl, ct, cr, cb);
			
			//打印出子view四个边距离父控件的距离
			Log.d(CRICLEMENU_TAG, cl+","+ct+","+cr+","+cb);
		}
		
	}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值