英文:
One timer object that all timers in the TextView are updated
问题
My application should show several timers and start them at the same time.
应用程序应该显示多个计时器并同时启动它们。
The problem is that these don't run in sync. You can see this on the GIF, you can see that they lose synchronicity. After a certain time you can see that a timer is updated faster than the others. First timer 1 is displayed, followed by timer 2 with a delay and then again with a certain delay timer 3.
问题是它们不同步运行。您可以在GIF上看到这一点,可以看到它们失去了同步性。一段时间后,您可以看到一个计时器更新得比其他计时器快。首先显示计时器1,然后是计时器2,然后有一个延迟,然后是计时器3。
I want to create multiple timers and each timer has a different duration.
我想创建多个计时器,每个计时器都有不同的持续时间。
I want to display these timers in a TextView. I initialize this in an ArrayList in the MainActivity
. These are then added in a RecyclerView
so that they are also displayed. To start, I go through a for loop and execute the startTimer()
method for each individual object. Unfortunately, the times are not correct for every timer. That means one timer is faster, the second is slower and so on. So each timer starts at different times or the text changes at different times.
我想在TextView中显示这些计时器。我在MainActivity
中的ArrayList中初始化它们,然后将它们添加到RecyclerView
中,以便它们也会显示。为了启动,我通过for循环遍历并执行每个对象的startTimer()
方法。不幸的是,每个计时器的时间都不正确。这意味着一个计时器更快,第二个更慢,依此类推。因此,每个计时器在不同的时间开始,或者文本在不同的时间更改。
Have only one timing item (e.g., ScheduledExecutorService) in a viewmodel, not 10 in an activity. While you may want to depict 10 times, in the UI, you only need one timing item to tell you when a second has elapsed. Have the viewmodel emit details of the time values that should be rendered in the UI. Have the UI simply display those time values.
在视图模型中只有一个定时项(例如ScheduledExecutorService),而不是在活动中有10个。尽管您可能想描绘10次,但在UI中,您只需要一个定时项来告诉您何时已经过去了一秒。让视图模型发出应该在UI中呈现的时间值的详细信息。让UI简单地显示这些时间值。
This text on top was an answer. How could I build this in so that I only have one timer element and it controls the times? How can I let these timers run synchronously? Each timer should be updated and displayed at the same time. I look forward to an answer, thank you in advance for your support!
这段文本是一个答案。我应该如何构建它,以便我只有一个计时器元素,并且它控制时间?我如何让这些计时器同步运行?每个计时器都应该在同一时间更新和显示。我期待您的答案,提前感谢您的支持!
英文:
My application should show several timers and start them at the same time.
The problem is that these don't run in sync. You can see this on the GIF, you can see that they lose synchronicity.
After a certain time you can see that a timer is updated faster than the others. First timer 1 is displayed, followed by timer 2 with a delay and then again with a certain delay timer 3.
I want to create multiple timers and each timer has a different duration.
I want to display these timers in a TextView. I initialize this in an ArrayList in the MainActivity
. These are then added in a ReyclerView
so that they are also displayed. To start, I go through a for loop and execute the startTimer()
method for each individual object. Unfortunately, the times are not correct for every timer. That means one timer is faster, the second is slower and so on. So each timer starts at different times or the text changes at different times.
> Have only one timing item (e.g., ScheduledExecutorService) in a
> viewmodel, not 10 in an activity. While you may want to depict 10
> times, in the UI, you only need one timing item to tell you when a
> second has elapsed. Have the viewmodel emit details of the time values
> that should be rendered in the UI. Have the UI simply display those
> time values
This text on top was an answer. How could I build this in so that I only have one timer element and it controls the times? How can I let these timers run synchronously?
Each timer should be updated and displayed at the same time.
I look forward to an answer, thank you in advance for your support!
MainActivity
public class MainActivity extends AppCompatActivity implements ModelTimer.MyCallback {
private RecyclerView recyclerView;
private Button button_start;
private Dialog epicDialog;
public static ArrayList<ModelTimer> timerList;
public static TimerAdapter adapter;
public static Context context;
@Override
protected void onCreate(Bundle savedInstanceState) {
recyclerView = findViewById(R.id.recyclerview_timers);
button_start = findViewById(R.id.button_start);
timerList= new ArrayList<>();
for (int i = 1; i <= 10; i++) {
timerList.add(new Timer(i, 600000 * i))); //Each timer has a different time
}
recyclerView.setLayoutManager(recyclerViewLayoutManager);
adapter = new TimerAdapter(this, timerList);
recyclerView.setAdapter(adapter);
context = this;
button_start.setOnClickListener(v -> {
for (Timer timer: timerList) {
timer.startTimer();
});
@Override
public void updateMyText(int index, long time) {
timerList.get(index-1).setTime(time);
adapter.notifyDataSetChanged();
}
}
@Override
public void updateMyText(int index, long time) {
timerList.get(index-1).setTime(time);
adapter.notifyDataSetChanged();
}
}
ModelTimer
public class ModelTimer {
public interface MyCallback {
public void updateMyText(int index, long time);
}
private int index;
private long time;
private CountDownTimer mCountDownTimer;
private boolean mTimerRunning;
private long startTime;
private long mTimeLeftInMillis;
private String timeLeftFormatted;
private MyCallback myCallback = null;
public ModelTimer(int index, long startTimeMilliseconds, MyCallback callback) {
this.index = index;
this.time = startTimeMilliseconds;
mTimeLeftInMillis = startTimeMilliseconds;
startTime = startTimeMilliseconds;
this.myCallback = callback;
}
public void startTimer() {
mCountDownTimer = new CountDownTimer(mTimeLeftInMillis, 1000) {
@Override
public void onTick(long millisUntilFinished) {
mTimeLeftInMillis = millisUntilFinished;
updateCountDownText();
}
@Override
public void onFinish() {
mTimerRunning = false;
}
}.start();
mTimerRunning = true;
}
public void resetTimer() {
mCountDownTimer.cancel();
mTimerRunning = false;
mTimeLeftInMillis = startTime;
timeLeftFormatted = formattedTime(startTime);
changeText(index-1);
}
public void updateCountDownText() {
//System.out.println("ID: " + getIndex() + " " + mTimeLeftInMillis);
if(myCallback != null) {
myCallback.updateMyText(getIndex(), mTimeLeftInMillis);
}
}
private void changeText(int element) {
String[] data = timeLeftFormatted.split(":");
int hours = Integer.parseInt(data[0]);
int minutes = Integer.parseInt(data[1]);
int seconds = Integer.parseInt(data[2]);
int timeSeconds = seconds + 60 * minutes + 3600 * hours;
long milliseconds = TimeUnit.MILLISECONDS.convert(timeSeconds, TimeUnit.SECONDS);
MainActivity.countdownList.get(element).setTime(milliseconds);
MainActivity.adapter.notifyDataSetChanged();
}
public static long getMilliseconds(String time) {
String[] data = time.split(":");
int hours = Integer.parseInt(data[0]);
int minutes = Integer.parseInt(data[1]);
int seconds = Integer.parseInt(data[2]);
int timeSeconds = seconds + 60 * minutes + 3600 * hours;
return TimeUnit.MILLISECONDS.convert(timeSeconds, TimeUnit.SECONDS);
}
public static String formattedTime(long time) {
int milliToSec = (int) (time / 1000);
int hours = milliToSec / 3600;
int minutes = (milliToSec / 60) % 60;
int seconds = milliToSec % 60;
return String.format(Locale.getDefault(), "%02d:%02d:%02d", hours, minutes, seconds);
}
public int getIndex() {
return this.index;
}
public void setIndex(int index) {
this.index = index;
}
public long getTime() { return this.time; }
public void setTime(long time) {
this.time = time;
}
}
TimerAdapter
public class TimerAdapter extends RecyclerView.Adapter<TimerAdapter.ViewHolder> {
private Context mContext;
private ArrayList<ModelTimer> list;
static String indexCountdown;
TimerAdapter(Context context, ArrayList<ModelTimer> list) {
mContext = context;
this.list = list;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
View view = layoutInflater.inflate(R.layout.recyclerview_countdowns, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
ModelTimer timerItemList = list.get(position);
TextView index = holder.countdown_index;
TextView time = holder.countdown_time;
CardView layout = holder.layout_countdowns;
index.setText(timerItemList .getIndex() +"");
time.setText(ModelTimer.formattedTime(timerItemList .getTime()));
}
@Override
public int getItemCount() {
return list.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView countdown_index, countdown_time;
private CardView layout_countdowns;
public ViewHolder(@NonNull View itemView) {
super(itemView);
countdown_index = itemView.findViewById(R.id.coutdown_index);
countdown_time = itemView.findViewById(R.id.countdown_time);
layout_countdowns = itemView.findViewById(R.id.layout_countdown);
}
}
}
答案1
得分: 1
你可以尝试使用 CountDownTimer 来实现。代码如下所示:
public class TimerHelper {
public interface UpdateCallback {
/**
* @return true 表示希望继续更新,false 表示不需要更新
*/
boolean secondPassedCallback();
}
private ArrayList<UpdateCallback> callbacksList = new ArrayList<>();
/**
* @param maxTime 所有定时器的最大时间
*/
public void start(int maxTime) {
CountDownTimer mCountDownTimer = new CountDownTimer(maxTime, 1000) {
@Override
public void onTick(long millisUntilFinished) {
ArrayList<UpdateCallback> itemsToRemove = new ArrayList<>();
for (UpdateCallback updateCallback : callbacksList) {
boolean wantToBeUpdated = updateCallback.secondPassedCallback(); // 移除到达 0 的定时器
if (!wantToBeUpdated) {
itemsToRemove.add(updateCallback);
}
}
callbacksList.removeAll(itemsToRemove);
}
@Override
public void onFinish() {
}
};
mCountDownTimer.start();
}
public boolean addCallback(UpdateCallback callback) {
return callbacksList.add(callback);
}
public boolean removeCallback(UpdateCallback callback) {
return callbacksList.remove(callback);
}
}
使用这个类,只需让你的 ModelTimer 实现这个回调接口,并在其中进行 UI 更新。最后,在活动中将按钮 button_start 的 OnClickListener 更改为:
button_start.setOnClickListener(v -> {
TimerHelper t = new TimerHelper();
for (ModelTimer timer : timerList) {
t.addCallback(timer);
}
});
英文:
You can try it with CountDownTimer if you want. It would look like this:
public class TimerHelper {
public interface UpdateCallback {
/**
* @return true if want to be updated again false otherwise
*/
boolean secondPassedCallback();
}
private ArrayList<UpdateCallback> callbacksList = new ArrayList<>();
/**
* @param maxTime maximum of all timers
*/
public void start(int maxTime) {
CountDownTimer mCountDownTimer = new CountDownTimer(maxTime, 1000) {
@Override
public void onTick(long millisUntilFinished) {
ArrayList<UpdateCallback> itemsToRemove=new ArrayList<>();
for (UpdateCallback updateCallback : callbacksList) {
boolean wantToBeUpdated = updateCallback.secondPassedCallback(); // remove timers that reached 0
if (!wantToBeUpdated) {
itemsToRemove.add(updateCallback);
}
}
callbacksList.removeAll(itemsToRemove);
}
@Override
public void onFinish() {
}
};
mCountDownTimer.start();
}
public boolean addCallback(UpdateCallback callback) {
return callbacksList.add(callback);
}
public boolean removeCallback(UpdateCallback callback) {
return callbacksList.remove(callback);
}
}
With this just make your ModelTimer implement this callback and do the UI update there. Finally in activity change button_start OnClickListener to:
button_start.setOnClickListener(v -> {
TimerHelper t = new TimerHelper();
for (ModelTimer timer : timerList) {
t.addCallback(timer);
}
});
答案2
得分: 0
在您的ReyclerView ViewHolder
中进行更改,而不是在ModelTimer
内部:
chronometer.setBase(new Date().getTime());
chronometer.setOnChronometerTickListener(new OnChronometerTickListener() {
public void onChronometerTick(Chronometer cArg) {
long t = SystemClock.elapsedRealtime() - cArg.getBase();
cArg.setText(DateFormat.format("HH:mm:ss", t));
}
});
chronometer.start();
英文:
You can in your ReyclerView ViewHolder
instead of inside in ModelTimer
chronometer.setBase(new Date().getTime());
chronometer.setOnChronometerTickListener(new OnChronometerTickListener() {
public void onChronometerTick(Chronometer cArg) {
long t = SystemClock.elapsedRealtime() - cArg.getBase();
cArg.setText(DateFormat.format("HH:mm:ss", t));
}
});
chronometer.start();
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论