自定义组件-绘制时钟

1、效果图

2、Canvas对象详解

1.translate(x,y):平移,将画布的坐标原点向左右方向移动x,向上下方向移动y.canvas的默认位置是在(0,0).

例子:画布原点假如落在(1,1),那么translate(10,10)就是在原点(1,1)基础上分别在x轴、y轴移动10,则原点变为(11,11)。
2.scale(x,y):扩大。x为水平方向的放大倍数,y为竖直方向的放大倍数。
3.rotate(angel):旋转.angle指旋转的角度,顺时针旋转。
4.transform():切变。所谓切变,其实就是把图像的顶部或底部推到一边。

5 save:用来保存Canvas的状态。save之后,可以调用Canvas的平移、放缩、旋转、错切、裁剪等操作。
6 restore:用来恢复Canvas之前保存的状态。防止save后对Canvas执行的操作对后续的绘制有影响。save和restore要配对使用(restore可以比save少,但不能多),如果restore调用次数比save多,会引发Error。
7、drawTextOnPath 在指定的path上面绘制文字。

3、代码

package com.example.canvastest.view;

import java.util.Calendar;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.RectF;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import com.example.canvastest.DensityUtil;
import com.example.canvastest.R;

/**
 * 绘制时钟
 *
 * @author libin
 *
 */
public class ClockView extends View {
    // 半径
    private float radius = 200;
    private Paint mDownPaint;
    // 宽度
    private float mCirclePaintWidth = 10;

    // 上面圆圈的画笔
    private Paint mOutSidePaint;
    // 画文字
    private Paint mTextPaint;

    // 文字大小
    private int textFontSize = 16;

    private int longLine ;
    private int shortLine;
    private int widthLine ;
    //小圆心的半径
    private int circle;

    private int hourDegrees;
    private int minDegrees;
    private int secDegrees;

    public ClockView(Context context) {
        super(context);
        init();
    }

    public ClockView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ClockView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mDownPaint = new Paint();
        mDownPaint.setColor(Color.BLACK);
        mDownPaint.setAntiAlias(true);
        mDownPaint.setStrokeWidth(mCirclePaintWidth);

        textFontSize = (int) getResources().getDimension(R.dimen.textSize);
        shortLine = (int) getResources().getDimension(R.dimen.shortLine);
        longLine = (int) getResources().getDimension(R.dimen.longLine);
        widthLine = (int) getResources().getDimension(R.dimen.widthLine);
        radius = (int) getResources().getDimension(R.dimen.circleRadius);
        circle = (int) getResources().getDimension(R.dimen.circle);
        // 上面的画笔
        mOutSidePaint = new Paint();
        mOutSidePaint.setAntiAlias(true);
        mOutSidePaint.setStrokeWidth(mCirclePaintWidth);
        mOutSidePaint.setStyle(Style.STROKE);
        mOutSidePaint.setColor(Color.BLACK);
        // 上面的画笔
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setColor(Color.BLACK);
        mTextPaint.setStrokeWidth(widthLine);
        mTextPaint.setTextAlign(Align.CENTER);

        mTextPaint.setTextSize(textFontSize);
        mDownPaint.setTextSize(textFontSize);

