- 最近写了个指针时钟的小程序,拿出来和大家分享一下。
- 指针时钟控件实现:
- 关键点:
- 1、Android自定义控件实现
- 2、Android自定义控件命名空间
- 3、Android自定义控件从布局文件中获取参数,让自己的控件更具有灵活性、可用性
- 4、Android图形绘制(点、线、圆和矩形)
- 5、Android Handler消息机制
- 主要思想:
- 自定义一个控件,继承自View类,实现onMeasuredDimension()方法指定控件大小;实现onDraw()方法计算圆心和偏移角度,来绘制圆心、时针和分针。然后需要做的就是在布局文件中指定钟表背景图、长宽距圆心比例和缩放比例即可。
- 一、 实现效果
- 二、代码实现
- public class HandClockView extends View implements Runnable {
- private static final String NAMESPACE = "http://www.pro.dev.com/common";
- private static final String CLOCK_FACE="colckFace";
- private static final String HAND_CENTER_WIDTH_SCALE = "handCenterWidthScale";
- private static final String HAND_CENTER_HEIGHT_SCALE = "handCenterHeightScale";
- private static final String SCALE = "scale";
- private static final String HOUR_HAND_SIZE = "hourHandSize";
- private static final String MINUTE_HAND_SIZE = "minuteHandSize";
- private Bitmap clockFace;
- private int clockFaceId;
- private float centerX;
- private float centerY;
- private float handCenterWidthScale;
- private float handCenterHeightScale;
- private float scale;
- private int hourHandSize;
- private int minuteHandSize;
- private Handler handler = new Handler();
- public HandClockView(Context context) {
- super(context);
- }
- public HandClockView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(attrs);
- }
- public HandClockView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- init(attrs);
- }
- private void init(AttributeSet attrs) {
- clockFaceId = attrs.getAttributeResourceValue(NAMESPACE, CLOCK_FACE, 0);
- Log.e("zhang.xiao", "clockFaceId=" + clockFaceId);
- if (clockFaceId > 0) {
- clockFace = BitmapFactory.decodeResource(getResources(), clockFaceId);
- }
- Log.e("zhang.xiao", "clockFace is null " + String.valueOf(clockFace == null));
- handCenterWidthScale = attrs.getAttributeFloatValue(NAMESPACE, HAND_CENTER_WIDTH_SCALE, clockFace.getWidth() / 2);
- handCenterHeightScale = attrs.getAttributeFloatValue(NAMESPACE, HAND_CENTER_HEIGHT_SCALE, clockFace.getHeight() / 2);
- scale = attrs.getAttributeFloatValue(NAMESPACE, SCALE, 0);
- hourHandSize = (int) (scale * attrs.getAttributeIntValue(NAMESPACE, HOUR_HAND_SIZE, 0));
- minuteHandSize = (int) (scale * attrs.getAttributeIntValue(NAMESPACE, MINUTE_HAND_SIZE, 0));
- }
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- setMeasuredDimension((int)(clockFace.getWidth() * scale), (int)(clockFace.getHeight() * scale));
- }
- @Override
- public void run() {
- invalidate();
- handler.postDelayed(this, 60 * 1000);
- }
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- Paint paint = new Paint();
- Rect src = new Rect();
- src.left = 0;
- src.top = 0;
- src.right = clockFace.getWidth();
- src.bottom = clockFace.getHeight();
- Rect target = new Rect();
- target.left = 0;
- target.top = 0;
- target.right = (int) (src.right * scale);
- target.bottom = (int) (src.bottom * scale);
- canvas.drawBitmap(clockFace, src, target, paint);
- centerX = clockFace.getWidth() * handCenterWidthScale * scale;
- centerY = clockFace.getHeight() * handCenterHeightScale * scale;
- canvas.drawCircle(centerX, centerY, 5, paint);
- paint.setStrokeWidth(3);
- Calendar cal = Calendar.getInstance();
- int minute = cal.get(Calendar.MINUTE);
- int hour = cal.get(Calendar.HOUR);
- int second = cal.get(Calendar.SECOND);
- Log.e("zhang.xiao", "hour=" + hour);
- Log.e("zhang.xiao", "minute=" + minute);
- double minuteRadian = Math.toRadians((360 - ((minute * 6) - 90)) % 360);
- double hourRadian = Math.toRadians(((360 - ((hour * 30) - 90)) % 360) - (30 * minute / 60));
- canvas.drawLine(centerX, centerY, (int) (centerX + minuteHandSize * Math.cos(minuteRadian)),
- (int) (centerY - Math.sin(minuteRadian) * minuteHandSize), paint);
- paint.setStrokeWidth(4);
- canvas.drawLine(centerX, centerY, (int) (centerX + hourHandSize * Math.cos(hourRadian)),
- (int) (centerY - Math.sin(hourRadian) * hourHandSize), paint);
- handler.postDelayed(this, (60 - second) * 1000);
- }
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- Log.e("zhang.xiao", "onDetachedFromWindow");
- }
- }
- 三、
- 布局文件中使用<?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:dev="http://www.pro.dev.com/common"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <com.test.HandClockView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- dev:colckFace="@drawable/clock3"
- dev:handCenterWidthScale="0.5"
- dev:handCenterHeightScale="0.5"
- dev:scale="0.3"
- dev:hourHandSize="100"
- dev:minuteHandSize="178"
- >
- </com.test.HandClockView>
- </LinearLayout>
标签说明:
dev:自定义控件命名空间
colckFace:钟表背景图
handCenterWidthScale:圆心和图宽缩放比例
handCenterHeightScale:圆心和图片高度缩放比例
scale:图片缩放比例
hourHandSize:时针长度
minuteHandSize:分钟长度
时钟实现代码二:
前不久在网上看见Android实现的模拟时钟,感觉十分有意思,这里是地址:
极客学院http://www.eoeandroid.com/forum.php?mod=viewthread&tid=58324可惜的是这种方式没有秒表。笔者突然对其有了兴趣,也想去实现以下自己的模拟时钟。折腾了一阵子总算是弄出来了现在将实现方式共享出来,大家一些交流。不多说,先上效果图:
首先我们应该准备相关的素材:时钟盘、时针、分针、秒针图片.
时钟盘:
时针:
分针:
秒针:
源码部分
配置文件,比较简单:
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical"
- tools:context=".MainActivity"
- android:background="@color/bg">
- <com.kiritor.mymodelclock.MyQAnalogClock
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center" />
- </LinearLayout>
MyQAnalogClock代码:用于构造时钟view以及其刷新显示:
- package com.kiritor.mymodelclock;
- import android.util.AttributeSet;
- import com.kiritor.mymodelclock.R;
- import android.content.Context;
- import android.graphics.*;
- import android.graphics.drawable.BitmapDrawable;
- import android.os.Handler;
- import android.view.View;
- import java.util.Calendar;
- import java.util.TimeZone;
- /**
- * Created by Kiritor on 13-5-30.
- */
- public class MyQAnalogClock extends View {
- //时钟盘,分针、秒针、时针对象
- Bitmap mBmpDial;
- Bitmap mBmpHour;
- Bitmap mBmpMinute;
- Bitmap mBmpSecond;
- BitmapDrawable bmdHour;
- BitmapDrawable bmdMinute;
- BitmapDrawable bmdSecond;
- BitmapDrawable bmdDial;
- Paint mPaint;
- Handler tickHandler;
- int mWidth;
- int mHeigh;
- int mTempWidth = bmdSecond.getIntrinsicWidth();
- int mTempHeigh;
- int centerX;
- int centerY;
- int availableWidth = 100;
- int availableHeight = 100;
- private String sTimeZoneString;
- public MyQAnalogClock(Context context,AttributeSet attr)
- {
- this(context,"GMT+8:00");
- }
- public MyQAnalogClock(Context context, String sTime_Zone) {
- super(context);
- sTimeZoneString = sTime_Zone;
- mBmpHour = BitmapFactory.decodeResource(getResources(),
- R.drawable.shizhen);
- bmdHour = new BitmapDrawable(mBmpHour);
- mBmpMinute = BitmapFactory.decodeResource(getResources(),
R.drawable.fenzhen);
- bmdMinute = new BitmapDrawable(mBmpMinute);
- mBmpSecond = BitmapFactory.decodeResource(getResources(),
- R.drawable.miaozhen);
- bmdSecond = new BitmapDrawable(mBmpSecond);
- mBmpDial = BitmapFactory.decodeResource(getResources(),
- R.drawable.android_clock_dial);
- bmdDial = new BitmapDrawable(mBmpDial);
- mWidth = mBmpDial.getWidth();
- mHeigh = mBmpDial.getHeight();
- centerX = availableWidth / 2;
- centerY = availableHeight / 2;
- mPaint = new Paint();
- mPaint.setColor(Color.BLUE);
- run();
- }
- public void run() {
- tickHandler = new Handler();
- tickHandler.post(tickRunnable);
- }
- private Runnable tickRunnable = new Runnable() {
- public void run() {
- postInvalidate();
- tickHandler.postDelayed(tickRunnable, 1000);
- }
- };
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- Calendar cal = Calendar.getInstance(TimeZone
- .getTimeZone(sTimeZoneString));
- int hour = cal.get(Calendar.HOUR);
- int minute = cal.get(Calendar.MINUTE);
- int second = cal.get(Calendar.SECOND);
- float hourRotate = hour * 30.0f + minute / 60.0f * 30.0f;
- float minuteRotate = minute * 6.0f;
- float secondRotate = second * 6.0f;
- boolean scaled = false;
- if (availableWidth < mWidth || availableHeight < mHeigh) {
- scaled = true;
- float scale = Math.min((float) availableWidth / (float) mWidth,
- (float) availableHeight / (float) mHeigh);
- canvas.save();
- canvas.scale(scale, scale, centerX, centerY);
- }
- bmdDial.setBounds(centerX - (mWidth / 2), centerY - (mHeigh / 2),
- centerX + (mWidth / 2), centerY + (mHeigh / 2));
- bmdDial.draw(canvas);
- mTempWidth = bmdHour.getIntrinsicWidth();
- mTempHeigh = bmdHour.getIntrinsicHeight();
- canvas.save();
- canvas.rotate(hourRotate, centerX, centerY);
- bmdHour.setBounds(centerX - (mTempWidth / 2), centerY
- - (mTempHeigh / 2), centerX + (mTempWidth / 2), centerY
- + (mTempHeigh / 2));
- bmdHour.draw(canvas);
- canvas.restore();
- mTempWidth = bmdMinute.getIntrinsicWidth();
- mTempHeigh = bmdMinute.getIntrinsicHeight();
- canvas.save();
- canvas.rotate(minuteRotate, centerX, centerY);
- bmdMinute.setBounds(centerX - (mTempWidth / 2), centerY
- - (mTempHeigh / 2), centerX + (mTempWidth / 2), centerY
- + (mTempHeigh / 2));
- bmdMinute.draw(canvas);
- canvas.restore();
- mTempWidth = bmdSecond.getIntrinsicWidth();
- mTempHeigh = bmdSecond.getIntrinsicHeight();
- canvas.rotate(secondRotate, centerX, centerY);
- bmdSecond.setBounds(centerX - (mTempWidth / 2), centerY
- - (mTempHeigh / 2), centerX + (mTempWidth / 2), centerY
- + (mTempHeigh / 2));
- bmdSecond.draw(canvas);
- if (scaled) {
- canvas.restore();
- }
- }
- }
主Activity:
- package com.kiritor.mymodelclock;
- import android.app.Activity;
- import android.os.Bundle;
- /**
- * Created by Kiritor on 13-5-30.
- */
- public class MainActivity extends Activity {
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- }
- }
好了一个简单的模拟时钟就完成了,不过通过观察运行效果可以看出的是,笔者对 时针、分针、秒针图片的设计不是太美观,看的不是太清楚,体验略差。不过这不是重点,读者可以自己去设计一下那4张图片,从而做出不同的效果!这里本实例使用Android Studio做的,完整项目源码就不上传了!Over!