前言:网上有许多关于自定义view的讲解,加上自己对自定义view挺感兴趣的以及公司的需求正好用到,所以写个简单的自定义view---数字插件。
基础:(1)自绘控件在构造函数中获取控件的属性,包括自定义属性;
(2)onMeasure测量控件本身以及自控件的大小。
(3)onLayout测量控件布局。
(4)ondraw对控件本身进行绘制。
(5)dispatchDraw通知子view绘制视图。
(6)onAttachedToWindow在第一次ondraw之前调用,通过可以作一些初始化的工作,例如:注册广播。
(7)onDetachedFromWindow销毁view的时候调用,可以作一些收尾的工作,例如:取消注册广播。
分析:数字时钟前两位显示小时的高位和地位,中间一个冒号,后面是分钟的高位和地位,如:12:35;所以本次以两种方式来实现:
(1)自绘控件:关键就是重写ondraw函数,在ondraw函数中完全绘制视图显示的内容。
1)在构造函数中,通过如下代码获取控件的属性值,并对变量进行赋值:
Resources r = context.getResources();
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BirdDigitalClockView, defStyle, 0);
int res_id = a.getResourceId(R.styleable.BirdDigitalClockView_time_res, 0);
mTimeDrawableArray = r.obtainTypedArray(res_id);
int size = mTimeDrawableArray.length();
mHourHigh = mTimeDrawableArray.getDrawable(0);
mHourLow = mTimeDrawableArray.getDrawable(1);
mMinuteHigh = mTimeDrawableArray.getDrawable(2);
mMinuteLow = mTimeDrawableArray.getDrawable(3);
mDivider = mTimeDrawableArray.getDrawable(size - 1);
a.recycle();
2)在onAttachedToWindow方法中注册广播
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_TIME_TICK);
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
getContext().registerReceiver(mIntentReceiver, filter, null, mHandler);
3)相应的在onDetachedFromWindow取消广播
if (mAttached) {
getContext().unregisterReceiver(mIntentReceiver);
mAttached = false;
}
4)在广播中监听,收到相应的广播就更新界面
if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {
String tz = intent.getStringExtra("time-zone");
mCalendar = new Time(TimeZone.getTimeZone(tz).getID());
}
onTimeChanged();
invalidate();
5)最后最终代码如下:
package com.example.myclockviewtest;
import java.util.Calendar;
import java.util.TimeZone;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.text.format.DateFormat;
import android.text.format.Time;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
/**
* 自绘控件
*/
public class BirdDigitalClockView extends View {
private Time mCalendar;
private Drawable mHourHigh;
private Drawable mHourLow;
private Drawable mMinuteHigh;
private Drawable mMinuteLow;
private Drawable mDivider;
private TypedArray mTimeDrawableArray;
private boolean mAttached;
private final Handler mHandler = new Handler();
private boolean mChanged;
public BirdDigitalClockView(Context context) {
this(context, null);
}
public BirdDigitalClockView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BirdDigitalClockView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
Resources r = context.getResources();
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BirdDigitalClockView, defStyle, 0);
int res_id = a.getResourceId(R.styleable.BirdDigitalClockView_time_res, 0);
mTimeDrawableArray = r.obtainTypedArray(res_id);
int size = mTimeDrawableArray.length();
mHourHigh = mTimeDrawableArray.getDrawable(0);
mHourLow = mTimeDrawableArray.getDrawable(1);
mMinuteHigh = mTimeDrawableArray.getDrawable(2);
mMinuteLow = mTimeDrawableArray.getDrawable(3);
mDivider = mTimeDrawableArray.getDrawable(size - 1);
a.recycle();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (!mAttached) {
mAttached = true;
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_TIME_TICK);
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
getContext().registerReceiver(mIntentReceiver, filter, null, mHandler);
}
mCalendar = new Time();
onTimeChanged();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mAttached) {
getContext().unregisterReceiver(mIntentReceiver);
mAttached = false;
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
boolean changed = mChanged;
if (changed) {
mChanged = false;
}
int availableWidth = getMeasuredWidth();
int w = 0;
// HourHigh
final Drawable hourHigh = mHourHigh;
int mHourHighWidth = hourHigh.getIntrinsicWidth();
int mHourHighHeight = hourHigh.getIntrinsicHeight();
// HourLow
final Drawable hourLow = mHourLow;
int mHourLowWidth = hourLow.getIntrinsicWidth();
int mHourLowHeight = hourLow.getIntrinsicHeight();
// dot
final Drawable divider = mDivider;
int mDividerWidth = divider.getIntrinsicWidth();
// MinuteHigh
final Drawable minuteHigh = mMinuteHigh;
int mMinuteHighWidth = minuteHigh.getIntrinsicWidth();
int mMinuteHighHeight = minuteHigh.getIntrinsicHeight();
// MinuteLow
final Drawable minuteLow = mMinuteLow;
int mMinuteLowWidth = minuteLow.getIntrinsicWidth();
int mMinuteLowHeight = minuteLow.getIntrinsicHeight();
// because all drawables's height is same
int height = mMinuteLowHeight;
int temp = availableWidth / 2 - divider.getIntrinsicWidth() / 2;
int temp_ = temp;
canvas.save();
// dot
if (changed) {
w = divider.getIntrinsicWidth();
divider.setBounds(temp, 0, temp + w, height);
temp = temp + w;
}
divider.draw(canvas);
canvas.restore();
canvas.save();
// MinuteHigh
if (changed) {
w = minuteHigh.getIntrinsicWidth();
minuteHigh.setBounds(temp, 0, temp + w, height);
temp = temp + w;
}
minuteHigh.draw(canvas);
canvas.restore();
canvas.save();
// MinuteLow
if (changed) {
w = minuteLow.getIntrinsicWidth();
minuteLow.setBounds(temp, 0, temp + w, height);
temp = temp_;
}
minuteLow.draw(canvas);
canvas.restore();
canvas.save();
// hourLow
if (changed) {
w = hourLow.getIntrinsicWidth();
hourLow.setBounds(temp - w, 0, temp, height);
temp = temp - w;
}
hourLow.draw(canvas);
canvas.restore();
canvas.save();
// HourHigh
if (changed) {
w = hourHigh.getIntrinsicWidth();
hourHigh.setBounds(temp - w, 0, temp, height);
}
hourHigh.draw(canvas);
canvas.restore();
canvas.save();
}
private void onTimeChanged() {
mCalendar.setToNow();
mChanged = true;
updateTime();
}
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {
String tz = intent.getStringExtra("time-zone");
mCalendar = new Time(TimeZone.getTimeZone(tz).getID());
}
onTimeChanged();
invalidate();
}
};
private void updateTime() {
final Calendar myCalendar = Calendar.getInstance(TimeZone.getDefault());
myCalendar.setTimeInMillis(System.currentTimeMillis());
int my_hour_high = 0;
int my_hour_low = 0;
int my_min_high = 0;
int my_min_low = 0;
int my_Hour = myCalendar.get(Calendar.HOUR_OF_DAY);
int my_Minute = myCalendar.get(Calendar.MINUTE);
if (DateFormat.is24HourFormat(getContext())) {
if (my_Hour == 24) {
my_Hour = 0;
}
my_hour_high = my_Hour / 10;
my_hour_low = my_Hour % 10;
} else {
if (my_Hour <= 12) {
if (my_Hour == 0) {
my_Hour = 12;
my_hour_high = my_Hour / 10;
my_hour_low = my_Hour % 10;
} else {
my_hour_high = my_Hour / 10;
my_hour_low = my_Hour % 10;
}
} else {
my_Hour = my_Hour - 12;
my_hour_high = my_Hour / 10;
my_hour_low = my_Hour % 10;
}
}
my_min_high = my_Minute / 10;
my_min_low = my_Minute % 10;
mHourHigh = mTimeDrawableArray.getDrawable(my_hour_high);
mHourLow = mTimeDrawableArray.getDrawable(my_hour_low);
mMinuteHigh = mTimeDrawableArray.getDrawable(my_min_high);
mMinuteLow = mTimeDrawableArray.getDrawable(my_min_low);
}
}
(2)组合控件:本例中组合控件和自绘控件最大的不同点就是:视图的绘制不需要自己去实现,包括大小、布局;但是关键点还是一样的,直接上代码:
package com.example.myclockviewtest;
import java.util.Calendar;
import java.util.TimeZone;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.text.format.DateFormat;
import android.text.format.Time;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.LinearLayout;
/*
* 组合控件
*/
public class BirdDigitalClockView2 extends LinearLayout {
private ImageView hour;
private ImageView hour_2;
private ImageView dot;
private ImageView minute;
private ImageView minute_2;
private TypedArray mTimeDrawableArray;
private Drawable mHourHigh;
private Drawable mHourLow;
private Drawable mMinuteHigh;
private Drawable mMinuteLow;
private Drawable mDivider;
private Time mCalendar;
private boolean mAttached = false;
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {
String tz = intent.getStringExtra("time-zone");
mCalendar = new Time(TimeZone.getTimeZone(tz).getID());
}
onTimeChanged();
invalidate();
}
};
public BirdDigitalClockView2(Context context) {
this(context, null);
}
public BirdDigitalClockView2(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BirdDigitalClockView2(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
LayoutInflater.from(context).inflate(R.layout.clock, this);
hour = (ImageView) findViewById(R.id.hour);
hour_2 = (ImageView) findViewById(R.id.hour_2);
dot = (ImageView) findViewById(R.id.dot);
minute = (ImageView) findViewById(R.id.minute);
minute_2 = (ImageView) findViewById(R.id.minute_2);
Resources r = context.getResources();
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.BirdDigitalClockView2, defStyle, 0);
int res_id = a.getResourceId(
R.styleable.BirdDigitalClockView2_time_res2, 0);
mTimeDrawableArray = r.obtainTypedArray(res_id);
int size = mTimeDrawableArray.length();
mHourHigh = mTimeDrawableArray.getDrawable(0);
mHourLow = mTimeDrawableArray.getDrawable(1);
mMinuteHigh = mTimeDrawableArray.getDrawable(2);
mMinuteLow = mTimeDrawableArray.getDrawable(3);
mDivider = mTimeDrawableArray.getDrawable(size - 1);
a.recycle();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (!mAttached) {
mAttached = true;
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_TIME_TICK);
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
getContext().registerReceiver(mIntentReceiver, filter, null, null);
}
mCalendar = new Time();
onTimeChanged();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mAttached) {
getContext().unregisterReceiver(mIntentReceiver);
mAttached = false;
}
}
private void onTimeChanged() {
mCalendar.setToNow();
updateTime();
}
private void updateTime() {
final Calendar myCalendar = Calendar.getInstance(TimeZone.getDefault());
myCalendar.setTimeInMillis(System.currentTimeMillis());
int my_hour_high = 0;
int my_hour_low = 0;
int my_min_high = 0;
int my_min_low = 0;
int my_Hour = myCalendar.get(Calendar.HOUR_OF_DAY);
int my_Minute = myCalendar.get(Calendar.MINUTE);
if (DateFormat.is24HourFormat(getContext())) {
if (my_Hour == 24) {
my_Hour = 0;
}
my_hour_high = my_Hour / 10;
my_hour_low = my_Hour % 10;
} else {
if (my_Hour <= 12) {
if (my_Hour == 0) {
my_Hour = 12;
my_hour_high = my_Hour / 10;
my_hour_low = my_Hour % 10;
} else {
my_hour_high = my_Hour / 10;
my_hour_low = my_Hour % 10;
}
} else {
my_Hour = my_Hour - 12;
my_hour_high = my_Hour / 10;
my_hour_low = my_Hour % 10;
}
}
my_min_high = my_Minute / 10;
my_min_low = my_Minute % 10;
mHourHigh = mTimeDrawableArray.getDrawable(my_hour_high);
mHourLow = mTimeDrawableArray.getDrawable(my_hour_low);
mMinuteHigh = mTimeDrawableArray.getDrawable(my_min_high);
mMinuteLow = mTimeDrawableArray.getDrawable(my_min_low);
hour.setImageDrawable(mHourHigh);
hour_2.setImageDrawable(mHourLow);
minute.setImageDrawable(mMinuteHigh);
minute_2.setImageDrawable(mMinuteLow);
}
}
(3)最后写好了控件只需要在xml文件中直接引用就可以了,例如:
<com.example.myclockviewtest.BirdDigitalClockView2
android:id="@+id/digital_clock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center_horizontal"
myapp:time_res2="@array/clock_widget_drawables" >
</com.example.myclockviewtest.BirdDigitalClockView2>
说明:1)myapp是一个命名空间,在xml的上面有定义,例如:xmlns:myapp="http://schemas.android.com/apk/res/com.example.myclockviewtest",其中http://schemas.android.com/apk/res/是固定的,后面是自定义类的包名。
2)time_res2这是一个自定义的属性,首先需要在attrs.xml文件中进行属性的声明:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="BirdDigitalClockView">
<attr name="time_res" format="reference" />
</declare-styleable>
<declare-styleable name="BirdDigitalClockView2">
<attr name="time_res2" format="reference" />
</declare-styleable>
</resources>
.....................................................................................................易水寒.....................................................................................................