        mHandler.sendEmptyMessage(10);
    }

    @Override
    protected void onDraw(Canvas canvas) {

        // 平移,将画布的坐标原点向左右方向移动x,向上下方向移动y.canvas的默认位置是在(0,0).
        // 例子:画布原点假如落在(1,1),那么translate(10,10)就是在原点
        // (1,1)基础上分别在x轴、y轴移动10,则原点变为(11,11)。
        canvas.translate(canvas.getWidth() / 2, canvas.getHeight() / 2); // 将位置移动画纸的坐标点:150,150
        // 画圆
        canvas.drawCircle(0, 0, radius, mOutSidePaint);
        canvas.save();

        // 绘制路径
        Path path = new Path();
        path.addArc(new RectF(-radius, -radius, radius, radius), -180, 180);
        mDownPaint.setTextAlign(Align.CENTER);

        int pathy = DensityUtil.dip2px(getContext(), -5);
        // 让文字显示到中间
        canvas.drawTextOnPath("canvas绘制钟表", path, 0, pathy, mDownPaint);
        canvas.restore();

        //绘制刻度
        canvas.save();
        canvas.rotate(210);
        float y = radius;
        int count = 60; // 总刻度数
        for (int i = 0; i < count; i++) {
            if (i % 5 == 0) {
                canvas.drawLine(0f, y-longLine+2, 0, y+mCirclePaintWidth/2 , mTextPaint);
                canvas.drawText(String.valueOf(i / 5 + 1), 0, y -mCirclePaintWidth/2-longLine, mTextPaint);

            } else {
                canvas.drawLine(0f, y  - shortLine , 0f,y-mCirclePaintWidth/2, mTextPaint);
            }
            canvas.rotate(360 / count, 0f, 0f); // 旋转画纸
        }
        canvas.restore();

        //绘制秒针
        canvas.save();
        canvas.rotate(secDegrees);
        Paint hourPaint = new Paint(mDownPaint);
        hourPaint.setColor(Color.BLACK);
        hourPaint.setStrokeWidth(circle);

        canvas.drawLine(0, -longLine, 0, radius*2/3, hourPaint);
        canvas.restore();

        //绘制分针
        canvas.save();
        canvas.rotate(minDegrees);
        canvas.drawLine(0, -longLine, 0, radius*1/2, hourPaint);
        canvas.restore();

        //绘制时针
        canvas.save();
        canvas.rotate(hourDegrees);
        canvas.drawLine(0, -longLine, 0, radius*1/3, hourPaint);
        canvas.restore();

        //绘制中心黑点
        Paint tmpPaint = new Paint(mDownPaint);
        tmpPaint.setColor(Color.BLACK);
        canvas.drawCircle(0, 0, circle*2, tmpPaint);

        //绘制中心红点
        tmpPaint.setColor(Color.RED);
        canvas.drawCircle(0, 0, circle*4/3, tmpPaint);
        super.onDraw(canvas);
    }

    /**
     * 获取角度
     */
    private void calculate(){
        Calendar calendar = Calendar.getInstance();
        int hour = calendar.get(Calendar.HOUR);
        if (hour>12) {
            hour = hour-12;
        }
        hourDegrees = hour*30-180;

        int min = calendar.get(Calendar.MINUTE);
        minDegrees = min * 6-180;

        int sec = calendar.get(Calendar.SECOND);
        secDegrees = sec * 6-180;
        Log.i("tag", " "+ hour+" "+min+" "+sec);
    }

    /**
     * 定时器
     */
    private Handler mHandler = new Handler(){
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
            case 10:
                calculate();
                invalidate();
                this.sendEmptyMessageDelayed(10, 1000);
                break;
            default:
                break;
            }
        };
    };
}

4、常量数据

 <dimen name="textSize">14sp</dimen>
    <dimen name="circleRadius">100dip</dimen>
    <dimen name="shortLine">9dip</dimen>
    <dimen name="longLine">15dip</dimen>
    <dimen name="widthLine">5dip</dimen>
    <dimen name="circle">3dip</dimen>
时间: 2024-12-25 22:48:52

自定义组件-绘制时钟的相关文章

Android自定义组件系列【9】——Canvas绘制折线图

有时候我们在项目中会遇到使用折线图等图形,Android的开源项目中为我们提供了很多插件,但是很多时候我们需要根据具体项目自定义这些图表,这一篇文章我们一起来看看如何在Android中使用Canvas绘制折线图.先看看绘制的效果: 实现原理很简单,我就直接给出代码: package com.example.testcanvasdraw; import java.util.ArrayList; import java.util.List; import java.util.Random; impo

【Android 应用开发】 自定义组件 宽高适配方法, 手势监听器操作组件, 回调接口维护策略, 绘制方法分析 -- 基于 WheelView 组件分析自定义组件

