模拟时钟的实现

关键在于掌握view自定义绘制的关键方法

1.onMeasure(int widthMeasureSpec, int heightMeasureSpec)

当父控件要摆放该控件时,会触发。两个参数:widthMeasureSpec 和 heightMeasureSpec,是父亲能够提供给孩子的空间,孩子参考这个空间值结合自己

的实际情况,需要调用setMeasuredDimension来设置自己显示的空间,API是这么描述的:


CONTRACT: When overriding this method, you must call setMeasuredDimension(int, int) to store the measured width and height of this view. Failure to do so will trigger an IllegalStateException, thrown bymeasure(int, int). Calling the superclass' onMeasure(int, int) is a valid use.


2.onDraw(Canvas canvas)

实际操作的就是传入的canvas,画布,Canvas提供了一系列方法,让我们心情“泼墨挥洒”,本例主要用到旋转

rotate(float degrees, float px, float py)

获取表盘中心点坐标,根据时间值算出旋转的角度,即实现了表盘指针的显示


3.Handler+Runnable+invalidate

实现定时每秒刷新view的显示


代码如下:

TimezoneAnalogClock.java

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.util.AttributeSet;
import android.view.View;
import android.view.View.MeasureSpec;

public class TimezoneAnalogClock extends View {
	private Time mTime;//获取当前的精确时间
	private Drawable mClockHandHour;//时针
	private Drawable mClockHandMin;//分针
	private Drawable mClockHandSec;//秒针
	private Drawable mClockDial;//表盘
	private int mClockDialWidth;//表盘宽度
	private int mClockDialHeight;//表盘高度

	private float mMinute;//当前时间分针值
	private float mHour;//当前时间时针值
	private float mSecond;//当前时间钞针值
	private boolean mChanged;//状态是否改变
	private Runnable mRunnable;//用于实时刷新时间显示
	private boolean mStop;//是否处于激活状态
	private Handler mHandler;//用于实时刷新时间显示
	Context mContext;
	
	static public boolean getRunningState(TimezoneAnalogClock mAnalogClock) {
		return !mAnalogClock.mStop;
	}
	
	static public Runnable getRunnable(TimezoneAnalogClock mAnalogClock) {
		return mAnalogClock.mRunnable;
	}
	
	static public Handler getHandler(TimezoneAnalogClock mAnalogClock) {
		return mAnalogClock.mHandler;
	}
	
	static public void onTimeChanged(TimezoneAnalogClock mAnalogClock) {
		mAnalogClock.onTimeChanged();
	}

	public TimezoneAnalogClock(Context context) {
		this(context, null);
		mContext = context;
	}

	public TimezoneAnalogClock(Context context, AttributeSet attributeSet) {
		this(context, attributeSet, 0);
		mContext = context;
	}

	public TimezoneAnalogClock(Context context, AttributeSet attributeSet, int paramInt) {
		super(context, attributeSet, paramInt);
		mContext = context;
		//获取图片资源
		Resources resources = context.getResources();
	    mClockDial = resources.getDrawable(R.drawable.clock_dial);
	    mClockHandHour = resources.getDrawable(R.drawable.clock_hand_hour);
	    mClockHandMin = resources.getDrawable(R.drawable.clock_hand_minute);
	    mClockHandSec = resources.getDrawable(R.drawable.clock_hand_second);
	    mTime = new Time();
	    //获取表盘大小信息
	    mClockDialWidth = mClockDial.getIntrinsicWidth();
	    mClockDialHeight = mClockDial.getIntrinsicHeight();
	}

	//更新时钟UI风格
	private void updateDial(Time time) {
		setContentDescription(DateUtils.formatDateTime(mContext, time.toMillis(false), 129));
		Resources resources = mContext.getResources();
    
		//昼夜显示不同的风格
		if ((mHour < 6.0F) || (mHour >= 18.0F)) {
			mClockDial = resources.getDrawable(R.drawable.clock_dial_black);
			mClockHandHour = resources.getDrawable(R.drawable.clock_hand_hour_black);
			mClockHandMin = resources.getDrawable(R.drawable.clock_hand_minute_black);
		} else {
			mClockDial = resources.getDrawable(R.drawable.clock_dial);
			mClockHandHour = resources.getDrawable(R.drawable.clock_hand_hour);
			mClockHandMin = resources.getDrawable(R.drawable.clock_hand_minute);
		}
		mClockHandSec = resources.getDrawable(R.drawable.clock_hand_second);
	}

  
  	private void onTimeChanged() {
  		//更新为当前时间
  		mTime.setToNow();
  		int hour = mTime.hour;
  		int minute = mTime.minute;
  		int second = mTime.second;
  		mSecond = second;
  		mMinute = (minute + second / 60.0F);
  		mHour = (hour + mMinute / 60.0F);
  		mChanged = true;
  		updateDial(mTime);
  	}

