声明:本文简述Android应用开发中,自定义组件的实现方式,参考和查阅部分资料,整理而成。

参考资料:

Android 手把手教您自定义ViewGroup:http://blog.csdn.net/lmj623565791/article/details/38339817

Android自定义控件:http://blog.163.com/ppy2790@126/blog/static/103242241201382210910473/

Android自定义控件开发入门:http://blog.csdn.net/sunmc1204953974/article/details/38456791

一、准备知识

1、View、ViewGroup

View是一个抽象的视图对象(虽然这个类不是抽象的),它定义了一个视图所需具有的属性和基本操作方法。

职责:1、根据测量模式和父容器(ViewGroup)给出的建议的宽和高,计算出自己的宽和高;2、在父容器(ViewGroup)为其指定的区域内绘制自己的形态。

ViewGroup本质上也是一个View,它相当于一个放置View的容器,这个容器的属性可以通过XML布局文件进行配置,比如:宽度(layout_width)、高度(layout_height)、对齐方式(layout_gravity)等。

职责:1、给容器内的childView(View)计算出建议的宽和高和测量模式(为什么只是建议呢,别忘了childView宽和高可以设置为wrap_content,这样只有childView才能计算出自己的宽和高);2、决定childView的位置。

Android 自定义组件 方法篇_第1张图片
这种灵活的View层次结构可以形成非常复杂的UI布局,开发者可据此设计、开发非常精致的UI界面。

2、ViewGroup和LayoutParams之间的关系

大家可以回忆一下,当在LinearLayout中写childView的时候,可以写layout_gravity,layout_weight属性;在RelativeLayout中的childView有layout_centerInParent属性,却没有layout_gravity,layout_weight,这是为什么呢?这是因为每个ViewGroup需要指定一个LayoutParams,用于确定支持childView支持哪些属性,比如LinearLayout指定LinearLayout.LayoutParams等。如果大家去看LinearLayout的源码,会发现其内部定义了LinearLayout.LayoutParams,在此类中,你可以发现weight和gravity的身影。

3、View的3种测量模式

上面提到了ViewGroup会为childView指定测量模式,下面简单介绍下三种测量模式:

EXACTLY=1:表示设置了精确的值,一般当childView设置其宽、高为精确值、match_parent时,ViewGroup会将其设置为EXACTLY;(父对象要求子对象必须严格按照它给定的值来约束自己)

AT_MOST=2:表示子布局被限制在一个最大值内,一般当childView设置其宽、高为wrap_content时,ViewGroup会将其设置为AT_MOST;(子对象可以自行选择给定范围内的值)

UNSPECIFIED=0:表示子布局想要多大就多大,一般出现在AadapterView的item的heightMode中、ScrollView的childView的heightMode中;此种模式比较少见。(父对象没有强制要求子对象必须遵循哪些约束)

二、原理介绍

2.1、View结构原理

Android系统的视图结构的设计也采用了组合模式,即View作为所有图形的基类,Viewgroup对View继承扩展为视图容器类。

View定义了绘图的基本操作 基本操作由三个函数完成:measure()、layout()、draw(),其内部又分别包含了onMeasure()、onLayout()、onDraw()三个子方法。具体操作如下: 2.1.1、measure操作 measure操作主要用于计算视图的大小,即视图的宽度和长度。在view中定义为final类型,要求子类不能修改。measure()函数中又会调用下面的函数: (1)onMeasure(),视图大小的将在这里最终确定,也就是说measure只是对onMeasure的一个包装,子类可以覆写onMeasure()方法实现自己的计算视图大小的方式,并通过setMeasuredDimension(width, height)保存计算结果。
2.1.2、layout操作 layout操作用于设置视图在屏幕中显示的位置。在view中定义为final类型,要求子类不能修改。layout()函数中有两个基本操作: (1)setFrame(l,t,r,b),l,t,r,b即子视图在父视图中的具体位置,该函数用于将这些参数保存起来; (2)onLayout(),在View中这个函数什么都不会做,提供该函数主要是为viewGroup类型布局子视图用的;
2.1.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()两个方法。

2.2、View类的构造方法

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

View( Contextcontext) Simple constructor to use when creating a view from code.
View( Contextcontext, AttributeSetattrs) Constructor that is called when inflating a view from XML.
View( Contextcontext, AttributeSetattrs, int defStyle) Perform inflation from XML and apply a class-specific base style.

2.3、给自定义View增加属性

2.3.1、在res/values文件下定义一个attrs.xml文件。

    <?xml version="1.0" encoding="utf-8"?>      <resources>          <declare-styleable name="MyView">              <attr name="textColor" format="color" />              <attr name="textSize" format="dimension" />          </declare-styleable>      </resources>  

2.3.2、在 MyView.java代码编写如下,其中下面的构造方法是重点,我们获取定义的属性 R.sytleable.MyView_textColor,获取方法中后面通常设定默认值( float textSize = a.getDimension(R.styleable.MyView_textSize,36);) 防止我们在 xml文件中没有定义.从而使用默认值!

MyView就是定义在<declare-styleable name="MyView"></declare-styleable>里的 名字,获取里面属性用名字_ 属性连接起来就可以.TypedArray通常最后调用.recycle()方法,为了保持以后使用该属性一致性!

    public MyView(Context context,AttributeSet attrs)          {              super(context,attrs);              mPaint = new Paint();                            TypedArray a = context.obtainStyledAttributes(attrs,                      R.styleable.MyView);                            int textColor = a.getColor(R.styleable.MyView_textColor,                      0XFFFFFFFF);              float textSize = a.getDimension(R.styleable.MyView_textSize, 36);                            mPaint.setTextSize(textSize);              mPaint.setColor(textColor);                            a.recycle();          }  
