您的位置:首页 > 移动开发 > Android开发

Android群英传之Android控件建构与自定义控件详解知识点总结

2015-12-06 23:14 387 查看

Android群英传之Android控件建构与自定义控件详解知识点总结

一、Android控件架构

Android中的控件大致被分为两类:ViewGroup控件与View控件。ViewGroup控件作为父控件可以包好多个View控件,并管理其包含的View控件。通过ViewGroup,整个界面上形成一个树结构,即控件树。下面看下Android界面架构图:



从图中我们可以知道,每个Activity都包含一个Window对象,在Android中Window对象通常由PhoneWindow来实现PhoneWindow将一个DecorView设置为整个应用窗口的根View,在显示上它将屏幕分成两部分,一个是TitleView,另一个是ContentView,它是一个ID为content的Framelayout,所有我们通常会在onCreate方法中setContentView。

二、View的测量

我们都知道在自定义控件的时候,我们在onMeasure()方法中进行控件的测量。Android系统给我们设计了一个功能强大的类MeasureSpec类,通过该类可以帮助我们测量View。MeasureSpec是一个32位的int值,其中高2位为测量的模式,低30位为测量的大小,使用位运算的目的就是提高并优化效率。测量模式可以分为三种:

EXACTLY:精确测量模式,一般是由于我们指定了控件的大小或者设为match_parent属性。

AT_MOST:即最大值模式,当控件大小不指定时,控件大小随内容变化而变化,即设为wrap_content属性时。

UNSPECIFIED:这个属性比较怪,它不指定测量模式,View想多大就多大,通常情况下在绘制自定义View时使用。

View类默认的onMeasure()方法中只支持EXACTLY模式,所以如果在自定义控件的时候不重写onMeasure()方法的话,就是默认的精确测量值模式。一般情况下我们都会重写onMeasure方法进行指定。我们通过查看源码,可以发现系统在测量的时候最终还是调用了setMeasuredDimension(int measuredWidth,int measuredHeight)方法将测量后的宽高设置进去。下面就给大家看一个测试的简单实例:

[code]    @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的绘制

我们知道在view的绘制中,我们是在onDraw()方法中进行view的绘制。onDraw方法给我们提供了一个Canvas对象,让我们来绘制需要的东西。我们看下创建一个Canvas对象。

[code]    Canvas canvas = new Canvas(bitmap);


在创建一个Canvas对象时,我们通常会将一个bitmap对象跟Canvas画布绑定在一起,这个过程称之为装载画布。这个bitmap存储所有绘制在canvas画板上的像素信息,所以当你使用这个canvas进行drawXXX方法时,信息都在bitmap上。

四、ViewGroup的测量

在前面的分析中,我们知道ViewGroup中包含了很多的View对象,所以ViewGroup的大小同样是我们指定或者设置为wrap_content由子控件的大小控制,当设为wrap_content的时候,ViewGroup的大小就由内部分别遍历子View测量。当对子view测量完毕后,就执行View的Layout方法进行放置它们。

五、ViewGroup的绘制

ViewGroup通常情况下不需要绘制,因为它本身就没有需要绘制的东西,如果不指定ViewGroup的背景颜色,那么ViewGroup的onDraw()方法都不会被调用。但是,ViewGroup会使用dispatchDraw()方法来绘制其子类View,其过程同样是遍历所有子View,并调用子View的绘制方法来完成的。

六、自定义View

我们都知道自定义控件的恰当使用,能让我们的应用有亮点,但是滥用自定义View会带来适得其反的效果,一个让用户熟悉方便使用的控件测试好控件。在自定义View时,有以下几个重要的回调方法:

onFinishInflate():从XML加载组件后回调

onSizeChanged():组件大小改变时回调。

onMeasure():回调该方法来进行测量。

onLayout():回调该方法进行控件的布局。

onTouchEvent():监听控件的触摸事件回调。

通常情况下,自定义控件有以下三大类:

通过对现有控件扩展;这类主要是我们继承系统的控件进行扩展。

通过控件的组合来实现新的控件。

重写来实现新的全新控件。

6.1对现有控件的扩展

我就不列举书中的例子了,就顺手给大家写一个例子吧!这个例子是实现解决这个问题的,我们默认情况下设置它的Gravity为居中,当获取焦点的时候,需要Gravity为左边,所以这个时候,我们为了这个小功能,不可能去重写写一个EditText,这里就可以对系统的控件进行扩充。代码如下:

[code]    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);
        }
    }
    }


这样就是一个简单的对现有控件扩展的例子。

6.2创建复合控件

复合控件使用起来方便,比如我们的一个页面布局中,有很多重复类型的布局结构,这时我们就可以将它们抽取出来,做成复合控件,然后进行使用,这样也能减少代码的布局结构,具体例子就列出来了。这里涉及到的知识点有:

自定义属性

定义回调接口

6.3重写View来实现全新控件

这里就是我们的纯自定义控件。具体流程上面也有介绍,主要就是如何去实现绘制。可以参照网上的一些基础教程去学习下,推荐下鸿洋和爱哥的自定义控件教程,非常不错,一百个赞。

7、自定义ViewGroup

前面我们已经简单介绍了自定义ViewGroup的基本流程。这里推荐鸿洋的一篇自定义ViewGroup博客

8、事件拦截机制分析

在自定义控件中,事件的分发很重要。网上介绍的也很多,

推荐几个地址:

Android 编程下 Touch 事件的分发和消费机制

Android:30分钟弄明白Touch事件分发机制

至此,基本的知识点罗列出来,对照学习吧!

作者:mr_dsw 欢迎转载,与人分享是进步的源泉!

转载请保留地址:http://blog.csdn.net/mr_dsw
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: