自定义View的类必须继承自View并且重写onDraw(Canvas canvas)方法;
增加自定义的属性描述必须在values中增加attrs.xml来描述View的属性;
例如:
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<declare-styleable name="ClockView">
<attr name="clockColor" format="color" />
<attr name="visible" format="boolean"/>
<attr name="timeZone" format="dimension">
<enum name="londan" value="0" />
<enum name="beijing" value="8" />
<enum name="newyork" value="20" />
</attr>
</declare-styleable>
</resources>
color,boolean,dimension,string为属性的数据类型;
在自定义的View中引用可以通过TypeArray来获取,例如;
TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.ClockView);
int clockColor = typedArray.getColor(R.styleable.ClockView_clockColor, Color.WHITE);
其中index的值为:R.styleable.组件名称_属性。
在layout中的布局文件为
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:joydao="http://schemas.android.com/apk/res/net.joydao.clock"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<net.joydao.clock.view.ClockView
android:id="@+id/joydaoClock" android:layout_width="wrap_content"
android:layout_height="wrap_content" joydao:clockColor="#ffffff"
joydao:visible="false" joydao:timeZone="beijing" />
</LinearLayout>
下面给出完整的Clock例子:
ClockView.java
package net.joydao.clock.view;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import net.joydao.clock.R;
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.util.DisplayMetrics;
import android.view.View;
import android.view.WindowManager;
public class ClockView extends View implements Runnable {
private Paint colorCirclePaint;
private Paint pointPaint;
private Paint hourMarkPaint;
private Paint minuteMarkPaint;
private Paint secondNeedlePaint;
private Paint minuteNeedlePaint;
private Paint hourNeedlePaint;
private Paint textPaint;
private Paint timePaint;
private float hourMarkLen;
private float minuteMarkLen;
private float clockCircle;
private float radius;
private float hourNeedleRadius;
private float minuteNeedleRadius;
private float secondNeedleRadius;
private float cx;
private float cy;
private boolean running = false;
private int mYear;
private int mMonth;
private int mDay;
private int mHour;
private int mMinute;
private int mSecond;
private int clockColor;
public ClockView(Context context){
this(context,null);
}
public ClockView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.ClockView);
clockColor = typedArray.getColor(R.styleable.ClockView_clockColor, Color.WHITE);
typedArray.recycle();
pointPaint = new Paint();
pointPaint.setAntiAlias(true);
pointPaint.setStyle(Paint.Style.STROKE);
colorCirclePaint = new Paint();
colorCirclePaint.setTextAlign(Paint.Align.CENTER);
colorCirclePaint.setAntiAlias(true);
colorCirclePaint.setStrokeWidth(clockCircle);
colorCirclePaint.setStyle(Paint.Style.STROKE);
hourMarkPaint = new Paint();
hourMarkPaint.setTextAlign(Paint.Align.CENTER);
hourMarkPaint.setAntiAlias(true);
hourMarkPaint.setStyle(Paint.Style.STROKE);
minuteMarkPaint = new Paint();
minuteMarkPaint.setTextAlign(Paint.Align.CENTER);
minuteMarkPaint.setAntiAlias(true);
minuteMarkPaint.setStyle(Paint.Style.STROKE);
secondNeedlePaint = new Paint();
secondNeedlePaint.setTextAlign(Paint.Align.CENTER);
secondNeedlePaint.setAntiAlias(true);
secondNeedlePaint.setStyle(Paint.Style.STROKE);
minuteNeedlePaint = new Paint();
minuteNeedlePaint.setTextAlign(Paint.Align.CENTER);
minuteNeedlePaint.setAntiAlias(true);
minuteNeedlePaint.setStyle(Paint.Style.STROKE);
hourNeedlePaint = new Paint();
hourNeedlePaint.setTextAlign(Paint.Align.CENTER);
hourNeedlePaint.setAntiAlias(true);
hourNeedlePaint.setStyle(Paint.Style.STROKE);
textPaint = new Paint();
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setAntiAlias(true);
timePaint = new Paint();
timePaint.setTextAlign(Paint.Align.CENTER);
timePaint.setAntiAlias(true);
start();
}
public int getClockColor() {
return clockColor;
}
public void setClockColor(int clockColor) {
this.clockColor = clockColor;
this.postInvalidate();
}
public void start(){
running = true;
new Thread(this).start();
}
public void stop(){
running = false;
}
private void resetTime(){
Calendar c = Calendar.getInstance(TimeZone.getDefault());
c.setTime(new Date());
mYear = c.get(Calendar.YEAR);
mMonth = c.get(Calendar.MONTH)+1;
mDay = c.get(Calendar.DAY_OF_MONTH);
mHour = c.get(Calendar.HOUR_OF_DAY);
mMinute = c.get(Calendar.MINUTE);
mSecond = c.get(Calendar.SECOND);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
pointPaint.setColor(clockColor);
colorCirclePaint.setColor(clockColor);
hourMarkPaint.setColor(clockColor);
minuteMarkPaint.setColor(clockColor);
secondNeedlePaint.setColor(clockColor);
minuteNeedlePaint.setColor(clockColor);
hourNeedlePaint.setColor(clockColor);
textPaint.setColor(clockColor);
timePaint.setColor(clockColor);
cx = getWidth()/2;
cy = getHeight()/2;
int tmp = getWidth()<=getHeight()?getWidth():getHeight();
radius = tmp/2-2*clockCircle;
hourMarkLen = radius/15;
minuteMarkLen = radius/30;
clockCircle = radius/60;
pointPaint.setStrokeWidth(radius/25);
textPaint.setTextSize(radius/8);
timePaint.setTextSize(radius/5);
hourMarkPaint.setStrokeWidth(radius/35);
minuteMarkPaint.setStrokeWidth(radius/70);
secondNeedlePaint.setStrokeWidth(radius/70);
minuteNeedlePaint.setStrokeWidth(radius/35);
hourNeedlePaint.setStrokeWidth(radius/20);
hourNeedleRadius = radius - radius/2;
minuteNeedleRadius = radius - radius/3;
secondNeedleRadius = radius - radius/5;
drawClockPanel(canvas,radius);
drawNeedle(canvas);
drawTime(canvas);
}
private void drawTime(Canvas canvas){
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
DateFormat timeFormat = new SimpleDateFormat("hh:mm:ss a");
String dateText = dateFormat.format(new Date());
String timeText = timeFormat.format(new Date());
float textX = cx;
float timeY = cy+radius/2;
float dateY = timeY+1.5f*textPaint.getTextSize();
canvas.drawText(dateText, textX, dateY, textPaint);
canvas.drawText(timeText, textX, timeY, timePaint);
}
private void drawClockPanel(Canvas canvas,float radius){
//System.out.println(getWidth()+":"+getHeight());
//画钟的外圈
canvas.drawCircle(cx, cy, radius, colorCirclePaint);
//画钟的圆点
canvas.drawCircle(cx, cy, radius/60, pointPaint);
int hourLen = 12;
int minLen = 60;
for(int index = 0;index<hourLen;index++){
drawMark(canvas,index,cx,cy,radius,radius-hourMarkLen,(2*Math.PI/hourLen)*index+Math.PI/2,hourMarkPaint,true);
}
for(int index = 0;index<minLen;index++){
drawMark(canvas,index,cx,cy,radius,radius-minuteMarkLen,(2*Math.PI/minLen)*index+Math.PI/2,minuteMarkPaint,false);
}
}
private void drawNeedle(Canvas canvas){
double hourAngle = (2*Math.PI/12)*mHour+Math.PI/2+((2*Math.PI)/(12*60))*mMinute+((2*Math.PI)/(12*60*60))*mSecond;
double minuteAngle = (2*Math.PI/(12*5))*mMinute+Math.PI/2+((2*Math.PI)/(12*5*60))*mSecond;
double secondAngle = (2*Math.PI/60)*mSecond+Math.PI/2;
drawNeedle(canvas,cx,cy,hourAngle,hourNeedleRadius,hourNeedlePaint);
drawNeedle(canvas,cx,cy,minuteAngle,minuteNeedleRadius,minuteNeedlePaint);
drawNeedle(canvas,cx,cy,secondAngle,secondNeedleRadius,secondNeedlePaint);
}
private void drawMark(Canvas canvas,int index,float cx,float cy,float r1,float r2,double angle,Paint paint,boolean drawNumber){
float startX = (float)(cx-r2*Math.cos(angle));
float startY = (float)(cy-r2*Math.sin(angle));
float stopX = (float)(cx-r1*Math.cos(angle));
float stopY = (float)(cy-r1*Math.sin(angle));
float textSize = textPaint.getTextSize();
float radiusText = r2 - textSize;
if(index>=3 && index<=9){
radiusText = r2-textSize/3;
}
float textX = (float)(cx-radiusText*Math.cos(angle));
float textY = (float)(cy-radiusText*Math.sin(angle));
if(index == 3 || index == 9){
textY = textY + textSize/4;
}
canvas.drawLine(startX,startY,stopX,stopY, paint);
if(drawNumber){
if(index==0){
index = 12;
}
canvas.drawText(String.valueOf(index), textX, textY, textPaint);
}
}
private void drawNeedle(Canvas canvas,float cx,float cy,double angle,float radius,Paint paint){
canvas.drawLine(cx, cy, (float)(cx-radius*Math.cos(angle)), (float)(cy-radius*Math.sin(angle)), paint);
}
public static Screen getScreenPix(Context context) {
DisplayMetrics dm = new DisplayMetrics();
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
windowManager.getDefaultDisplay().getMetrics(dm);
return new Screen(dm.widthPixels,dm.heightPixels);
}
public static class Screen{
public int widthPixels;
public int heightPixels;
public Screen(){
}
public Screen(int widthPixels,int heightPixels){
this.widthPixels=widthPixels;
this.heightPixels=heightPixels;
}
@Override
public String toString() {
return "("+widthPixels+","+heightPixels+")";
}
}
@Override
public void run() {
while(running){
try {
resetTime();
postInvalidate();
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
布局文件main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:joydao="http://schemas.android.com/apk/res/net.joydao.clock"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<net.joydao.clock.view.ClockView
android:id="@+id/joydaoClock" android:layout_width="wrap_content"
android:layout_height="wrap_content" joydao:clockColor="#ffffff"
joydao:visible="false" joydao:timeZone="beijing" />
</LinearLayout>
属性描述文件attrs.xml:
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<declare-styleable name="ClockView">
<attr name="clockColor" format="color" />
<attr name="visible" format="boolean"/>
<attr name="timeZone" format="dimension">
<enum name="londan" value="0" />
<enum name="beijing" value="8" />
<enum name="newyork" value="20" />
</attr>
</declare-styleable>
</resources>
效果图: