自訂 AnalogClock 繼承 View,使用 CountDownTimer 每秒觸發 invalidate() 重繪。實際運行時秒針間歇性停滯,隨後一次跳動多格(如跳 3 秒),體驗極差。
MyCount extends CountDownTimer(10000, 1000):理論上每 1 秒 onTick 一次,10 秒後 onFinish 重啟。onTick 內取得當前秒數,計算 mSecond = 6.0f * second,設旗標 mSeconds = true,呼叫 invalidate()。onDraw 中根據 mSeconds 決定是否重新設定秒針邊界並繪製。Handler.sendMessageDelayed 實現,受主線程訊息佇列擁塞影響,實際觸發間隔可能超過 1 秒。onFinish 重啟機制導致時間漂移:每 10 秒重建一次定時器,累積誤差越來越大。onDraw 執行多次 canvas.rotate/save/restore、Drawable.setBounds/draw,若佈局複雜易掉幀。mSeconds 旗標邏輯缺陷:僅在 onTick 置 true,onDraw 首次繪製後即置 false,若同一秒內重繪多次(如視窗遮擋),秒針會閃爍或靜止。private final Handler mHandler = new Handler(Looper.getMainLooper());
private final Runnable mTickRunnable = new Runnable() {
@Override public void run() {
mCalendar.setToNow();
mSecond = 6.0f * mCalendar.second;
mSeconds = true;
invalidate();
mHandler.postDelayed(this, 1000); // 固定 1 秒後再次執行
}
};
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mHandler.post(mTickRunnable); // 啟動
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mHandler.removeCallbacks(mTickRunnable); // 停止
}
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override public void doFrame(long frameTimeNanos) {
mCalendar.setToNow();
mSecond = 6.0f * mCalendar.second + (mCalendar.get(Time.MILLISECOND) / 1000f) * 6.0f;
invalidate();
Choreographer.getInstance().postFrameCallback(this);
}
};
Drawable 邊界計算移出 onDraw,僅在尺寸變更(onSizeChanged)時更新。setLayerType(LAYER_TYPE_HARDWARE, null)(API 14+)。Path 或 drawLine 直接繪製指針,替代 Drawable 降低開銷。onDraw 開頭加入 if (!mSeconds && !mChanged) return; 避免無效重繪。在 onTick / doFrame 打印 Log.d("Clock", "tick: " + System.currentTimeMillis()),觀察實際觸發間隔;同時用 adb shell dumpsys gfxinfo <pkg> 檢查幀率分布。