自定义控件学习之钟表制作

谷歌已经为我们提供了非常丰富的控件,一般任务均可完成,但是有时候一些特别任务还是需要我们自定义控件来完成。

本文尝试完成一个自定义钟表控件。

</pre><pre name="code" class="java">package com.shusheng007.customviews;

import com.shusheng007.mp3plaryer.R;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.text.format.Time;
import android.util.AttributeSet;
import android.view.View;

public class CustomClockView extends View
{
	private Time mCalendar;
	// 表盘上的各指针图
	private Drawable mHourHand;
	private Drawable mMinuteHand;
	//表盘图片
	private Drawable mDial;
	
	// 定义表盘的宽和高
	private int mDialWidth;
	private int mDialHeight;

	private boolean mAttached;

	private final Handler mHandler = new Handler();
	private float mMinutes;
	private float mHour;
	//用于判断,view是否需要重新绘制
	private boolean mChanged;
	
	public CustomClockView(Context context)
	{
		this(context,null);
	}

	public CustomClockView(Context context, AttributeSet attrs)
	{
		this(context, attrs,0);
	}

	public CustomClockView(Context context, AttributeSet attrs, int defStyleAttr)
	{
		super(context, attrs, defStyleAttr);
		Resources r = context.getResources();
		
		/*
		 * 通过资源加载各图片,用于绘制时钟
		 */
		mDial = r.getDrawable(R.drawable.clock_dial);
		mHourHand = r.getDrawable(R.drawable.clock_hour);
		mMinuteHand = r.getDrawable(R.drawable.clock_minute);

		mCalendar = new Time();
		mDialWidth = mDial.getIntrinsicWidth();
		mDialHeight = mDial.getIntrinsicHeight();
	}
	/*
	 * 要计算该组件的宽和高时调用该方法
	 */
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
	{
		//获得模式和尺寸
		int widthMode = MeasureSpec.getMode(widthMeasureSpec);
		int widthSize = MeasureSpec.getSize(widthMeasureSpec);
		int heightMode = MeasureSpec.getMode(heightMeasureSpec);
		int heightSize = MeasureSpec.getSize(heightMeasureSpec);
		
		float hScale = 1.0f;
		float vScale = 1.0f;
		
		//判断尺寸模式是否为Unspecified,如果不是就需要判断表盘大小是否超出容器尺寸
		if (widthMode != MeasureSpec.UNSPECIFIED && widthSize < mDialWidth) {
			hScale = (float) widthSize / (float) mDialWidth;
		}

		if (heightMode != MeasureSpec.UNSPECIFIED && heightSize < mDialHeight) {
			vScale = (float) heightSize / (float) mDialHeight;
		}
		
		//如果表盘图像尺寸超出其组件的尺寸,则需要缩放
		float scale = Math.min(hScale, vScale);
		//此函数必须调用,用于设置空间尺寸
		setMeasuredDimension(
				resolveSize((int) (mDialWidth * scale), widthMeasureSpec),
				resolveSize((int) (mDialHeight * scale), heightMeasureSpec));
	}
	
	@Override
	protected void onDraw(Canvas canvas)
	{
		// TODO Auto-generated method stub
		super.onDraw(canvas);
		// 用一个标识来判断是否要重新绘
		boolean changed = mChanged;
		if (changed) {
			mChanged = false;
		}
		
		// 获取在其父容器中的的位置信
		final int mRight = getRight();
		final int mLeft = getLeft();
		final int mTop = getTop();
		final int mBottom = getBottom();
		
		// 计算实际的宽和高
		int availableWidth = mRight - mLeft;
		int availableHeight = mBottom - mTop;

		// 计算时钟的原点
		int x = availableWidth / 2;
		int y = availableHeight / 2;
		
		// 表盘的宽和高
		final Drawable dial = mDial;
		int w = dial.getIntrinsicWidth();
		int h = dial.getIntrinsicHeight();
		
		boolean scaled = false;
		// 利用实际宽高和表盘的宽高,判断是否需要缩放画布
		if (availableWidth < w || availableHeight < h) {
			scaled = true;
			float scale = Math.min((float) availableWidth / (float) w,
					(float) availableHeight / (float) h);
			canvas.save();
			canvas.scale(scale, scale, x, y);
		}
		
		if (changed) {
			dial.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
		}
		dial.draw(canvas);
		
		canvas.save();
		canvas.rotate(mHour / 12.0f * 360.0f, x, y);
		final Drawable hourHand = mHourHand;
		if (changed) {
			w = hourHand.getIntrinsicWidth();
			h = hourHand.getIntrinsicHeight();
			hourHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y
					+ (h / 2));
		}
		hourHand.draw(canvas);
		canvas.restore();
		
		canvas.save();
		canvas.rotate(mMinutes / 60.0f * 360.0f, x, y);

		final Drawable minuteHand = mMinuteHand;
		if (changed) {
			w = minuteHand.getIntrinsicWidth();
			h = minuteHand.getIntrinsicHeight();
			minuteHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y
					+ (h / 2));
		}
		minuteHand.draw(canvas);
		canvas.restore();
		
		if (scaled) {
			canvas.restore();
		}

		Matrix matrix = new Matrix();
		matrix.setRotate(45, 50, 50);
		matrix.setScale(1.5f, 1.5f);
		canvas.concat(matrix);
	}
	/*
	 * (non-Javadoc)
	 * 
	 * @see android.view.View#onAttachedToWindow()
	 * 在组件绘制到window之前调用,这时组件的相关资源已经读取完毕。一般在该方法中进行逻辑上的初始化操作�1�?�?�?
	 * 
	 * 在该实例中,进行了IntentReceiver注册的操作�1�?�?�?
	 */
	@Override
	protected void onAttachedToWindow() {
		super.onAttachedToWindow();
		onTimeChanged();
		if (!mAttached) {
			mAttached = true;
			IntentFilter filter = new IntentFilter();
			// 注册时间改变的Intent,当时间变动时改变时钟时�?�?
			filter.addAction(Intent.ACTION_TIME_TICK);
			filter.addAction(Intent.ACTION_TIME_CHANGED);
			getContext().registerReceiver(mIntentReceiver, filter, null,
					mHandler);
		}
	}

	/*
	 * @see android.view.View#onDetachedFromWindow()
	 * 当该组件不再window上显示时调用。一般进行一些IntentReceiver的销毁工作
	 */
	@Override
	protected void onDetachedFromWindow() {
		super.onDetachedFromWindow();
		if (mAttached) {
			getContext().unregisterReceiver(mIntentReceiver);
			mAttached = false;
		}
	}

	private void onTimeChanged() {
		long time = System.currentTimeMillis();
		mCalendar.set(time);

		int hour = mCalendar.hour;
		int minute = mCalendar.minute;
		int second = mCalendar.second;

		mMinutes = minute + second / 60.0f;
		mHour = hour + mMinutes / 60.0f;
		mChanged = true;
	}

	private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			onTimeChanged();
			//使view显示内容无效,然后View,执行ondraw重绘
			invalidate();
		}
	};
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值