本帖最后由 Android_Robot 于 2016-10-19 15:10 编辑
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)方法将测量后的宽高设置进去。下面就给大家看一个测试的简单实例: - @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;
- }
复制代码 通过上面的简单实例,我们可以发现,首先获取我们所需要的测量模式,然后根据我们的需求进行指定。就是这么简单,就是这么easy。具体的效果,大家可以简单练习下。
三、View的绘制 我们知道在view的绘制中,我们是在onDraw()方法中进行view的绘制。onDraw方法给我们提供了一个Canvas对象,让我们来绘制需要的东西。我们看下创建一个Canvas对象。 - 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():监听控件的触摸事件回调。
通常情况下,自定义控件有以下三大类: - 通过对现有控件扩展;这类主要是我们继承系统的控件进行扩展。
- 通过控件的组合来实现新的控件。
- 重写来实现新的全新控件。
(一)对现有控件的扩展 我就不列举书中的例子了,就顺手给大家写一个例子吧!这个例子是实现解决这个问题的,我们默认情况下设置它的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);
- }
- }
- }
复制代码这样就是一个简单的对现有控件扩展的例子。 (二)创建复合控件 复合控件使用起来方便,比如我们的一个页面布局中,有很多重复类型的布局结构,这时我们就可以将它们抽取出来,做成复合控件,然后进行使用,这样也能减少代码的布局结构,具体例子就列出来了。这里涉及到的知识点有: (三)重写View来实现全新控件 这里就是我们的纯自定义控件。具体流程上面也有介绍,主要就是如何去实现绘制。传智·黑马自定义控件那两天的课程还是讲解的很精华的,好好学是可以领悟精髓的。
七、自定义ViewGroup
八、事件拦截机制分析 在自定义控件中,事件的分发很重要: 推荐几个相关精华资源:
- Android 编程下 Touch 事件的分发和消费机制
- Android:30分钟弄明白Touch事件分发机制
至此,基本的知识点罗列出来,对照学习吧!
其他精华资源推荐:
|