MyView.javaMyView控件全部代码如下:

    package com.android.tutor;      import android.content.Context;      import android.content.res.TypedArray;      import android.graphics.Canvas;      import android.graphics.Color;      import android.graphics.Paint;      import android.graphics.Rect;      import android.graphics.Paint.Style;      import android.util.AttributeSet;      import android.view.View;      public class MyView extends View {          private Paint mPaint;          private Context mContext;          private static final String mString = "Welcome to Mr Wei's blog";                    public MyView(Context context) {              super(context);              mPaint = new Paint();          }          public MyView(Context context,AttributeSet attrs)          {              super(context,attrs);              mPaint = new Paint();                            TypedArray a = context.obtainStyledAttributes(attrs,                      R.styleable.MyView);                            int textColor = a.getColor(R.styleable.MyView_textColor,                      0XFFFFFFFF);              float textSize = a.getDimension(R.styleable.MyView_textSize, 36);                            mPaint.setTextSize(textSize);              mPaint.setColor(textColor);                            a.recycle();          }          @Override          protected void onDraw(Canvas canvas) {              // TODO Auto-generated method stub              super.onDraw(canvas);              //设置填充              mPaint.setStyle(Style.FILL);                            //画一个矩形,前俩个是矩形左上角坐标,后面俩个是右下角坐标              canvas.drawRect(new Rect(10, 10, 100, 100), mPaint);                            mPaint.setColor(Color.BLUE);              //绘制文字              canvas.drawText(mString, 10, 110, mPaint);          }      }  
2.3.3、将自定义的 MyView加入布局 main.xml文件中,并且使用自定义属性,自定义属性必须加上:

"xmlns:test="http://schemas.android.com/apk/res/com.android.tutor" ,test是自定义属性的前缀, com.android.tutor是包名.

main.xml全部代码如下:

    <?xml       version="1.0" encoding="utf-8"?>      <LinearLayout       xmlns:android="http://schemas.android.com/apk/res/android"                          xmlns:test="http://schemas.android.com/apk/res/com.android.tutor"          android:orientation="vertical"          android:layout_width="fill_parent"          android:layout_height="fill_parent"          >      <TextView            android:layout_width="fill_parent"           android:layout_height="wrap_content"           android:text="@string/hello"          />      <com.android.tutor.MyView          android:layout_width="fill_parent"           android:layout_height="fill_parent"           test:textSize="20px"          test:textColor="#fff"      />      </LinearLayout>  

三、方法介绍

3.1、自定义控件的实现方式:

3.1.1、第一种:需要在原生控件的基本功能上进行扩展,这个时候只需要继承并对控件进行扩展。如,一种可以让TextView中的文字闪光的控件。

3.1.2、第二种:需要几个控件的组合功能,一般是自定义布局,可以用一个类继承一个布局,这个布局中包含多个控件。

3.1.3、第三种:以上两种不能解决,则独立绘制新的控件,直接从View或ViewGroup开始绘制。如,一种水波(地震波)报警动画。

3.1、开发自定义控件的步骤:

  • 了解View的工作原理
  • 编写继承自View的子类
  • 为自定义View类增加属性
  • 绘制控件
  • 响应用户消息
  • 自定义回调函数

3.2、自定义View的常用方法

  • onFinishInflate() 回调方法,当应用从XML加载该组件并用它构建界面之后调用的方法
  • onMeasure() 检测View组件及其子组件的大小时回调。
  • onLayout() 当该组件需要分配其子组件的位置、大小时回调。View类中布局发生改变时会调用的方法,这个方法是所有View、ViewGroup及其派生类都具有的方法,重载该类可以在布局发生改变时作定制处理,这在实现一些特效时非常有用。
  • onSizeChange() 当该组件的大小被改变时
  • onDraw() 当组件将要绘制它的内容时回调。View类中用于重绘的方法,这个方法是所有View、ViewGroup及其派生类都具有的方法,也是AndroidUI绘制最重要的方法。开发者可重载该方法,并在重载的方法内部基于参数canvas绘制自己的各种图形、图像效果。
  • onKeyDown 当按下某个键盘时
  • onKeyUp 当松开某个键盘时
  • onTrackballEvent 当发生轨迹球事件时
  • onTouchEvent 当发生触屏事件时
  • onWindowFocusChanged(boolean) 当该组件得到、失去焦点时
  • onAtrrachedToWindow() 当把该组件放入到某个窗口时
  • onDetachedFromWindow() 当把该组件从某个窗口上分离时触发的方法
  • onWindowVisibilityChanged(int): 当包含该组件的窗口的可见性发生改变时触发的方法

更多相关文章

  1. Android MediaPlayer 常用方法介绍
  2. 整理:Android自带、第三方需要添加依赖的控件
  3. Android Interface(UI) 界面控件简单介绍01
  4. android 自定义导航控件
  5. android中HttpURLConnection调用getResponseCode()时崩溃 解决方
  6. Android 控件之Gallery图片集
  7. Android 数据绑定视图 dataBinding
  8. Android自学笔记(Android x86模拟器Intel Atom x86 System Image
  9. android Button组件的属性和方法

随机推荐

  1. Android(安卓)EditView 属性大全
  2. Android属性
  3. 2013.11.29 ——— android layout不显示
  4. android adb shell 命令大全
  5. android的init.rc文件的语法
  6. Android图形系统的底层实现
  7. Android动态加载——加载已安装APK中的类
  8. 如果Imageview与Linearlayout有叠加且可
  9. Android(安卓)adb shell 命令大全
  10. 环境变量ANDROID_SDK_HOME的作用