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

来源:互联网 发布:java链表的实现 编辑:程序博客网 时间:2024/06/08 14:54

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

(一直对自定义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()方法@Overrideprotected 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()方法@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {//获得子view的个数int cCount= getChildCount();//定义子view的长宽,和marginint 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);}}


0 0
原创粉丝点击