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小时制也不是当前的时间