谷歌已经为我们提供了非常丰富的控件,一般任务均可完成,但是有时候一些特别任务还是需要我们自定义控件来完成。
本文尝试完成一个自定义钟表控件。
</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();
}
};
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/14821.html