自定义控件制作时钟

自定义控件作为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 ,还不太熟练,感觉有点难,但是相信 千里之行,始于足下。只要开始做,慢慢的就能越来越熟练。加油!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值