Android自定义控件之基本原理

本文深入探讨Android自定义控件的实现方法,包括View的工作原理、绘制过程及响应用户事件等内容,帮助开发者掌握自定义控件的关键技术。

转载请注明出处!


在Android开发中说到自定义控件,这个应该很熟悉了。在开发过程中原生的控件已经不能满足业务的需求了,这个时候难道改业务需求,,,不可能啦。基于Android的开发,谷歌让我们可以自定义控件以满足业务需求

首先来了解下UI界面中使用的空间VIew树


通过图可发现view可是空间的鼻祖

UI底层界面架构:

android视图最外层是一个window对象
phoneWindow来实现。
phoneWindow将一个decorView作为整个布局的根view.
屏幕分为TitleViewContentView.
ContentView的根布局为framelayout.

自定义控件要求:

     1. 应当遵守Android标准的规范(命名,可配置,事件处理等)。
     2. 在XML布局中可配置控件的属性。
     3. 对交互应当有合适的反馈,比如按下,点击等。
     4. 具有兼容性, Android版本很多,应该具有广泛的适用性。

自定义控件学习步骤:

  1 .View的工作原理 
  2 .编写View类 
  3.为View类增加属性 
  4 .绘制屏幕 
  5. 响应用户消息 
  6 .自定义回调函数

通常自定义控件继承View或者ViewGroup,还可以继承原生的控件进行相应的修改,继承View的自定义控件则不可以包裹
其他控件,主要通过onDraw()方法进行绘画展示画面。。。

Android系统的视图结构的设计也采用了组合模式,即View作为所有图形的基类,Viewgroup对View继承扩展为视图容器类。
View定义了绘图的基本操作
基本操作由三个函数完成:measure()、layout()、draw(),其内部又分别包含了onMeasure()、onLayout()、onDraw()三个子方法。具体操作如下:
1、measure操作
     measure操作主要用于计算视图的大小,即视图的宽度和长度。在view中定义为final类型,要求子类不能修改。measure()函数中又会调用下面的函数:
     (1)onMeasure(),视图大小的将在这里最终确定,也就是说measure只是对onMeasure的一个包装,子类可以覆写onMeasure()方法实现自己的
计算视图大小的方式,并通过setMeasuredDimension(width, height)保存计算结果。
 
2、layout操作
     layout操作用于设置视图在屏幕中显示的位置。在view中定义为final类型,要求子类不能修改。layout()函数中有两个基本操作:
     (1)setFrame(l,t,r,b),l,t,r,b即子视图在父视图中的具体位置,该函数用于将这些参数保存起来;
     (2)onLayout(),在View中这个函数什么都不会做,提供该函数主要是为viewGroup类型布局子视图用的;
 
3、draw操作
     draw操作利用前两部得到的参数,将视图显示在屏幕上,到这里也就完成了整个的视图绘制工作。子类也不应该修改该方法,因为其内部定义了绘图的基本操作:
     (1)绘制背景;
     (2)如果要视图显示渐变框,这里会做一些准备工作;
     (3)绘制视图本身,即调用onDraw()函数。在view中onDraw()是个空函数,也就是说具体的视图都要覆写该函数来实现自己的显示(比如TextView在这里实现了绘制文字的过程)。
而对于ViewGroup则不需要实现该函数,因为作为容器是“没有内容“的,其包含了多个子view,而子View已经实现了自己的绘制方法,因此只需要告诉子view绘制自己就可以了,也就是下面的dispatchDraw()方法;
     (4)绘制子视图,即dispatchDraw()函数。在view中这是个空函数,具体的视图不需要实现该方法,它是专门为容器类准备的,也就是容器类必须实现该方法;
     (5)如果需要(应用程序调用了setVerticalFadingEdge或者setHorizontalFadingEdge),开始绘制渐变框;
     (6)绘制滚动条;
      从上面可以看出自定义View需要最少覆写onMeasure()和onDraw()两个方法。

View的构造方法

