A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© Android_Robot 中级黑马   /  2016-5-12 10:21  /  4269 人查看  /  24 人回复  /   1 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 Android_Robot 于 2016-10-19 15:10 编辑


Android控件建构与自定义控件详解

一、Android控件架构
       Android中的控件大致被分为两类:ViewGroup控件与View控件。
      ViewGroup控件作为父控件可以包含好多个View控件,并管理其包含的View控件。通过ViewGroup,整个界面上形成一个树结构,即控件树。下面看下Android界面架构图:
QQ截图20160512095726.jpg
       从图中我们可以知道,每个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)方法将测量后的宽高设置进去。下面就给大家看一个测试的简单实例:
  1. @Override
  2.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  3.         int widthSize = MeasureSpec.getSize(widthMeasureSpec);
  4.         int widthMode = MeasureSpec.getMode(widthMeasureSpec);

  5.         int heightSize = MeasureSpec.getSize(heightMeasureSpec);
  6.         int heightMode = MeasureSpec.getMode(heightMeasureSpec);

  7.         if(heightMode == MeasureSpec.AT_MOST){
  8.                 heightSize = mDisplayMetrics.densityDpi * 30;
  9.         }
复制代码
       通过上面的简单实例,我们可以发现,首先获取我们所需要的测量模式,然后根据我们的需求进行指定。就是这么简单,就是这么easy。具体的效果,大家可以简单练习下。

三、View的绘制
       我们知道在view的绘制中,我们是在onDraw()方法中进行view的绘制。onDraw方法给我们提供了一个Canvas对象,让我们来绘制需要的东西。我们看下创建一个Canvas对象。
  1. 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,这里就可以对系统的控件进行扩充。代码如下:
  1. public class IOSEditText extends EditText {
  2.       public IOSEditText(Context context, AttributeSet attrs) {
  3.       super(context, attrs);
  4.       }

  5.      @Override
  6.      protected void onFocusChanged(boolean focused, int direction,
  7.      Rect previouslyFocusedRect) {
  8.             if(focused){
  9.                        setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
  10.             }else{
  11.                        setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
  12.            }
  13.      }
  14.     }
复制代码
这样就是一个简单的对现有控件扩展的例子。
(二)创建复合控件
       复合控件使用起来方便,比如我们的一个页面布局中,有很多重复类型的布局结构,这时我们就可以将它们抽取出来,做成复合控件,然后进行使用,这样也能减少代码的布局结构,具体例子就列出来了。这里涉及到的知识点有:
  • 自定义属性
  • 定义回调接口
(三)重写View来实现全新控件
       这里就是我们的纯自定义控件。具体流程上面也有介绍,主要就是如何去实现绘制。传智·黑马自定义控件那两天的课程还是讲解的很精华的,好好学是可以领悟精髓的。

七、自定义ViewGroup
      前面我们已经简单介绍了自定义ViewGroup的基本流程。这一篇自定义ViewGroup帖子。

八、事件拦截机制分析
           在自定义控件中,事件的分发很重要:
          推荐几个相关精华资源:

  • Android 编程下 Touch 事件的分发和消费机制
  • Android:30分钟弄明白Touch事件分发机制

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

其他精华资源推荐:

24 个回复

倒序浏览
可以的!!!!!
回复 使用道具 举报
进来 顶一下  支持哟
回复 使用道具 举报
进来 顶一下  支持哟
回复 使用道具 举报
支持  支持 受益
回复 使用道具 举报
mark........
回复 使用道具 举报
mark..........
回复 使用道具 举报
涨姿势了
回复 使用道具 举报
谢谢分享
回复 使用道具 举报
马克一下,给楼主点赞
回复 使用道具 举报
顶贴走人
回复 使用道具 举报
好好。。。。。
回复 使用道具 举报
进来顶一下,支持,加油
回复 使用道具 举报
感谢楼主的分享
回复 使用道具 举报
有认真看,自定义View那讲的感觉不是很清楚,我也不太懂,但自定义的View规范讲(进行自定义的控件设计)应该需要重写onMeure类(可写可不写,看需要)和OnDraw类吧
回复 使用道具 举报
赞赞赞。。。。。。。。。
回复 使用道具 举报
赞赞赞赞赞
回复 使用道具 举报
挺好的东西!正好我们用
回复 使用道具 举报
下载下看看,好东西,顶一下
回复 使用道具 举报

进来 顶一下  支持哟
回复 使用道具 举报
12下一页
您需要登录后才可以回帖 登录 | 加入黑马