我编写了一个基于 android.widget.Chronometer 的简单国际象棋时钟应用程序。我将它与
setCountDown(true)
标志一起使用,并且所有逻辑都运行良好。每个天文台表都在单独的Fragment
中创建。但有时天文台初始值会出现一些问题。例如,我将初始值设置为 20 秒,但实际上我有这个:
在应用程序重新启动时,这种区别可能是在随机时钟上。我这样设置初始值,在我的类中扩展android.widget.Chronometer
,并且mTimeLimit每次等于20000:
private void setTimeLimit() {
mStartTime = SystemClock.elapsedRealtime() + mTimeLimit;
setBase(mStartTime);
}
我认为问题可能出在片段创建时间等方面。谁知道我错了什么?
我通过在初始值上添加 100 millis 找到了一个解决方案:
mStartTime = SystemClock.elapsedRealtime() + mTimeLimit + 100;
似乎它解决了问题,但我认为这不是最好的决定,也许有人知道更好的方法。
出现问题的原因是 Chronometer 类通过截断毫秒的除法来计算秒,而它应该对结果进行四舍五入。
这在向上计数时不是问题,但在向下计数时却是问题。
我在问题跟踪器上打开了一个问题,请求修改 Chronometer 类中的计算:
https://issuetracker.google.com/issues/297733248
在计时器的基础上添加任意数量的毫秒可能在某些设备中有效,但在速度较慢的设备中,问题可能仍然会发生。
我的解决方案(当问题在问题跟踪器中得到解决时)是基于框架的 Chronometer 类创建一个自定义的“MyChronometer”类,但具有正确的计算(请参阅“updateText”方法中的计算):
@SuppressLint("AppCompatCustomView")
@RemoteView
public class MyChronometer extends TextView {
public interface OnChronometerTickListener {
void onChronometerTick(MyChronometer chronometer);
}
private long mBase;
private boolean mVisible;
private boolean mStarted;
private boolean mRunning;
private final StringBuilder mRecycle = new StringBuilder(8);
private OnChronometerTickListener mOnChronometerTickListener;
private boolean mCountDown;
public MyChronometer(Context context) {
this(context, null, 0);
}
public MyChronometer(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyChronometer(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public MyChronometer(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
mBase = SystemClock.elapsedRealtime();
updateText(mBase);
}
public void setCountDown(boolean countDown) {
mCountDown = countDown;
updateText(SystemClock.elapsedRealtime());
}
public void setBase(long base) {
mBase = base;
dispatchChronometerTick();
updateText(SystemClock.elapsedRealtime());
}
public long getBase() {
return mBase;
}
public void setOnChronometerTickListener(OnChronometerTickListener listener) {
mOnChronometerTickListener = listener;
}
public OnChronometerTickListener getOnChronometerTickListener() {
return mOnChronometerTickListener;
}
public void start() {
mStarted = true;
updateRunning();
}
public void stop() {
mStarted = false;
updateRunning();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mVisible = false;
updateRunning();
}
@Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
mVisible = visibility == VISIBLE;
updateRunning();
}
@Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
updateRunning();
}
private synchronized void updateText(long now) {
long diffMillis = mCountDown ? mBase - now : now - mBase;
int seconds = Math.round(diffMillis / 1000f);
String text;
if (seconds < 0) {
text = "-" + DateUtils.formatElapsedTime(mRecycle, -seconds);
} else {
text = DateUtils.formatElapsedTime(mRecycle, seconds);
}
setText(text);
}
private void updateRunning() {
boolean running = mVisible && mStarted && isShown();
if (running != mRunning) {
if (running) {
updateText(SystemClock.elapsedRealtime());
dispatchChronometerTick();
postDelayed(mTickRunnable, 1000);
} else {
removeCallbacks(mTickRunnable);
}
mRunning = running;
}
}
private final Runnable mTickRunnable = new Runnable() {
@Override
public void run() {
if (mRunning) {
updateText(SystemClock.elapsedRealtime());
dispatchChronometerTick();
postDelayed(mTickRunnable, 1000);
}
}
};
void dispatchChronometerTick() {
if (mOnChronometerTickListener != null) {
mOnChronometerTickListener.onChronometerTick(this);
}
}
@Override
public CharSequence getAccessibilityClassName() {
return MyChronometer.class.getName();
}
}