博客地址 : http://blog.csdn.net/shulianghan/article/details/41520569 代码下载 : -- GitHub : https://github.com/han1202012/WheelViewDemo.git -- CSDN : http://download.csdn.net/detail/han1202012/8208997 ; 博客总结 : 博文内容 : 本文完整地分析了 WheelView 所有的源码, 包括其适配器类型, 两种回调接

Android自定义组件系列【8】——遮罩文字动画

遮罩文字的动画我们在Flash中非常常见,作为Android的应用开发者你是否也想将这种动画做到你的应用中去呢?这一篇文章我们来看看如何自定义一个ImageView来实现让一张文字图片实现文字的遮罩闪烁效果,下面先来看看效果吧. (录屏幕延时导致效果看起来不是很好) 一.实现原理 实现原理是重写View的onCreate方法,获取图片资源后对每个像素的透明度进行修改来实现,再启动一个线程来循环改变某个区域中的像素透明度. RGBA基础知识:(下面几段介绍文字引用自维基百科) RGBA是代表Red

Android自定义组件系列【7】——进阶实践(4)

上一篇<>中补充了关于Android中事件分发的过程知识,这一篇我们接着来分析任老师的<可下拉的PinnedHeaderExpandableListView的实现>. 一.StickyLayout中的OnGiveUpTouchEventListener接口的作用是什么? public interface OnGiveUpTouchEventListener { public boolean giveUpTouchEvent(MotionEvent event); } 在Sticky

android自定义组件

官方文档 /Myselfcomponent/res/values/attrs.xml <?xml version="1.0" encoding="utf-8"?> <resources>     <declare-styleable name="MyView"         >         <attr name="textColor" format="color&quo

Qsys自定义组件的开始-Avalon总线规范(中文)

学习FPGA这么长时间了,一直没有整理自己的学习内容,这回要把每一段时间的学习内容总结一下,就从自定义组件开始吧.一定要坚持下来呀!! Avalon 总线规范 参考手册   (Avalon从端口传输与流模式从端口传输部分)  //*************************************    http://www.altera.com 免责声明: 本手册原自Altera 公司发布的<Avalon Bus Specification-Reference Manual>,一切权力

Android开发——构建自定义组件

Android中,你的应用程序程序与View类组件有着一种固定的联系,例如按钮(Button). 文本框(TextView), 可编辑文本框(EditText), 列表框(ListView), 复选框(CheckBox), 单选框(RadioButton), 滚动条(Gallery), 微调器(Spinner), 等等,还有一些比较先进的有着特殊用途的View组件,例如 AutoCompleteTextView, ImageSwitcher和 TextSwitcher.除此之外,种类繁多的像 线

安卓开发_浅谈自定义组件

在Android中,所有的UI界面都是由View类和ViewGroup类及其子类组合而成.其中,View类是所有UI组件的基类,而ViewGroup类是容纳这些UI组件的容器. 其本身也是View类的子类. 在实际开发中,View类还不足以满足程序所有的需求.这时,便可以通过继承View类来开发自己的组件. 开发自定义组件的步骤: 1.创建一个继承android.view.View类的View类,并且重写构造方法. 2.根据需要重写相应的方法. 3.创建并实例化自定义View类,并将其添加到布局

自定义组件之卫星Button(一)

方案来自慕课网,因水平和记忆力有限,将方案记录于此. 移动开发一个好的应用离不开漂亮的UI,漂亮的UI离不开充满想象力的UI组件.在此记录一个自定义UI组件的案例,也是学习自定义UI的开始.本例是satellite-menu的一种实现.satellite-menu效果如下图: 我们需要认识这个组件.这个组件相较于其他组件有两个特别的属性.一个是组件的位置,另一个是组件Item与Button的距离.这两个属性,我们希望开发者能自定. 第一步,我们在value文件夹下,建立一个xml文件attr.x