1.简单数字时钟
学习通过Handler +线程刷新UI,时钟或者计时器练习
下面这段简短的代码就可以实现(关键代码就只有Handler的post和postDelayed方法),其中的机制(消息队列等)还要继续学习
public class RefreshActivity extends Activity implements Runnable {
private TextView tv;
private Handler h = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_refresh);
tv = (TextView) findViewById(R.id.tv);
h.post(this);
/*
* Handler的post方法
* final boolean post(Runnable r)
* Causes the Runnable r to be added to the message queue.
* 把RefreshActivity这个线程加到消息队列中
*/
}
@Override
public void run() {
// 线程体
Date date = new Date(System.currentTimeMillis());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = sdf.format(date);
tv.setText(time);
h.postDelayed(this, 1000);
/*
* Handler的postDelayed方法
* final boolean postDelayed(Runnable r, long delayMillis)
* Causes the Runnable r to be added to the message queue, to be run after the
* specified amount of time elapses.
*
* 把RefreshActivity这个线程延时1秒加入到消息队列
*/
}
}
这个是常规的比较完整的写法了,比较容易理解
/**
* 整个Activity开启一个UI主线程,负责子线程的管理、UI的更新
* */
public class NewRefreshActivity extends Activity {
private TextView tv;
private Handler handler;
private String time;
private boolean isRunning = true;//用这个标志位来让线程不断运行下去
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_refresh);
tv = (TextView) findViewById(R.id.tv);
/**
* Handler属于主线程,把子线程中传递过来的数据用来更新UI
*
* Handler之所以存在,是因为子线程是不能够更改创建UI的线程中的UI的
* */
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
tv.setText(msg.obj "");
}
};
/**
* 用来处理时间增长的子线程(匿名内部类)
* 在这里让时间以1秒为单位增长,并把改变了的时间放到msg的obj属性中,通过Handler传给主线程
*
* */
new Thread() {
@Override
public void run() {
super.run();
while(isRunning){
try {
sleep(1000);//睡眠1秒
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat(
"yyyy/MM/dd HH:mm:ss");
time = sdf.format(date);
Message msg = new Message();
msg.obj = time;
handler.sendMessage(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
2.关于android自带时钟小工具
Android自带的只有时针和分针的AnalogClock,只要在上面的代码中初始化就能走了,为什么呢?
4.关于自定义时钟
把需要的图片资源都放到drawable中,在自定义的View(继承自View类)中,用BitmapDrawble加载图片,在线程中每隔秒就重绘秒针图片、60秒重绘分针图片、360秒重绘时针图片(关于重绘的位置,还有点复杂,感觉)
下载了一个筒子写的自定义时钟源码,没怎么明白,差不多就是这么个思路
@RemoteView
public class AnalogClock extends View {
private BitmapDrawable mDialDrawable;
private BitmapDrawable mHourHandDrawable;
private BitmapDrawable mMinuteHandDrawable;
private BitmapDrawable mSecondHandDrawable;
private int mDialWidth;
private int mDialHeight;
private boolean mAttached = false;
private float mHours;
private float mMinutes;
private float mSeconds;
private int totaltime;
/**
* 标志时间、时钟布局大小等是否有改变
*/
private boolean mChanged;
/**
* 线程队列管理,消息传递和处理机制
*/
private Handler loopHandler = new Handler();
/**
* 标志页面刷新线程尚未执行
*/
private boolean isRun = false;
/**
* 时钟运行
*/
private void run()
{
/**
* 将线程加入队列
*/
loopHandler.post(tickRunnable);
}
private Runnable tickRunnable = new Runnable() {
public void run() {
/**
* 在非UI线程调用,强制刷新界面
*/
postInvalidate();
totaltime++;
/**
* 将线程加入队列,1000毫秒后启动
*/
loopHandler.postDelayed(tickRunnable, 1000);
}
};
/**
* 构造方法
*/
public AnalogClock(Context context) {
this(context, null);
}
public AnalogClock(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AnalogClock(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
totaltime = 0;
Resources r = this.getContext().getResources();
InputStream is =null;
/**
* 初始化表盘,时针,分针, 秒针
*/
is = r.openRawResource(R.drawable.clockdroid2_dial);
mDialDrawable = new BitmapDrawable(is);
is = r.openRawResource(R.drawable.clockdroid2_hour);
mHourHandDrawable = new BitmapDrawable(is);
is = r.openRawResource(R.drawable.clockdroid2_minute);
mMinuteHandDrawable = new BitmapDrawable(is);
is = r.openRawResource(R.drawable.clockdroid2_second);
mSecondHandDrawable = new BitmapDrawable(is);
/**
* 获取表盘有效像素宽高
*/
mDialWidth = mDialDrawable.getIntrinsicWidth();
mDialHeight = mDialDrawable.getIntrinsicHeight();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (!mAttached) {
mAttached = true;
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
getContext().registerReceiver(mIntentReceiver, filter, null, loopHandler);
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mAttached) {
getContext().unregisterReceiver(mIntentReceiver);
mAttached = false;
}
}
@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;
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 onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mChanged = true;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(!isRun)
{
run();
isRun = true;
return;
}
onTimeChanged();
boolean changed = mChanged;
if (changed) {
mChanged = false;
}
int availableWidth = getWidth();
int availableHeight = getHeight();
int x = availableWidth / 2;
int y = availableHeight / 2;
final Drawable dial = mDialDrawable;
int w = dial.getIntrinsicWidth();
int h = dial.getIntrinsicHeight();
if (changed) {
dial.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
}
dial.draw(canvas);
canvas.save();
canvas.rotate(mHours / 12.0f * 360.0f, x, y);
final Drawable hourHand = mHourHandDrawable;
if (changed) {
w = hourHand.getIntrinsicWidth();
h = hourHand.getIntrinsicHeight();
hourHand.setBounds(x - (w / 2), y - (h * 53 / 100), x + (w / 2), y + (h * 47 / 100));
}
hourHand.draw(canvas);
canvas.restore();
canvas.save();
canvas.rotate(mMinutes / 60.0f * 360.0f, x, y);
final Drawable minuteHand = mMinuteHandDrawable;
if (changed) {
w = minuteHand.getIntrinsicWidth();
h = minuteHand.getIntrinsicHeight();
minuteHand.setBounds(x - (w / 2), y - (h * 53 / 100), x + (w / 2), y + (h * 47 / 100));
}
minuteHand.draw(canvas);
canvas.restore();
canvas.save();
canvas.rotate(mSeconds / 60.0f * 360.0f, x, y);
final Drawable scendHand = mSecondHandDrawable;
if (changed) {
w = scendHand.getIntrinsicWidth();
h = scendHand.getIntrinsicHeight();
scendHand.setBounds(x - (w / 2), y - (h * 53 / 100), x + (w / 2), y + (h * 47 / 100));
}
scendHand.draw(canvas);
canvas.restore();
}
private void onTimeChanged() {
mSeconds = totaltime % 60;
mMinutes = totaltime / 60;
mHours = totaltime / 3600;
mChanged = true;
}
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
onTimeChanged();
invalidate();
}
};
}
这个显示,貌似不正确,两个钟显示不一样,数字时钟就按12小时制也不是当前的时间