声明:本文简述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
View是一个抽象的视图对象(虽然这个类不是抽象的),它定义了一个视图所需具有的属性和基本操作方法。
职责:1、根据测量模式和父容器(ViewGroup)给出的建议的宽和高,计算出自己的宽和高;2、在父容器(ViewGroup)为其指定的区域内绘制自己的形态。
ViewGroup本质上也是一个View,它相当于一个放置View的容器,这个容器的属性可以通过XML布局文件进行配置,比如:宽度(layout_width)、高度(layout_height)、对齐方式(layout_gravity)等。
职责:1、给容器内的childView(View)计算出建议的宽和高和测量模式(为什么只是建议呢,别忘了childView宽和高可以设置为wrap_content,这样只有childView才能计算出自己的宽和高);2、决定childView的位置。
大家可以回忆一下,当在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的身影。
上面提到了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中;此种模式比较少见。(父对象没有强制要求子对象必须遵循哪些约束)
View( Context context)
Simple constructor to use when creating a view from code.
|
|
View( Context context, AttributeSet attrs)
Constructor that is called when inflating a view from XML.
|
|
View( Context context, AttributeSet attrs, int defStyle)
Perform inflation from XML and apply a class-specific base style.
|
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>
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.java MyView控件全部代码如下:
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.1、第一种:需要在原生控件的基本功能上进行扩展,这个时候只需要继承并对控件进行扩展。如,一种可以让TextView中的文字闪光的控件。
3.1.2、第二种:需要几个控件的组合功能,一般是自定义布局,可以用一个类继承一个布局,这个布局中包含多个控件。
3.1.3、第三种:以上两种不能解决,则独立绘制新的控件,直接从View或ViewGroup开始绘制。如,一种水波(地震波)报警动画。