自定义控件作为Android中一个非常重要的功能,一直以来都被初学者认为是代表高手的象征。适当的使用自定义View,可以丰富应用程序的体验效果。创建自定义view的时候需要一步一步来,从一个基本的效果开始,慢慢的增加功能,绘制更复杂的效果。
接下来开始制作一个简单的时钟:
新建TimeView,继承View
public class TimeView extends View{
public TimeView(Context context) {
super(context);
}
public TimeView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext=context;
}
}
定义属性:
<declare-styleable name="MyTimeViewStyle">
<attr name="HourColor" format="color"></attr>
<attr name="MinuteColor" format="color"></attr>
<attr name="SecondColor" format="color"></attr>
<attr name="CircleColr" format="color"></attr>
<attr name="DegreeColor" format="color"></attr>
<attr name="NumberTextColor" format="color"></attr>
<attr name="NumberTextSize" format="dimension"></attr>
</declare-styleable>
属性定义好之后可以设置view的基本属性,这里主要是控制时钟 分钟 秒钟的颜色,刻度 字的大小。
在xml文件中使用:
<com.app.event.TimeView
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:id="@+id/TimeView"
android:layout_width="match_parent"
android:layout_height="match_parent"
custom:CircleColr="#ff8c00"
custom:NumberTextColor="#1e90ff"
/>
系统提供了TypedArray这样的数据结构来获取自定义属性集。
TypedArray ta=mContext.obtainStyledAttributes(attrs,R.styleable.MyTimeViewStyle);
textSize=ta.getDimension(R.styleable.MyTimeViewStyle_NumberTextSize, sp2px(12));
text_color=ta.getColor(R.styleable.MyTimeViewStyle_NumberTextColor, Color.RED);
hour_color=ta.getColor(R.styleable.MyTimeViewStyle_HourColor, Color.RED);
minute_color=ta.getColor(R.styleable.MyTimeViewStyle_MinuteColor, Color.BLUE);
second_color=ta.getColor(R.styleable.MyTimeViewStyle_SecondColor, Color.YELLOW);
circle_color=ta.getColor(R.styleable.MyTimeViewStyle_CircleColr, Color.BLACK);
degree_color=ta.getColor(R.styleable.MyTimeViewStyle_DegreeColor, Color.BLACK);
ta.recycle();
初始化设置:
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//半径 block为距边缘的间隔
radius=(getWidth()-block)/2;
//中心点坐标 centerX centerY
centerX=getWidth()/2;
centerY=getHeight()/2;
//时钟 分钟 秒钟的长度
hour_length=radius/4;
minute_length=radius/2;
second_length=radius*3/4;
}
画圆
private void drawCircle(Canvas canvas){
mPaint.setColor(circle_color);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(centerX, centerY, radius, mPaint);
}
画刻度 数字
private void drawDegree(Canvas canvas){
mPaint.setColor(degree_color);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setTextAlign(Paint.Align.CENTER);
for(int i=0;i<60;i++){
//当i==0,5,10...的时候需要画大刻度
if(i%5==0){
canvas.drawLine(centerX+(float)(radius*Math.cos(Math.PI/30*i)),
centerY+(float)(radius*Math.sin(Math.PI / 30 * i)),
centerX+(float)((radius-bigLength)*Math.cos(Math.PI/30*i)),
centerY+(float)((radius-bigLength)*Math.sin(Math.PI /30 * i)),
mPaint
);
//同时也需要画数字
canvas.drawText((i/5+3>12?i/5+3-12:i/5+3)+"",centerX+(float)((radius-bigLength-textSize)*Math.cos(Math.PI/30*i)),
centerY+textSize/3+(float)((radius-bigLength-textSize)*Math.sin(Math.PI /30 * i)),textPaint);
}else{
//其他的画小刻度
canvas.drawLine(centerX+(float)(radius*Math.cos(Math.PI/30*i)),
centerY+(float)(radius*Math.sin(Math.PI / 30 * i)),
centerX+(float)((radius-smallLength)*Math.cos(Math.PI/30*i)),
centerY+(float)((radius-smallLength)*Math.sin(Math.PI / 30 * i)),
mPaint
);
}
}
}
画时钟 分钟 秒钟
private void drawIndicator(Canvas canvas){
//画秒钟
mPaint.setColor(second_color);
canvas.drawLine(centerX, centerY,
(float) (centerX + second_length * Math.cos((second - 15) * Math.PI / 30)),
(float) (centerY + second_length * Math.sin((second - 15) * Math.PI / 30)),
mPaint
);
//画分钟
mPaint.setColor(minute_color);
canvas.drawLine(centerX, centerY,
(float) (centerX + minute_length * Math.cos((minute - 15) * Math.PI / 30 + (second * Math.PI) / 1800)),
(float) (centerY + minute_length * Math.sin((minute - 15) * Math.PI / 30+(second*Math.PI)/1800)),//让分钟微小的变动
mPaint
);
//画时钟
mPaint.setColor(hour_color);
canvas.drawLine(centerX, centerY,
(float) (centerX + hour_length * Math.cos((hour - 3) * Math.PI / 6+(minute*Math.PI)/360)),
(float) (centerY + hour_length * Math.sin((hour - 3) * Math.PI / 6+(minute*Math.PI)/360)),//让时钟微小的变动
mPaint
);
}
让指针动起来
@Override
//获取当前的时间
protected void onFinishInflate() {
super.onFinishInflate();
Calendar calendar=Calendar.getInstance();
hour=calendar.get(Calendar.HOUR);
minute=calendar.get(Calendar.MINUTE);
second=calendar.get(Calendar.SECOND);
timer=new Timer();
timer.schedule(task,0,1000);
}
TimerTask task=new TimerTask() {
@Override
public void run() {
second++;
mHanlder.sendEmptyMessage(0);
}
};
private Handler mHanlder=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(second>59){
second=0;
minute++;
if(minute>59){
minute=0;
hour++;
if(hour>12){
hour=0;
minute=0;
second=0;
}
}
}
//刷新
invalidate();
}
};
效果如下图 时钟和分钟会随着秒钟的改变有微小的变化
附上TimeView的完整代码
package com.app.event;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;
public class TimeView extends View{
private float radius;//半径
private float block=10;
private float strokeWidth=5;
private float smallLength=10,bigLength=20;
private Context mContext;
private float hour_length,minute_length,second_length;//时钟 分钟 秒钟长度
private int hour_color,minute_color,second_color,circle_color,degree_color,text_color;//时钟 分钟 秒钟颜色
private Paint mPaint;
private Paint textPaint;
private float textSize;
private float centerX,centerY;
private int hour,minute,second;
private Timer timer;
public TimeView(Context context) {
super(context);
}
public TimeView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext=context;
initPaint(attrs);
}
private void initPaint(AttributeSet attrs){
TypedArray ta=mContext.obtainStyledAttributes(attrs,R.styleable.MyTimeViewStyle);
textSize=ta.getDimension(R.styleable.MyTimeViewStyle_NumberTextSize, sp2px(12));
text_color=ta.getColor(R.styleable.MyTimeViewStyle_NumberTextColor, Color.RED);
hour_color=ta.getColor(R.styleable.MyTimeViewStyle_HourColor, Color.RED);
minute_color=ta.getColor(R.styleable.MyTimeViewStyle_MinuteColor, Color.BLUE);
second_color=ta.getColor(R.styleable.MyTimeViewStyle_SecondColor, Color.YELLOW);
circle_color=ta.getColor(R.styleable.MyTimeViewStyle_CircleColr, Color.BLACK);
degree_color=ta.getColor(R.styleable.MyTimeViewStyle_DegreeColor, Color.BLACK);
ta.recycle();
mPaint=new Paint();
mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
mPaint.setStrokeWidth(strokeWidth);
textPaint=new Paint();
textPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setColor(text_color);
textPaint.setTextSize(textSize);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawCircle(canvas);
drawDegree(canvas);
drawIndicator(canvas);
}
/**
* 画圆盘
* @param canvas
*/
private void drawCircle(Canvas canvas){
mPaint.setColor(circle_color);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(centerX, centerY, radius, mPaint);
}
/**
* 画刻度
* @param canvas
*/
private void drawDegree(Canvas canvas){
mPaint.setColor(degree_color);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setTextAlign(Paint.Align.CENTER);
for(int i=0;i<60;i++){
if(i%5==0){
canvas.drawLine(centerX+(float)(radius*Math.cos(Math.PI/30*i)),
centerY+(float)(radius*Math.sin(Math.PI / 30 * i)),
centerX+(float)((radius-bigLength)*Math.cos(Math.PI/30*i)),
centerY+(float)((radius-bigLength)*Math.sin(Math.PI /30 * i)),
mPaint
);
canvas.drawText((i/5+3>12?i/5+3-12:i/5+3)+"",centerX+(float)((radius-bigLength-textSize)*Math.cos(Math.PI/30*i)),
centerY+textSize/3+(float)((radius-bigLength-textSize)*Math.sin(Math.PI /30 * i)),textPaint);
}else{
canvas.drawLine(centerX+(float)(radius*Math.cos(Math.PI/30*i)),
centerY+(float)(radius*Math.sin(Math.PI / 30 * i)),
centerX+(float)((radius-smallLength)*Math.cos(Math.PI/30*i)),
centerY+(float)((radius-smallLength)*Math.sin(Math.PI / 30 * i)),
mPaint
);
}
}
}
/**
* 画指针
* @param canvas
*/
private void drawIndicator(Canvas canvas){
mPaint.setColor(second_color);
canvas.drawLine(centerX, centerY,
(float) (centerX + second_length * Math.cos((second - 15) * Math.PI / 30)),
(float) (centerY + second_length * Math.sin((second - 15) * Math.PI / 30)),
mPaint
);
mPaint.setColor(minute_color);
canvas.drawLine(centerX, centerY,
(float) (centerX + minute_length * Math.cos((minute - 15) * Math.PI / 30 + (second * Math.PI) / 1800)),
(float) (centerY + minute_length * Math.sin((minute - 15) * Math.PI / 30+(second*Math.PI)/1800)),
mPaint
);
mPaint.setColor(hour_color);
canvas.drawLine(centerX, centerY,
(float) (centerX + hour_length * Math.cos((hour - 3) * Math.PI / 6+(minute*Math.PI)/360)),
(float) (centerY + hour_length * Math.sin((hour - 3) * Math.PI / 6+(minute*Math.PI)/360)),
mPaint
);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
Calendar calendar=Calendar.getInstance();
hour=calendar.get(Calendar.HOUR);
minute=calendar.get(Calendar.MINUTE);
second=calendar.get(Calendar.SECOND);
timer=new Timer();
timer.schedule(task,0,1000);
}
TimerTask task=new TimerTask() {
@Override
public void run() {
second++;
mHanlder.sendEmptyMessage(0);
}
};
private Handler mHanlder=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(second>59){
second=0;
minute++;
if(minute>59){
minute=0;
hour++;
if(hour>12){
hour=0;
minute=0;
second=0;
}
}
}
invalidate();
}
};
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Log.i("=====onMeasure", getWidth() + "====" + getHeight());
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
radius=(getWidth()-block)/2;
centerX=getWidth()/2;
centerY=getHeight()/2;
hour_length=radius/4;
minute_length=radius/2;
second_length=radius*3/4;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
Log.i("=====onLayout", left + "===" + top + "====" + right + "====" + bottom);
}
/**
* px转化为dp
* @param pxValue
* @return
*/
private int px2dip(float pxValue){
float scale=mContext.getResources().getDisplayMetrics().density;
return (int)(pxValue/scale+0.5f);
}
/**
* dp转化为px
* @param dipValue
* @return
*/
private int dip2px(float dipValue){
float scale=mContext.getResources().getDisplayMetrics().density;
return (int)(dipValue*scale+0.5f);
}
/**
* px转化为sp
* @param pxValue
* @return
*/
private int px2sp(float pxValue){
float scale=mContext.getResources().getDisplayMetrics().scaledDensity;
return (int)(pxValue/scale+0.5f);
}
/**
* sp转化为px
* @param spValue
* @return
*/
private int sp2px(float spValue){
float scale=mContext.getResources().getDisplayMetrics().scaledDensity;
return (int)(spValue*scale+0.5f);
}
}
刚开始研究自定义View ,还不太熟练,感觉有点难,但是相信 千里之行,始于足下。只要开始做,慢慢的就能越来越熟练。加油!