创建自定义控件的3种主要实现方式:
1)继承已有的控件来实现自定义控件: 主要是当要实现的控件和已有的控件在很多方面比较类似, 通过对已有控件的扩展来满足要求。
2)通过继承一个布局文件实现自定义控件,一般来说做组合控件时可以通过这个方式来实现。
    注意此时不用onDraw方法,在构造广告中通过inflater加载自定义控件的布局文件,再addView(view),自定义控件的图形界面就加载进来了。
3)通过继承view类来实现自定义控件,使用GDI绘制出组件界面,一般无法通过上述两种方式来实现时用该方式


view的测量:

view的测量通过onMesure()来进行的:
onMesure用来确定视图大小和位置。
MesureSpec用来帮助我们测量view.
  1. Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            // TODO Auto-generated method stub
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);调用setMeasureredDimension(int width,int height);将测量后的数据设置进去。

    测量模式:
    EXACTLY
    当视图控件精确确定大小的时候,系统使用该模式,精确模式。默认支持这种模式。
    AT_MOST
    控件的layout_width和layout_height设置为wrap_layout的时候,控件尺寸不超过父控件大小。   
    UNSPECIFIED
    自定义控件的使用使用,不指定view大小。
如何获取测量模式和测量大小:
  1. int specMode =MeasureSpec.getMode(measureSpec);
    int specsize =MeasureSpec.getSize(measureSpec);
  2. view的布局:
  3. View的布局主要是在onLayout中对子控件进行摆放
  4. public void layout(int l, int t, int r, int b)
    protected boolean setFrame(int left, int top, int right, int bottom)
    protected void onLayout(boolean changed, int left, int top, int right, int bottom)

    layout通过调用setFrame(l,t,r,b),l,t,r,b即子视图在父视图中的具体位置,onLayout一般只会在自定义ViewGroup中才会使用

  5. view的绘制:
View的绘制主要是在onDraw中通过canvas(画布)和paint(画笔)来进行绘制操作。
如下是绘制圆形ImageView里面onDraw方法里面的设置:
  1. @Override
        protected void onDraw(Canvas canvas) {
            if (getDrawable() == null) {
                return;
            }
            canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius,
                    mBitmapPaint);
            if (mBorderWidth != 0) {
                canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius,
                        mBorderPaint);
            }
        }
canvas当你重写onDraw方法的时候由系统提供,通过这个对象来进行绘制操作。
  1. package com.android.myview;
  2. import android.os.Bundle;
  3. import android.app.Activity;
  4. import android.content.Context;
  5. import android.graphics.Canvas;
  6. import android.graphics.Color;
  7. import android.graphics.Paint;
  8. import android.graphics.Paint.Style;
  9. import android.graphics.Rect;
  10. import android.util.AttributeSet;
  11. import android.view.Menu;
  12. import android.view.View;
  13.  
  14. public class MainActivity extends View {
  15.     private Paint paint;
  16.     private String text="text";
  17.  
  18.     public MainActivity(Context context, AttributeSet attrs) {
  19.         super(context, attrs);
  20.         // TODO Auto-generated constructor stub
  21.     }
  22.      
  23.     protected void onDraw(Canvas canvas) {
  24.         super.onDraw(canvas);
  25.         paint=new Paint();   //画笔
  26.         paint.setColor(Color.BLUE);  // 画笔颜色
  27.         paint.setStyle(Style.FILL);   //fill填充
  28.         canvas.drawRect(new Rect(10, 10, 100, 100), paint);
  29.          
  30.         paint.setColor(Color.GREEN); 
  31.         paint.setTextSize(35.0f); 
  32.         canvas.drawText(text, 10, 60, paint);
  33.          
  34.     }
  35.  
  36. }
  37.  
  38. <?xml version="1.0" encoding="utf-8"?>
  39. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  40.     android:layout_width="match_parent"
  41.     android:layout_height="match_parent"
  42.     android:orientation="vertical" >
  43.   
  44.     <com.android.myview.MainActivity
  45.         android:id="@+id/myview"
  46.          android:layout_width="wrap_content"  
  47.          android:layout_height="wrap_content"  
  48.         />
  49. </LinearLayout>



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值