基于布局类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)的更多相关文章

  1. Android用户界面 UI组件--TextView及其子类(二) Button,selector选择器,sharp属性

    1.XML文件中的OnClick 属性可以指定在Activity中处理点击事件的方法,Activity中必须定义该属性指定的值作为方法的名字且有一个View类型的参数,表示此物件被点击. 2.使用se ...

  2. Android用户界面UI组件--AdapterView及其子类(五) Spinner和SpinnerAdapter

    Spinner就是下拉框组件,可以自定义下拉布局样式,可以使用ArrayAdapter以及SpinnerAdapter适配 在Adapter中实现SpinnerAdapter,继承BaseAdapte ...

  3. Android用户界面 UI组件--AdapterView及其子类(一) ListView及各种Adapter详解

    ListView就是列表组件,一般通过继承ListActivity使用系统提供的ListView. 所有的AdapterView组件都需要有一个对应的Adapter作为适配器来显示列表中元素的布局方式 ...

  4. Android用户界面 UI组件--TextView及其子类(三) EditView以及各种Span文字样式讲解

    EditView和TextView的用法差不多,只是文字可编辑 小技巧: 设置EditText隐藏键盘  setInputType(0); 设置EditText不被输入法遮盖  getWindow() ...

  5. Android用户界面 UI组件--TextView及其子类(一) TextView

    1.TextView android:autoLink设置是否当文本为URL链接/email/电话号码/map时,文本显示为可点击的链接.可选值(none /web/email/phone/map/a ...

  6. Android用户界面 UI组件--自动提示输入框 AutoCompleteTextView和MultiAutoCompleteTextView

    AutoCompleteTextView: 就是一个带自动提示的EditText,当输入字符时,会出现提示. android:completionThreshold  输入几个字符时提示 androi ...

  7. Android用户界面UI组件--AdapterView及其子类(四) GridView

    GridView常用的XML属性: android:columnWidth  设置列的宽度. android:horizontalSpacing  两列之间的间距.  android:numColum ...

  8. Android用户界面UI组件--AdapterView及其子类(三) ExpandableListView

    ExpandableListView: List中的每一项可以展开收缩. 一种伸缩式的ListView. android:cacheColorHint="#00000000" 这个 ...

  9. Android用户界面 UI组件--AdapterView及其子类(二) AdapterViewAnimator及其子类

    AdapterViewAnimator:当在视图间切换时会显示动画. android:animateFirstView 定义ViewAnimation首次显示时是否对当前视图应用动画. android ...

随机推荐

  1. Android studio怎么修改文件名

    选中需要重新命名的文件 点击Android studio菜单中列表中的Refactor的选项 选择下拉菜单中的“rename”的选项 弹出rename的选项框,在输入框中输入需要重新的命名的名称. 点 ...

  2. JVM虚拟机—JVM的类加载机制

    1 什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构 ...

  3. 基于WinIO 3.0实现驱动级键盘模拟输入

    基于WinIO 3.0实现驱动级键盘模拟输入 一个业务场景需要使用驱动级的键盘模拟,折腾了2天,总结一下,为后人节省时间. 限制条件: 1.需要真实PC机,虚拟机不行 2.仅支持PS/2 键盘(指外接 ...

  4. ibatis $与#的区别

    在sql配置中比如in(#rewr#) 与in ($rewr$) 在Ibatis中我们使用SqlMap进行Sql查询时需要引用参数,在参数引用中遇到的符号#和$之间的区分为,#可以进行与编译,进行类型 ...

  5. C#:当前时间转换成文件名

    DateTime.Now.ToFileTime().ToString(); 结果是一个字符串,类似:131238643554094913.

  6. kubeadm方式安装kubernetes

    系统: Ubuntu 18.04.2 LTS 内存: 8G 机器: 属性 IP Hostname ssh  Master  192.168.91.48 blackray-pc     node1  1 ...

  7. clipboard

    我们在网页上放置一个复制按钮,主要用来方便用户复制链接之类的复杂文本,以往的做法是,通过JS依靠Flash,甚至借助jQuery庞大的js库来实现文本复制到剪贴板的.今天我要给大家介绍的是一款极现代的 ...

  8. springboot的Scheduled定时器不工作

    问题情况 使用springboot,使用注解方式启动定时器进行业务调度. 在入口类中加了注解如下: package org.test.xyz; @SpringBootApplication @Enab ...

  9. linux命令(6/9):watch命令

    watch是一个非常实用的命令,基本所有的Linux发行版都带有这个小工具,如同名字一样,watch可以帮你监测一个命令的运行结果,省得你一遍遍的手动运行.在Linux下,watch是周期性的执行下个 ...

  10. 如何修改Django中的日期和时间格式 DateTimeField

    html页面从数据库中读出DateTimeField字段时,显示的时间格式和数据库中存放的格式不一致,比如数据库字段内容为2017-06-03 13:00:00,但是页面显示的却是Apr. 03, 2 ...