Android 用户界面---定制组件(Custom Components)
基于布局类View和ViewGroup的基本功能,Android为创建自己的UI界面提供了先进和强大的定制化模式。首先,平台包含了各种预置的View和ViewGroup子类---Widget和layout,可以使用它们来构造自己的UI界面。
部分的可以利用的widget包括:Button、TextView、EditText、ListView、CheckBox、RadioButton、Gallery、Spinner、以及比较特殊用途的AutoCompleteTextView、ImageSwitcher和TextSwitcher。
其中可利用的布局是:LinearLayout、FrameLayout、RelativeLayout以及其他的布局。更多的例子请看“共通布局对象”。http://developer.android.com/guide/topics/ui/layout-objects.html
如果遇到了没有预置的widget或layout的需求,可以创建自己的View子类。如果只需要对既存的widget或layout进行小的调整,那么只需简单的继承widget或layout,并且重写它们的方法。
创建自己的View子类,以便能够精准的控制屏幕元素的外观和功能。以下是用定制View对象来实现这种控制想法的一些例子:
1. 创建一个完全的定制化渲染的View类型,如用类似模拟电子控制的2D图形来渲染的音量控制按钮。
2. 把一组View组件组合成一个新的单一组件,制作一些像ComboBox(一个下拉列表和文本输入域的组合)、双面板选择器(左右两个列表面板,右边的列表面板中的项目与左边列表面板中的一个项目相关联)等组件。
3. 重写一个EditText组件在屏幕上的渲染的方法。
4. 捕获一些像按键一样的事件,并在某些定制的方法中处理它们(如游戏)。
基本方法
以下是创建自定义View组件需要要了解基本概要:
1. 自定义的View类要继承一个既存的View类或其子类;
2. 在子类重写父类的一些方法。要覆写的父类方法是用‘on’开头的,例如,onDraw()、onMeasure()和onKeyDown()等,这有点类似于重写Activity或ListActivity的生存周期回调的on…事件。
3. 使用新的扩展类,一旦完成,新扩展的类就能被用于替换基本的View对象。
提示:扩展类能够作为使用它们的Acticity的内部类来定义。这样对控制对它们的访问是有益的,当然可以创建一个新的公共的View类,这样就可以在应用程序范围内来使用。
完全定制化的组件
完全定制化的组件能够用于创建你所期望的显示效果的图形化组件。可以是看上去像旧的模拟仪表的图形化VU仪表,或者是一个长的歌词视图,有一个跳动的球沿着歌词移动,以便跟着这卡拉OK机歌唱,这两种情况,无论如何组织内置的组件都无法满足要求。
幸运的是,能够使用任意自己喜欢的方法来创建组件的外观和行为,唯一的限制就是你的想象力、屏幕的尺寸和可利用的处理能力(因为应用程序最终可能运行在比桌面工作站处理能力要弱的设备上)。
以下是创建完全定制组件的步骤:
1. 毋庸置疑,能够扩展的最通用的视图是View类,因此通常是继承这个View类来创建自己的新的组件;
2. 提供一个能够从XML中获取属性和参数的构造器,并且也能够使用自己属性和参数(如VU仪表的颜色和范围,指针的宽度和阻尼等);
3. 创建组件中可能的事件监听器、属性访问器和修饰符以及尽可能准确的行为等;
4. 覆写onMeasure()回调方法,如果想要组件显示一些东西,也要覆写onDraw()回调。虽然它们都有默认的行为,onDraw()回调默认什么也不做,onMeasure()方法默认的要设置组件的尺寸为100x100;
5. 覆写其他的需要on…方法。
扩展onDraw()和onMeasure()
onDraw()方法会把能够实现的任何想要的东西放到一个Canvas对象上,如2D图形、标准或定制的组件、样式化的文本、或其他任何能够想到的东西。
注意:View类不能使用3D图形。如果要使用3D图形,必须继承SurfaceView类,而不是View类,并且要在一个独立的线程中描画。
onMeasure()方法有点复杂,它是组件和它的容器之间的渲染约束的关键部分。覆写onMeasure(),以便准确高效的报告组件被包含部分的尺寸。由于来自父容器限制的要求,使得尺寸的测量有些复杂,并且组件的尺寸一旦被计算完成,就要调用setMeasureDimension()方法来保存测量的宽度和高度。如果在onMeasure()方法中调用setMeasureDimension()方法失败,这个结果在测量时将是一个异常的值。
在上层看,实现onMeasure()方法的步骤如下:
1. 要用父容器的宽度和高度的计量规格来调用被覆写的onMensure()方法(widthMeasureSpec和heightMeasureSpec参数都是代表了尺寸的整数),这两个参数应该作为生成组件的宽度和高度的约束要求。对于这些规格约束类型的完整说明可以在View类说明的View.onMeasure(int,int)方法中找到。
2. 组件的onMeasure()方法应该计算用于渲染组件所需的尺寸(宽度和高度)。组件应该尽量保留在被传入的规格范围内,尽管它能够选择超出规格范围(在这种情况下,父容器能够选择做的事情包括:裁剪、滚动、抛出异常、或者要求onMeasure()方法用不同的尺寸规格再试)。
3. 一旦组件的宽度和高度被计算完成,就必须调用setMeasuredDimension(int width, int height)方法来保存计算结果。不这样做就会抛出一个异常。
下表是framework调用View类的其他标准方法:
分类 |
方法 |
说明 |
Creation |
Constructors |
构造器的调用有两种类型:1.在代码中创建View对象;2.用布局文件填充View对象。第二种类型应该解析和应用布局文件中的任何属性定义。 |
onFinishInflate() |
View对象和它的所有子对象都用XML填充完之后,调用这个方法。 |
|
Layout |
onMeasure(int, int) |
调用这个方法决定View对象及其所有子对象的尺寸要求。 |
onLayout(boolean,int,int,int,int) |
当View对象给它的所有子对象分配尺寸和位置时,调用这个方法。 |
|
onSizeChanged(int,int,int,int) |
当View对象的尺寸发生改变时,调用这个方法。 |
|
Drawing |
onDraw(Canvas) |
当View对象渲染它的内容时,调用这个方法。 |
Event |
onKeyDown(int,KeyEvent) |
当一个键的按下事件发生时,调用这个方法 |
onKeyUp(int,KeyEvent) |
当一个键弹起事件发生时,调用这个方法 |
|
onTrackballEvent(MotionEvent) |
当鼠标轨迹球滚动事件发生时,调用这个方法。 |
|
onTouchEvent(MotionEvent) |
当触屏事件发生时,调用这个方法。 |
|
Focus |
onFocusChanged(boolean,int,Rect) |
当View对象获取或失去焦点时,调用这个方法。 |
onWindowFocusChanged(boolean) |
当包含View对象的窗口获得或失去焦点时,调用这个方法。 |
|
Attaching |
onAttachedToWindow() |
当View对象被绑定到一个窗口时,调用这个方法。 |
onDetachedFromWindow() |
当View对象被从它的窗口中分离的时候,调用这个方法。 |
|
onWindowVisibilityChanged(int) |
当包含View对象的窗口的可见性发生改变时,调用这个方法。 |
|
定制View的例子
在API Demos中提供了一个定制的View对象的例子:CustomView。这个定制的View定义在LabelView类中。
LabelView示例展示了很多定制组件的不同特征:
1. 继承View类的完全定制化的组件;
2. 参数化的带有View填充参数(在XML中定义的参数)方式构造View对象。有一些填充参数使用通过这个View的父类传递过来的,还有一些用于labelView对象而定义的定制的属性;
3. 你所期望看到的标准的公共类型的方法,如setText()、setTextSize()、setTextColor()等等;
4. 一个重写的onMeasure()方法,它决定和设置了组件的渲染尺寸。(注意:在LabelView类中,实际的工作是由一个私有的measureWidth()方法来做的。)
5. 一个重写的onDraw()方法,它在提供的Canvas上描画标签。
从这个示例的custom_view_1.xml中,能够看到一些LabelView定制View的用法。实际上,可以看到android:命名空间参数和定制的app:命名空间的组合。这些app:参数是LabelView类所承认的并用于工作的一些定制化的属性,并且这些参数在示例的R资源定义类的styleable内部类中被定义。
- import android.content.Context;
- import android.content.res.TypedArray;
- import android.graphics.Canvas;
- import android.graphics.Color;
- import android.graphics.Paint;
- import android.util.AttributeSet;
- import android.view.View;
- public class CrossView extends View {
- private float mRotation;
- private Paint mPaint;
- public CrossView( Context context, AttributeSet attrs ) {//AttributeSet对象在实例化时被系统传给了视图
- super( context, attrs );
- //start 创建paint对象
- mPaint = new Paint();
- mPaint.setAntiAlias( true );
- mPaint.setColor( 0xFFFFFFFF );
- //end 创建paint对象
- //使用obtainStyledAttributes方法来创建一个TypedArray,这是访问存储于AttributeSet中的值的一个方便类,这类执行内部缓存,
- //所以当你结束使用它之后随时调用回收函数。注意:需同时使用<declare-styleable>名称和<arr>名称的访问自定义属性
- TypedArray arr = getContext().obtainStyledAttributes( attrs,R.styleable.cross );
- int color = arr.getColor( R.styleable.cross_android_color, Color.WHITE );
- float rotation = arr.getFloat( R.styleable.cross_rotation, 0f );
- //remember to call this when finished
- arr.recycle();
- setColor(color);
- setRotation(rotation);
- }
- private void setRotation( float rotation ) {
- // TODO Auto-generated method stub
- mRotation = rotation;
- }
- private void setColor( int color ) {
- // TODO Auto-generated method stub
- mPaint.setColor( color );
- }
- //重写onMeasure方法
- @Override
- protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec ) {
- // TODO Auto-generated method stub
- super.onMeasure( widthMeasureSpec, heightMeasureSpec );
- //需要使用计算好的宽和高的值作为该方法的实参
- setMeasuredDimension( calculateMeasure(widthMeasureSpec), calculateMeasure(heightMeasureSpec) );
- }
- float[] mPoints = {0.5f,0f,0.5f,1f,0f,0.5f,1f,0.5f};
- @Override
- protected void onDraw( Canvas canvas ) {
- // TODO Auto-generated method stub
- super.onDraw( canvas );
- canvas.save();//所有的在画布上绘图的调用都应当受对应的sava()和restore()的约束
- int scale = getWidth();
- canvas.scale( scale, scale );
- canvas.rotate( mRotation );
- canvas.drawLines( mPoints, mPaint );//绘制十字的两条线
- canvas.restore();//所有的在画布上绘图的调用都应当受对应的sava()和restore()的约束
- }
- private static final int DEFAULT_SIZE = 100;//默认的试图尺寸
- //实现计算测量值的代码
- private int calculateMeasure(int measureSpec){
- int result = ( int ) ( DEFAULT_SIZE*getResources().getDisplayMetrics().density );
- int specMode = MeasureSpec.getMode( measureSpec );//在MeasureSpec中检索模式
- int specSize = MeasureSpec.getSize( measureSpec );//在MeasureSpec中检索尺寸
- //基于模式选择尺寸
- if(specMode == MeasureSpec.EXACTLY){
- result = specSize;
- }else if(specMode == MeasureSpec.AT_MOST){
- result = Math.min( result, specSize );
- }
- return result;
- }
- }
- (二)自定义属性
- <?xml version="1.0" encoding="utf-8"?>
- <!-- 该文件被放置在res/values/目录下 -->
- <resources>
- <!-- 声明属性 -->
- <declare-styleable name="cross">
- <attr name="android:color"/>
- <attr name="rotation" format="string"/>
- </declare-styleable>
- <!--
- <attr name="test" format="string"/>
- <declare-styleable name="foo">
- <attr name="test"/>
- </declare-styleable>
- <declare-styleable name="bar">
- <attr name="test"/>
- </declare-styleable> -->
- </resources>
- (三)在XML中使用自定义View
- <?xml version="1.0" encoding="utf-8"?>
- <!-- 要使用在XML中的自定义属性,首先必须为视图声明命名空间 -->
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:example="http://schemas.android.com/apk/res/com.example"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <!-- 添加CrossView -->
- <com.example.CrossView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- />
- <com.example.CrossView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- example.rotation="30"
- android:color="#0000FF"/>
- />
- <com.example.CrossView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- example.rotation="40"
- android:color="#FFFF00"
- />
- </LinearLayout>
Android 用户界面---定制组件(Custom Components)的更多相关文章
- Android用户界面 UI组件--TextView及其子类(二) Button,selector选择器,sharp属性
1.XML文件中的OnClick 属性可以指定在Activity中处理点击事件的方法,Activity中必须定义该属性指定的值作为方法的名字且有一个View类型的参数,表示此物件被点击. 2.使用se ...
- Android用户界面UI组件--AdapterView及其子类(五) Spinner和SpinnerAdapter
Spinner就是下拉框组件,可以自定义下拉布局样式,可以使用ArrayAdapter以及SpinnerAdapter适配 在Adapter中实现SpinnerAdapter,继承BaseAdapte ...
- Android用户界面 UI组件--AdapterView及其子类(一) ListView及各种Adapter详解
ListView就是列表组件,一般通过继承ListActivity使用系统提供的ListView. 所有的AdapterView组件都需要有一个对应的Adapter作为适配器来显示列表中元素的布局方式 ...
- Android用户界面 UI组件--TextView及其子类(三) EditView以及各种Span文字样式讲解
EditView和TextView的用法差不多,只是文字可编辑 小技巧: 设置EditText隐藏键盘 setInputType(0); 设置EditText不被输入法遮盖 getWindow() ...
- Android用户界面 UI组件--TextView及其子类(一) TextView
1.TextView android:autoLink设置是否当文本为URL链接/email/电话号码/map时,文本显示为可点击的链接.可选值(none /web/email/phone/map/a ...
- Android用户界面 UI组件--自动提示输入框 AutoCompleteTextView和MultiAutoCompleteTextView
AutoCompleteTextView: 就是一个带自动提示的EditText,当输入字符时,会出现提示. android:completionThreshold 输入几个字符时提示 androi ...
- Android用户界面UI组件--AdapterView及其子类(四) GridView
GridView常用的XML属性: android:columnWidth 设置列的宽度. android:horizontalSpacing 两列之间的间距. android:numColum ...
- Android用户界面UI组件--AdapterView及其子类(三) ExpandableListView
ExpandableListView: List中的每一项可以展开收缩. 一种伸缩式的ListView. android:cacheColorHint="#00000000" 这个 ...
- Android用户界面 UI组件--AdapterView及其子类(二) AdapterViewAnimator及其子类
AdapterViewAnimator:当在视图间切换时会显示动画. android:animateFirstView 定义ViewAnimation首次显示时是否对当前视图应用动画. android ...
随机推荐
- 购物单问题—WPS使用excel
**** 180.90 88折 **** 10.25 65折 **** 56.14 9折 **** 104.65 ...
- 用仿ActionScript的语法来编写html5——第六篇,TextField与输入框
一,对比1,html5中首先看看在html5的canvas中的文字显示 var canvas = document.getElementById("myCanvas"); var ...
- python学习之路-第七天-python面向对象编程简介
面向对象编程 在python中,同样是类和对象作为重要的组成部分. 而且在python中基本数据类型如int都是有封装类的,都有自己的方法,应该是和java里面的Integer类似吧 类包括域和方法: ...
- Oracle DG强制激活 备库
在实际运营环境中,我们经常碰到类似这样的需求,譬如想不影响现网业务评估DB补丁在现网环境中运行的时间,或者是想在做DB切换前想连接Standby DB做实际业务运行的测试,如果在9i版本的时候,想做到 ...
- Spark生态系统剖析--王家林老师
- Delphi 正则表达式之TPerlRegEx 类的属性与方法(7): Split 函数
Delphi 正则表达式之TPerlRegEx 类的属性与方法(7): Split 函数 //字符串分割: Split var reg: TPerlRegEx; List: TStrings; ...
- service 需要注意的地方
@service标记的class,只能用于标记了@controller的类,用于其他的会出错 mybatis查询查询到返回记录,查询不到返回null
- angularjs中directive声明scope对象的用法
总的来说用法 分三种: >1: scope: false --> 继承父域,实现 双向数据绑定 示例代码 可自测: <!DOCTYPE html> <html lang ...
- Asp.Net中OnClientClick与OnClick的区别
当我们当击这个按钮时,自动先执行的客户端,再执行服务器端的.如果客户端返回的是false,那么服务器端对应的方法永远不会执行.这样就达到检测,只有通过才去执行服务器端的方法.
- 用VS2013编译FFMPEG232
http://blog.csdn.net/finewind/article/details/38854517 如果只是拿来使用,网上有现成的SDK.但我是想深入研究FFMPEG代码,又不熟悉Linux ...