Android中的控件大致被分为两类:ViewGroup控件与View控件。ViewGroup控件作为父控件可以包好多个View控件,并管理其包含的View控件。通过ViewGroup,整个界面上形成一个树结构,即控件树。下面看下Android界面架构图:
从图中我们可以知道,每个Activity都包含一个Window对象,在Android中Window对象通常由PhoneWindow来实现PhoneWindow将一个DecorView设置为整个应用窗口的根View,在显示上它将屏幕分成两部分,一个是TitleView,另一个是ContentView,它是一个ID为content的Framelayout,所有我们通常会在onCreate方法中setContentView。
我们都知道在自定义控件的时候,我们在onMeasure()方法中进行控件的测量。Android系统给我们设计了一个功能强大的类MeasureSpec类,通过该类可以帮助我们测量View。MeasureSpec是一个32位的int值,其中高2位为测量的模式,低30位为测量的大小,使用位运算的目的就是提高并优化效率。测量模式可以分为三种:
View类默认的onMeasure()方法中只支持EXACTLY模式,所以如果在自定义控件的时候不重写onMeasure()方法的话,就是默认的精确测量值模式。一般情况下我们都会重写onMeasure方法进行指定。我们通过查看源码,可以发现系统在测量的时候最终还是调用了setMeasuredDimension(int measuredWidth,int measuredHeight)方法将测量后的宽高设置进去。下面就给大家看一个测试的简单实例:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if(heightMode == MeasureSpec.AT_MOST){
heightSize = mDisplayMetrics.densityDpi * 30;
}
if(widthMode == MeasureSpec.AT_MOST){
widthSize = mDisplayMetrics.densityDpi * 300;
}
setMeasuredDimension(widthSize, heightSize);
}
通过上面的简单实例,我们可以发现,首先获取我们所需要的测量模式,然后根据我们的需求进行指定。就是这么简单,就是这么easy。具体的效果,大家可以简单练习下。
我们知道在view的绘制中,我们是在onDraw()方法中进行view的绘制。onDraw方法给我们提供了一个Canvas对象,让我们来绘制需要的东西。我们看下创建一个Canvas对象。
Canvas canvas = new Canvas(bitmap);
在创建一个Canvas对象时,我们通常会将一个bitmap对象跟Canvas画布绑定在一起,这个过程称之为装载画布。这个bitmap存储所有绘制在canvas画板上的像素信息,所以当你使用这个canvas进行drawXXX方法时,信息都在bitmap上。
在前面的分析中,我们知道ViewGroup中包含了很多的View对象,所以ViewGroup的大小同样是我们指定或者设置为wrap_content由子控件的大小控制,当设为wrap_content的时候,ViewGroup的大小就由内部分别遍历子View测量。当对子view测量完毕后,就执行View的Layout方法进行放置它们。
ViewGroup通常情况下不需要绘制,因为它本身就没有需要绘制的东西,如果不指定ViewGroup的背景颜色,那么ViewGroup的onDraw()方法都不会被调用。但是,ViewGroup会使用dispatchDraw()方法来绘制其子类View,其过程同样是遍历所有子View,并调用子View的绘制方法来完成的。
我们都知道自定义控件的恰当使用,能让我们的应用有亮点,但是滥用自定义View会带来适得其反的效果,一个让用户熟悉方便使用的控件测试好控件。在自定义View时,有以下几个重要的回调方法:
通常情况下,自定义控件有以下三大类:
我就不列举书中的例子了,就顺手给大家写一个例子吧!这个例子是实现解决这个问题的,我们默认情况下设置它的Gravity为居中,当获取焦点的时候,需要Gravity为左边,所以这个时候,我们为了这个小功能,不可能去重写写一个EditText,这里就可以对系统的控件进行扩充。代码如下:
public class IOSEditText extends EditText {
public IOSEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onFocusChanged(boolean focused, int direction,
Rect previouslyFocusedRect) {
if(focused){
setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
}else{
setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
}
}
}
这样就是一个简单的对现有控件扩展的例子。
复合控件使用起来方便,比如我们的一个页面布局中,有很多重复类型的布局结构,这时我们就可以将它们抽取出来,做成复合控件,然后进行使用,这样也能减少代码的布局结构,具体例子就列出来了。这里涉及到的知识点有:
这里就是我们的纯自定义控件。具体流程上面也有介绍,主要就是如何去实现绘制。可以参照网上的一些基础教程去学习下,推荐下鸿洋和爱哥的自定义控件教程,非常不错,一百个赞。
前面我们已经简单介绍了自定义ViewGroup的基本流程。这里推荐鸿洋的一篇自定义ViewGroup博客
在自定义控件中,事件的分发很重要。网上介绍的也很多,
推荐几个地址:
Android 编程下 Touch 事件的分发和消费机制
Android:30分钟弄明白Touch事件分发机制
至此,基本的知识点罗列出来,对照学习吧!
作者:mr_dsw 欢迎转载,与人分享是进步的源泉!
转载请保留地址:http://blog.csdn.net/mr_dsw