  	//根据时区获取时间
  	public void setTimezone(String timezone) {
  		if (TextUtils.isEmpty(timezone))
  			return;
  		mTime = new Time(timezone);
  		onTimeChanged();
  		invalidate();
  	}

  
  	@Override
  	protected void onAttachedToWindow() {
  		super.onAttachedToWindow();
  		mStop = false;
  		mHandler = new Handler();
  		mRunnable = new TimezoneAnalogClockRunnable(this);
  		mRunnable.run();
  		onTimeChanged();
  	}

  	
  	@Override
	protected void onDetachedFromWindow() {
	    super.onDetachedFromWindow();

	    mStop = true;
  	}

  	
  @Override
	protected void onDraw(Canvas canvas) {
	    int scaled = 0;
	    super.onDraw(canvas);
	    boolean flag = mChanged;
	    if (flag)
	      mChanged = false;
	    int width = this.mRight - this.mLeft;//画布宽度
	    int height = this.mBottom - this.mTop;//画布高度
	    int CenterX = width / 2;//画布中心点X坐标
	    int CenterY = height / 2;//画布中心点Y坐标
	    Drawable clockdial = mClockDial;
	    int dialWidth = clockdial.getIntrinsicWidth();//表盘图片宽度
	    int dialHeight = clockdial.getIntrinsicHeight();//表盘图片高度
	    if ((width < dialWidth) || (height < dialHeight)) {
	    	scaled = 1;
	    	//整体宽、高同比例缩小,取二者小值
	    	float dialScale = Math.min(width / dialWidth, height / dialHeight);
	    	canvas.save();
	    	canvas.scale(dialScale, dialScale, CenterX, CenterY);
	    }
	    
	    //画表盘
	    if (flag) {
	    	//根据中心点确定显示位置
	    	clockdial.setBounds(CenterX - dialWidth / 2, CenterY - dialWidth / 2, CenterX + dialWidth / 2, CenterY + dialHeight / 2);
	    }
	    clockdial.draw(canvas);
	    
	    //画时针
	    canvas.save();
	    canvas.rotate(360.0F * (mHour / 12.0F), CenterX, CenterY);//根据时间旋转
	    Drawable clockHour = mClockHandHour;
	    if (flag) {
	    	int hourWidth = clockHour.getIntrinsicWidth();
	    	int hourHeight = clockHour.getIntrinsicHeight();
	    	//根据中心点确定画布上的位置
	    	clockHour.setBounds(CenterX - hourWidth / 2, CenterY - hourHeight / 2, CenterX + hourWidth / 2, CenterY + hourHeight / 2);
	    }
	    clockHour.draw(canvas);
	    canvas.restore();
	    
	    //画分针
	    canvas.save();
	    canvas.rotate(360.0F * (mMinute / 60.0F), CenterX, CenterY);//根据时间旋转
	    Drawable clockMinute = mClockHandMin;
	    if (flag) {
	    	int minuteWidth = clockMinute.getIntrinsicWidth();
	    	int minuteHeight = clockMinute.getIntrinsicHeight();
	    	//根据中心点确定画布上的位置
	    	clockMinute.setBounds(CenterX - minuteWidth / 2, CenterY - minuteHeight / 2, CenterX + minuteWidth / 2, CenterY + minuteHeight / 2);
	    }
	    clockMinute.draw(canvas);
	    canvas.restore();
	    
	    //画秒针
	    canvas.save();
	    canvas.rotate(360.0F * (mSecond / 60.0F), CenterX, CenterY);//根据时间旋转
	    Drawable clockSec = mClockHandSec;
	    if (flag) {
	    	int secWidth = clockSec.getIntrinsicWidth();
	    	int secHeight = clockSec.getIntrinsicHeight();
	    	//根据中心点确定画布上的位置
	    	clockSec.setBounds(CenterX - secWidth / 2, CenterY - secHeight / 2, CenterX + secWidth / 2, CenterY + secHeight / 2);
	    }
	    clockSec.draw(canvas);
	    canvas.restore();
	    
	    if (scaled != 0) 
	    	canvas.restore();
  	}

  	//根据实际情况摆放控件元素
  	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
	    float scaleH = 1.0F;
	    float scaleW = 1.0F;
	    int widthMode = View.MeasureSpec.getMode(widthMeasureSpec);
	    int widthSize = View.MeasureSpec.getSize(widthMeasureSpec);
	    int heightMode = View.MeasureSpec.getMode(heightMeasureSpec);
	    int heightSize = View.MeasureSpec.getSize(heightMeasureSpec);

	    //参考配置参数计算宽度的缩小比例
	    if ((widthMode != MeasureSpec.UNSPECIFIED) && (widthSize < mClockDialWidth)) {
	    	scaleW = widthSize / mClockDialWidth;
	    }	
	    //参考配置参数计算高度的缩小比例
	    if ((heightMode != MeasureSpec.UNSPECIFIED) && (heightSize < mClockDialHeight)) {
	    	scaleH = heightSize / mClockDialHeight;
	    }
	    //确保宽、高同比例缩小,取二者小值
	    float scale = Math.min(scaleW, scaleH);
	    //比较视图的期望大小返回一个合适的值,设置最终宽、高
	    setMeasuredDimension(resolveSizeAndState((int)(scale * mClockDialWidth), widthMeasureSpec, 0), resolveSizeAndState((int)(scale * mClockDialHeight), heightMeasureSpec, 0));
  	}

  	//获取变化状态
  	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  		super.onSizeChanged(w, h, oldw, oldh);
  		mChanged = true;
  	}
}

TimezoneAnalogClockRunnable.java

import android.os.Handler;
import android.os.SystemClock;

class TimezoneAnalogClockRunnable
  implements Runnable {
	TimezoneAnalogClock mAnalogClock; 
	TimezoneAnalogClockRunnable(TimezoneAnalogClock timezoneAnalogClock) {
		mAnalogClock = timezoneAnalogClock;
	}

	
	@Override
	public void run() {
		long now, runtime;
		if (TimezoneAnalogClock.getRunningState(mAnalogClock)) {
			//每秒种刷新一次
			now= SystemClock.uptimeMillis();
			runtime = now + (1000L - now % 1000L);
		    TimezoneAnalogClock.getHandler(mAnalogClock).postAtTime(TimezoneAnalogClock.getRunnable(mAnalogClock), runtime);
		    TimezoneAnalogClock.onTimeChanged(mAnalogClock);
		    mAnalogClock.invalidate();
		}
	}
}

效果图:



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值