Android内置了很多View,包括:
- TextView
- EditText
- Chronometer
- ListView
- Spinner
- Button
- ToggleButton
- ImageButton
- CheckBox
- RedioButton
- ViewFlipper
- VideoView
- QuickContactBadge
- ViewPager
关于View更多的信息,可以参考:http://developer.android.com/guide/tutorials/views/index.html
创建自定义View的三种方式
总的来说,有三种方法来创建新的View:
- 修改已有的View。
- 使用多个已有的View组成新的复合View
- 从头新建一个View
修改已有的View
创建某种View的子类并重写父类的方法,即可通过修改已有View来创建自定义的View,例如:
import android.content.Context; import android.graphics.Canvas; import android.util.AttributeSet; import android.view.KeyEvent; import android.widget.TextView; public class MyTextView extends TextView { public MyTextView (Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public MyTextView (Context context) { Super(context); } public MyTextView (Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onKeyDown(int keyCode, KeyEvent keyEvent) { [ ... Perform some special processing ... ] [ ... based on a particular key press ... ] // Use the existing functionality implemented by // the base class to respond to a key press event. return super.onKeyDown(keyCode, keyEvent); } }
创建复合控件View
复合控件由多个View组成,是ViewGroup的子类。通过继承ViewGroup并重写父类的构造函数,在构造过程中inflate某个Layout,是常用的生成复合控件的方法之一,例如:
public class ClearableEditText extends LinearLayout { EditText editText; Button clearButton; public ClearableEditText(Context context) { super(context); // Inflate the view from the layout resource. String infService = Context.LAYOUT_INFLATER_SERVICE; LayoutInflater li; li = (LayoutInflater)getContext().getSystemService(infService); li.inflate(R.layout.clearable_edit_text, this, true); // Get references to the child controls. editText = (EditText)findViewById(R.id.editText); clearButton = (Button)findViewById(R.id.clearButton); // Hook up the functionality hookupButton(); } }
配置文件:
<?xml version=”1.0” encoding=”utf-8”?> <LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android” android:orientation=”vertical” android:layout_width=”match_parent” android:layout_height=”wrap_content”> <EditText android:id=”@+id/editText” android:layout_width=”match_parent” android:layout_height=”wrap_content” />
<Button android:id=”@+id/clearButton” android:layout_width=”match_parent” android:layout_height=”wrap_content” android:text=”Clear” /> </LinearLayout>
使用Layout文件创建复合控件
使用<include>标签可以方便地复用已有的复合View,例如:
<include layout=”@layout/clearable_edit_text” android:id=”@+id/add_new_entry_input” android:layout_width=”match_parent” android:layout_height=”wrap_content” android:layout_gravity=”top”/>
创建自定义View
通过继承View或SurfaceView,程序员就可以实现自定义的View。View类有一个Canvas对象,可以在上面绘制自己的UI。SurfaceView类有一个Surface对象,Surface支持后台线程绘制,并可以使用OpenGL进行绘制。对于需要经常重绘并且不需要3D效果的UI,推荐使用轻量级的View来实现。
创建新的View
View中的OnMeasure()方法用于确定View的长和高,OnDraw()方法则用于绘制图形,下面是创建一个自定义View的示例代码:
public class MyView extends View { // Constructor required for in-code creation public MyView(Context context) { super(context); } // Constructor required for inflation from resource file public MyView (Context context, AttributeSet ats, int defaultStyle) { super(context, ats, defaultStyle ); } //Constructor required for inflation from resource file public MyView (Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onMeasure(int wMeasureSpec, int hMeasureSpec) { int measuredHeight = measureHeight(hMeasureSpec); int measuredWidth = measureWidth(wMeasureSpec); // MUST make this call to setMeasuredDimension or you will cause a runtime exception when the control is laid out. setMeasuredDimension(measuredHeight, measuredWidth); } private int measureHeight(int measureSpec) { int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); [ ... Calculate the view height ... ] return specSize; } private int measureWidth(int measureSpec) { int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); [ ... Calculate the view width ... ] return specSize; } @Override protected void onDraw(Canvas canvas) { [ ... Draw your visual interface ... ] } }
重写onDraw()方法
@Override protected void onDraw(Canvas canvas) { // Get the size of the control based on the last call to onMeasure. int height = getMeasuredHeight(); int width = getMeasuredWidth(); // Find the center int px = width/2; int py = height/2; // Create the new paint brushes. // NOTE: For efficiency this should be done in the views’s constructor Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mTextPaint.setColor(Color.WHITE); // Define the string. String displayText = “Hello World!”; // Measure the width of the text string. float textWidth = mTextPaint.measureText(displayText); // Draw the text string in the center of the control. canvas.drawText(displayText, px-textWidth/2, py, mTextPaint); }
重写OnMeasure()方法
Android中View的默认大小是100*100像素,重写OnMeasure()方法,可以实现自定义长和宽:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int measuredHeight = measureHeight(heightMeasureSpec); int measuredWidth = measureWidth(widthMeasureSpec); setMeasuredDimension(measuredHeight, measuredWidth); } private int measureHeight(int measureSpec) { // Return measured widget height. } private int measureWidth(int measureSpec) { // Return measured widget width. }
处于效率的考虑,measureSpec和measureSpec直接作为int参数传给了onMeasure(),但在使用之前,首先要decode:
int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec);
下面的代码是一个典型的处理Measure的例子:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int measuredHeight = measureHeight(heightMeasureSpec); int measuredWidth = measureWidth(widthMeasureSpec); setMeasuredDimension(measuredHeight, measuredWidth); } private int measureHeight(int measureSpec) { int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); // Default size if no limits are specified. int result = 500; if (specMode == MeasureSpec.AT_MOST) { // Calculate the ideal size of your control within this maximum size. // If your control fills the available space return the outer bound. result = specSize; } else if (specMode == MeasureSpec.EXACTLY) { // If your control can fit within these bounds return that value. result = specSize; } return result; } private int measureWidth(int measureSpec) { int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); // Default size if no limits are specified. int result = 500; if (specMode == MeasureSpec.AT_MOST) { // Calculate the ideal size of your control within this maximum size. // If your control fills the available space return the outer bound. result = specSize; } else if (specMode == MeasureSpec.EXACTLY) { // If your control can fit within these bounds return that value. result = specSize; } return result; }
处理UI交互事件
通过重写类似下面列举的这些方法,可以处理常见的UI交互事件:
- onKeyDown
- onKeyUp
- onTrackballEvent
- onTouchEvent