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小時制也不是當前的時間