当Android自带的View满足不了开发者时,自定义View就发挥了很好的作用。
建立一个自定义View,需要继承于View类,并且实现其中的至少一个构造函数和两个方法:onMeasure()和onDraw();
onMeasure()用于设置自定义View的尺寸,onDraw()用于绘制View中的内容。
在onDraw()方法中,需要调用画笔绘制图形或文本,绘制的模板时Canvas对象, Canvas类中用来绘制图形文本的方法有:
drawRect(RectF rect, Paint paint) //绘制区域,参数一为RectF一个区域
drawPath(Path path, Paint paint) //绘制一个路径,参数一为Path路径对象
drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) //贴图,参数一就是我们常规的Bitmap对象,参数二是源区域(这里是bitmap),参数三是目标区域(应该在canvas的位置和大小),参数四是Paint画刷对象,因为用到了缩放和拉伸的可能,当原始Rect不等于目标Rect时性能将会有大幅损失。
drawLine(float startX, float startY, float stopX, float stopY, Paintpaint) //画线,参数一起始点的x轴位置,参数二起始点的y轴位置,参数三终点的x轴水平位置,参数四y轴垂直位置,最后一个参数为Paint 画刷对象。
drawPoint(float x, float y, Paint paint) //画点,参数一水平x轴,参数二垂直y轴,第三个参数为Paint对象。
drawText(String text, float x, floaty, Paint paint) //渲染文本,Canvas类除了上面的还可以描绘文字,参数一是String类型的文本,参数二x轴,参数三y轴,参数四是Paint对象。
drawOval(RectF oval, Paint paint)//画椭圆,参数一是扫描区域,参数二为paint对象;
drawCircle(float cx, float cy, float radius,Paint paint)// 绘制圆,参数一是中心点的x轴,参数二是中心点的y轴,参数三是半径,参数四是paint对象;
drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)//画弧,参数一是RectF对象,一个矩形区域椭圆形的界限用于定义在形状、大小、电弧,参数二是起始角(度)在电弧的开始,参数三扫描角(度)开始顺时针测量的,参数四是如果这是真的话,包括椭圆中心的电弧,并关闭它,如果它是假这将是一个弧线,参数五是Paint对象。
绘制图形需要画笔Paint对象,Paint类中的方法有:
setARGB(int a, int r, int g, int b) // 设置 Paint对象颜色,参数一为alpha透明值
setAlpha(int a) // 设置alpha不透明度,范围为0~255
setAntiAlias(boolean aa) // 是否抗锯齿
setColor(int color) // 设置颜色,这里Android内部定义的有Color类包含了一些常见颜色定义
setTextScaleX(float scaleX) // 设置文本缩放倍数,1.0f为原始
setTextSize(float textSize) // 设置字体大小
setUnderlineText(booleanunderlineText) // 设置下划线
Demo
一个自定义时钟视图的写法:
<code class = "language-java" hljs= "" > public class MyView extends View {
private int width;
private int height;
private Paint mPaintLine;
private Paint mPaintCircle;
private Paint mPaintHour;
private Paint mPaintMinute;
private Paint mPaintSec;
private Paint mPaintText;
private Calendar mCalendar;
public static final int NEED_INVALIDATE = 0X23 ;
//每隔一秒,在handler中调用一次重新绘制方法
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case NEED_INVALIDATE:
mCalendar = Calendar.getInstance();
invalidate(); //告诉UI主线程重新绘制
handler.sendEmptyMessageDelayed(NEED_INVALIDATE, 1000 );
break ;
default :
break ;
}
}
};
public MyView(Context context) {
super (context);
}
public MyView(Context context, AttributeSet attrs) {
super (context, attrs);
mCalendar = Calendar.getInstance();
mPaintLine = new Paint();
mPaintLine.setColor(Color.BLUE);
mPaintLine.setStrokeWidth( 10 );
mPaintCircle = new Paint();
mPaintCircle.setColor(Color.GREEN); //设置颜色
mPaintCircle.setStrokeWidth( 10 ); //设置线宽
mPaintCircle.setAntiAlias( true ); //设置是否抗锯齿
mPaintCircle.setStyle(Paint.Style.STROKE); //设置绘制风格
mPaintText = new Paint();
mPaintText.setColor(Color.BLUE);
mPaintText.setStrokeWidth( 10 );
mPaintText.setTextAlign(Paint.Align.CENTER);
mPaintText.setTextSize( 40 );
mPaintHour = new Paint();
mPaintHour.setStrokeWidth( 20 );
mPaintHour.setColor(Color.BLUE);
mPaintMinute = new Paint();
mPaintMinute.setStrokeWidth( 15 );
mPaintMinute.setColor(Color.BLUE);
mPaintSec = new Paint();
mPaintSec.setStrokeWidth( 10 );
mPaintSec.setColor(Color.BLUE);
handler.sendEmptyMessage(NEED_INVALIDATE); //向handler发送一个消息,让它开启重绘
}
@Override
protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec) {
super .onMeasure(widthMeasureSpec, heightMeasureSpec);
width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
super .onDraw(canvas);
int circleRadius = 400 ;
//画出大圆
canvas.drawCircle(width / 2 , height / 2 , circleRadius, mPaintCircle);
//画出圆中心
canvas.drawCircle(width / 2 , height / 2 , 20 , mPaintCircle);
//依次旋转画布,画出每个刻度和对应数字
for ( int i = 1 ; i <= 12 ; i++) {
canvas.save(); //保存当前画布
canvas.rotate( 360 / 12 *i,width/ 2 ,height/ 2 );
//左起:起始位置x坐标,起始位置y坐标,终止位置x坐标,终止位置y坐标,画笔(一个Paint对象)
canvas.drawLine(width / 2 , height / 2 - circleRadius, width / 2 , height / 2 - circleRadius + 30 , mPaintCircle);
//左起:文本内容,起始位置x坐标,起始位置y坐标,画笔
canvas.drawText(+i, width / 2 , height / 2 - circleRadius + 70 , mPaintText);
canvas.restore(); //
}
int minute = mCalendar.get(Calendar.MINUTE); //得到当前分钟数
int hour = mCalendar.get(Calendar.HOUR); //得到当前小时数
int sec = mCalendar.get(Calendar.SECOND); //得到当前秒数
float minuteDegree = minute/60f* 360 ; //得到分针旋转的角度
canvas.save();
canvas.rotate(minuteDegree, width / 2 , height / 2 );
canvas.drawLine(width / 2 , height / 2 - 250 , width / 2 , height / 2 + 40 , mPaintMinute);
canvas.restore();
float hourDegree = (hour* 60 +minute)/12f/ 60 * 360 ; //得到时钟旋转的角度
canvas.save();
canvas.rotate(hourDegree, width / 2 , height / 2 );
canvas.drawLine(width / 2 , height / 2 - 200 , width / 2 , height / 2 + 30 , mPaintHour);
canvas.restore();
float secDegree = sec/60f* 360 ; //得到秒针旋转的角度
canvas.save();
canvas.rotate(secDegree,width/ 2 ,height/ 2 );
canvas.drawLine(width/ 2 ,height/ 2 - 300 ,width/ 2 ,height/ 2 + 40 ,mPaintSec);
canvas.restore();
}
}</code>
需要在布局中加载自定义View,名字必须是全名(包括包名):
activity_timer
<code class = "language-xml" hljs= "" ><!--?xml version= 1.0 encoding=utf- 8 ?-->
<linearlayout android:layout_height= "match_parent" android:layout_width= "match_parent" android:orientation= "vertical" xmlns:android= "http://schemas.android.com/apk/res/android" >
<com.example.administrator.selfdefinedview.widget.myview android:layout_height= "match_parent/" android:layout_width= "match_parent" >
</com.example.administrator.selfdefinedview.widget.myview></linearlayout></code>
主活动setContentView就行了
<code class = "language-java" hljs= "" > public class TimerActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout.activity_timer);
}
}</code>