有这么一个需求:要求在应用中每个界面都有一个时钟显示时间,以秒更新。
自然而然,就想到了自定义View—以TextView+Handler方式倒是虽然可以实现,但难免累赘;
而时钟显示肯定要本身维护一个更新UI的线程,继承View固然是正确的,但不如继承SurfaceView来得更为直接—作为一个经常用来播放视频和展现游戏画面的控件,无疑是最佳选择。
很简单的东西,除了surfaceview的通常注意事项,额外注意三点即可:
- 内容位置—行间距的处理
- 适配处理—根据内容大小调整边界
- 清屏刷新—最好使背景透明以便复用
代码如下:
public class DigitalClockView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
/**
* 视图控制
*/
private SurfaceHolder mHolder;
/**
* 画布
*/
private Canvas mCanvas;
/**
* 线程开启或停止标志
*/
private boolean isRun;
/**
* 日期画笔
*/
private TextPaint mDatePaint;
/**
* 时间画笔
*/
private TextPaint mTimePaint;
/**
* 日期格式
*/
private String mDateFormat;
/**
* 时间格式
*/
private String mTimeFormat;
/**
* 日期文字大小
*/
private float mDateTextSize;
/**
* 时间文字大小
*/
private float mTimeTextSize;
/**
* 日期文字内容距边界大小
*/
private float mDateTopPadding;
/**
* 时间文字内容距日期文字内容大小
*/
private float mTimeTopPadding;
public DigitalClockView(Context context) {
this(context, null);
}
public DigitalClockView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DigitalClockView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
int dateTextColor = -1;
int timeTextColor = -1;
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DigitalClockView);
final int count = a.getIndexCount();
for (int i = 0; i < count; i++) {
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.DigitalClockView_date_format:
mDateFormat = a.getString(attr);
break;
case R.styleable.DigitalClockView_time_format:
mTimeFormat = a.getString(attr);
break;
case R.styleable.DigitalClockView_date_text_size:
mDateTextSize = a.getDimension(attr, 20);
break;
case R.styleable.DigitalClockView_time_text_size:
mTimeTextSize = a.getDimension(attr, 20);
break;
case R.styleable.DigitalClockView_date_text_color:
dateTextColor = a.getColor(attr, -1);
break;
case R.styleable.DigitalClockView_time_text_color:
timeTextColor = a.getColor(attr, -1);
break;
}
}
// 文字画笔
mDatePaint = new TextPaint();
mTimePaint = new TextPaint();
// 画笔是否抗锯齿
mDatePaint.setAntiAlias(true);
mTimePaint.setAntiAlias(true);
// 文字居中
mDatePaint.setTextAlign(Paint.Align.CENTER);
mTimePaint.setTextAlign(Paint.Align.CENTER);
// 文字颜色
mDatePaint.setColor(dateTextColor);
mTimePaint.setColor(timeTextColor);
// 文字大小
mDatePaint.setTextSize(mDateTextSize);
mTimePaint.setTextSize(mTimeTextSize);
// 文字绘制位置
Paint.FontMetrics fontMetrics1 = mDatePaint.getFontMetrics();
Paint.FontMetrics fontMetrics2 = mTimePaint.getFontMetrics();
mDateTopPadding = Math.abs(fontMetrics1.top);
mTimeTopPadding = fontMetrics1.bottom - fontMetrics1.top + Math.abs(fontMetrics2.ascent);
// 获取控制
mHolder = getHolder();
mHolder.addCallback(this);
// 背景透明
this.setZOrderOnTop(true);
this.getHolder().setFormat(PixelFormat.TRANSPARENT);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
isRun = true;
new Thread(this).start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
//一般不必覆写
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
isRun = false;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int desiredWidth, desiredHeight;
if (widthMode == MeasureSpec.EXACTLY) {
desiredWidth = widthSize;
} else {
// 以文字宽度为适配
int dateTextWidth = (int) mDatePaint.measureText(mDateFormat);
int timeTextWidth = (int) mTimePaint.measureText(mTimeFormat);
desiredWidth = Math.max(dateTextWidth, timeTextWidth);
if (widthMode == MeasureSpec.AT_MOST) {
desiredWidth = Math.min(widthSize, desiredWidth);
}
}
if (heightMode == MeasureSpec.EXACTLY) {
desiredHeight = heightSize;
} else {
// 以文字高度为适配
Paint.FontMetrics fontMetrics1 = mDatePaint.getFontMetrics();
Paint.FontMetrics fontMetrics2 = mTimePaint.getFontMetrics();
desiredHeight = (int) (fontMetrics1.bottom - fontMetrics1.top + fontMetrics2.bottom - fontMetrics2.top);
if (heightMode == MeasureSpec.AT_MOST) {
desiredHeight = Math.min(heightSize, desiredHeight);
}
}
setMeasuredDimension(desiredWidth, desiredHeight);
}
@Override
public void run() {
long start, end;
while (isRun) {
start = System.currentTimeMillis();
draw();
end = System.currentTimeMillis();
// 消除延时
try {
if (end - start < 1000) {
Thread.sleep(1000 - (end - start));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void draw() {
try {
mCanvas = mHolder.lockCanvas();
if (mCanvas != null) {
// 刷屏,透明覆盖
mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SRC);
// 绘制文字
mCanvas.drawText(SysUtil.getDate(System.currentTimeMillis(), mDateFormat), mCanvas.getWidth() / 2,
mDateTopPadding, mDatePaint);
mCanvas.drawText(SysUtil.getDate(System.currentTimeMillis(), mTimeFormat), mCanvas.getWidth() / 2,
mTimeTopPadding, mTimePaint);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (mCanvas != null) {
mHolder.unlockCanvasAndPost(mCanvas);
}
}
}
自定义属性attrs.xml,包括了时间格式、字体大小、字体颜色三种属性,在代码中读取设置即可:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="DigitalClockView">
<attr name="date_format" format="string|reference"/>
<attr name="time_format" format="string|reference"/>
<attr name="date_text_size" format="dimension|reference"/>
<attr name="time_text_size" format="dimension|reference"/>
<attr name="date_text_color" format="color|reference"/>
<attr name="time_text_color" format="color|reference"/>
</declare-styleable>
</resources>
最后是应用activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/darkviolet" >
<com.xter.clock.view.DigitalClockView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
app:date_format="yyyy年MM月dd日"
app:date_text_color="@color/white"
app:date_text_size="@dimen/text_18"
app:time_format="HH:mm:ss"
app:time_text_color="@color/white"
app:time_text_size="@dimen/text_34" />
</RelativeLayout>
效果图:
源码: