自定义控件

Android自定义控件步骤如下:

1.       选择一个父类,继承该父类(如View,ViewGroup)

2.       写一系列构造函数

3.       重写一系列on函数(如onMeasure,onLayout,onDraw,onSizeChanged),需要的才重写

4.       写一系列属性,一系列pulibc函数,向外界透露接口

 

这只是极其概括的总结而已。具体来说,从继承的父类不同,可分为以下三种

A.继承自View,TextView,ButtonView等,也就是没有子元素的类;

B.继承自ViewGroup,LinearLayout等,也就是写一个Layout

C.继承自AdapterView,ListView等,也就是写一个Adapter控件(没做研究)

这三种在写的时候都遵循上述的四个步骤,但侧重点各有不同,其中步骤1,2,4都是要有的。差异关键在步骤3。A一般侧重于重写onMeasure,onDraw函数,特别是onDraw函数;B一般侧重于重写onMeasure,onLayout函数,onDraw函数一般不用重写。其中onMeasure,onLayout函数里面的测量(measure)以及布局(layout)算法是重点也是难点。

         下面分析android 的一个layout控件—FrameLayout

         FrameLayout控件继承自ViewGroup,它可以说是android布局系统中最简单的一个布局控件。它在ViewGroup的基础上,定义了自己的三个属性,对应的XML Attributes分别为android:foregroundandroid:foregroundGravityandroid:measureAllChildren。第一个属性是设置前景色;第二个属性是控制前景色的重心,前两个属性其实是对这其实是对android:backgroud的重写,其目的是可以控制背景的重心。第三个属性如果为true,则在测量时测量所有的子元素(即使该子元素为gone)。

         下面是对FrameLayout.java的分析(版本为2.2)

          49-72行是定义了一系列属性,包括与上面对应的mForegroundmForegroundGravitymMeasureAllChildren

          74-104行定义了三个构造函数,从中我们可以看出,mForeground默认值为null, mForegroundGravityGravity.FillmMeasureAllChildren默认值为false;

          另外需要注意的是103行调用a.recycle(),释放资源。

          106-432,是一系列函数,这些函数中,有的是对上面三个属性值的控制(set,get函数),有的是重写父类的函数,如onMeasure,onLayout,Draw函数等。

          在控制属性的函数中,值得注意的是setMeasureAllChildren,函数中没有调用requstLayout以及invalidate函数,因为不需要,这个很好理解;setForeground函数中,最后都调用了requstLayout以及invalidate函数,这意味着,设置调用setForeground的时候,将重新布局以及重绘;setForegroundGravity函数中,只调用了requstLayout函数,这意味着将重新布局,但不会重绘(why)。我们自己在定义属性的时候,根据需要在最后调用这两个函数。

          onMeasure的测量算法。将子元素测量后最大的宽度(加上FrameLayout本身的内边界padding),和最大的高度(加上padding)作为FrameLayout的测量值返回给它的上级控件。

         值得注意的是1.测量值要加上本身的padding;2.需要跟getSuggestedMinimumWidth进行对比;3.需要判断child.getVisibility()是否为GONE

         onLayout的布局算法。按照子元素存储的顺序以及其LayoutParams依次调用childLayout函数。

         值得注意的是:1.要判断child.getVisibility()是否为GONE2.传递给child的参数是相对于父元素的位置,并且是要减去child的外边距(margin)的.因为FrameLayout定义了自己的LayoutParams,所以重写了generateLayoutParamscheckLayoutParamsgenerateLayoutParams

         439-最后,定义了FrameLayout自己的LayoutParams主要是在MarginLayoutParams基础上,新加了gravity属性。



WrapLayout是我写的一个布局控件(代码在下一篇给出),它会按照水平(或垂直)的顺序,将它的子元素从左到右(或从上到下)依次排列,如果一行排满了,则自动转入下一行。以下是写这个控件时候的感受。

写这个控件的时候,还是遵循自定义控件的规则。1.继承父控件,这里继承自ViewGroup;

2.写构造函数,这里像LinearLayout一样写了两个构造函数;3.重写一系列on函数,这里主要是重写了onMeasure,onLayout函数;4.提供一系列pulic函数向外界提供功能(这里基本没有)

         这里重点跟难点是步骤3,重写onMeasure,onLayout函数,也就是我们测量跟布局时候的算法。

         一开始的时候,我还是不是特别清楚要有怎样的测量算法才满足要求;于是我就先写了布局算法(因为布局过程是很清楚的),等到我写完了布局算法,并加以测试之后,这时候我基本意识到了测量算法要怎么写。以下是onMeasure,onLayout的过程中,需要注意的几点:

1.       用getPaddingTop等函数取得View的padding,注意这里不能直接使用mPaddingTop属性直接获得,尽管在父类里被声明为protected类型的;

2.       如果获得子元素的LayoutParams是MarginLayoutParams,必须在你的布局控件里重写generateLayoutParamscheckLayoutParamsgenerateLayoutParams这三个函数;

3.       通过MarginLayoutParams取出子元素的Margin;

4.       onLayout函数中,通过参数r-l取出布局控件的宽度;

5.       onMeasureonLayout过程中,要使用View.getMeasuredWidth()取得子元素测量后的宽度。

转载自http://blog.csdn.net/zyntl/article/details/6113897


你可能感兴趣的:(android,算法,ListView,layout,null,attributes)