我构建的是具有多个计时器样式的计时器,使用微调器,因此用户可以在他们想要使用的计时器之间快速切换。
我的问题在于switch语句 - 每个定时器本身都能正常工作,但是当从基本定时器通过微调器进入倒数计时器时,第一个定时器(基本定时器)继续与新选择一起运行计时器。在这个例子中,bug显示为1秒,1秒,1秒,1秒,等等......
我的问题是:是否有一个命令可用于“杀死”我不再想继续运行的功能?如果没有,是否有更好的方法可以组织代码,以便switch语句将每个计时器进程分开?我尝试了一个没有运气的布尔检查,并且打破了计时器特定于运行的特定代码到目前为止对我来说没有用。
请参阅以下相关代码:
package com.mtag.app.muaythaiathletesguide;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import java.util.Locale;
public class TimerActivity extends Activity implements AdapterView.OnItemSelectedListener {
private int seconds = 0; // Number of seconds passed
private boolean running; // Check whether timer is running
private boolean wasRunning;
private int timeCap = 0; // Custom max time, stop timer when reached and reset here for countdown
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timer);
// Timer Selection Spinner
Spinner spinner = (Spinner) findViewById(R.id.timer_spinner);
// Create an ArrayAdapter using the string array and a default spinner layout
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
R.array.timer_spinner, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
// Apply the adapter to the spinner
spinner.setAdapter(adapter);
// Spinner click listener
spinner.setOnItemSelectedListener(this);
// Restore activity's state by getting values from Bundle
if (savedInstanceState != null && running) {
seconds = savedInstanceState.getInt("seconds");
running = savedInstanceState.getBoolean("running");
wasRunning = savedInstanceState.getBoolean("wasRunning");
}
}
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id){
String selection = parent.getItemAtPosition(pos).toString();
// TODO: Remove Toast outputs after testing
// Call Timer types when corresponding position is chosen
switch(pos) {
case 0: // Basic Stopwatch: Count from 0:00:00 to 99:59:59 (or cap)
onDestroy();
running = false; // Stop clock
seconds = 0; // Reset seconds to zero
timeCap = seconds; // Set time cap to match seconds on the clock, for reset point
runBasicTimer();
break;
case 1: // Countdown: Count from 99:59:59 (or cap) to 0:00:00
Toast.makeText(parent.getContext(), "Selected: " + selection, Toast.LENGTH_LONG).show();
onDestroy();
running = false;
seconds = 1200; // Default cap 20:00:00
timeCap = seconds;
runCountdownTimer();
break;
case 2: // Tabata: Beep every 20th and 30th second. Reset to 0:00:00 on each 30th second
Toast.makeText(parent.getContext(), "Selected: " + selection, Toast.LENGTH_LONG).show();
running = false;
seconds = 0;
runTabataTimer();
break;
case 3: // Fight Gone Bad: 17min cap, beep on each minute
Toast.makeText(parent.getContext(), "Selected: " + selection, Toast.LENGTH_LONG).show();
running = false;
seconds = 0;
runFGBTimer();
break;
case 4: // "3 On 1 Off": Beep every 3rd and 4th minute
Toast.makeText(parent.getContext(), "Selected: " + selection, Toast.LENGTH_LONG).show();
running = false;
seconds = 0;
runThreeOneTimer();
break;
case 5: // "5 On 1 Off": Beep every 5th and 6th minute
Toast.makeText(parent.getContext(), "Selected: " + selection, Toast.LENGTH_LONG).show();
running = false;
seconds = 0;
runFiveOneTimer();
break;
default:
running = false;
seconds = 0;
Toast.makeText(parent.getContext(), "Error", Toast.LENGTH_LONG).show();
break;
}
}
public void onNothingSelected(AdapterView<?> parent){
// Another interface callback
}
@Override
// Save the state of variables
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putInt("seconds", seconds);
savedInstanceState.putBoolean("running", running);
savedInstanceState.putBoolean("wasRunning", wasRunning);
}
@Override
protected void onResume() {
super.onResume();
// If the stopwatch was running at stop, set it running again
if (wasRunning)
running = true;
}
@Override
protected void onPause() {
super.onPause();
// Record state of stopwatch, running or not running
wasRunning = running;
running = false;
}
public void onClickStart(View view) {
running = true; // Start stopwatch
}
public void onClickStop(View view) {
running = false; // Stop stopwatch
}
public void onClickReset(View view) {
seconds = timeCap; // Reset seconds to zero
}
private void runBasicTimer() {
final TextView timeView = (TextView)findViewById(R.id.time_view);
final Handler handler = new Handler();
handler.post(new Runnable() {
@Override
public void run() {
int hours = seconds / 3600;
int minutes = (seconds % 3600) / 60;
int secs = seconds % 60;
// Format time to hours, minutes, and seconds
String time = String.format(Locale.getDefault(), "%d:%02d:%02d", hours, minutes, secs);
timeView.setText(time);
if (running) {
seconds++;
}
// Don't allow timer to go over 99:59:59
if (seconds >= 359999) {
running = false;
Toast.makeText(getApplicationContext(), "Maximum time reached", Toast.LENGTH_LONG).show();
}
// Post code again with delay of one second
handler.postDelayed(this, 1000);
}
});
}
private void runCountdownTimer() {
final TextView timeView = (TextView)findViewById(R.id.time_view);
final Handler handler = new Handler();
handler.post(new Runnable() {
@Override
public void run() {
int hours = seconds / 3600;
int minutes = (seconds % 3600) / 60;
int secs = seconds % 60;
// Format time to hours, minutes, and seconds
String time = String.format(Locale.getDefault(), "%d:%02d:%02d", hours, minutes, secs);
timeView.setText(time);
if (running) {
seconds--;
}
// Don't allow timer to go under 0:00:00
if (seconds <= 1) {
running = false;
Toast.makeText(getApplicationContext(), "Maximum time reached", Toast.LENGTH_LONG).show();
}
// Post code again with delay of one second
handler.postDelayed(this, 1000);
}
});
}
private void runTabataTimer() {
}
private void runFGBTimer() {
}
private void runThreeOneTimer() {
}
private void runFiveOneTimer() {
}
}
这是.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<GridLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
android:background="@color/colorWhite"
tools:context="com.mtag.app.muaythaiathletesguide.TimerActivity">
<TextView
android:id="@+id/timer_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@string/timer_style"/>
<Spinner
android:id="@+id/timer_spinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="20dp"
android:background="@color/colorLightGrey"
android:minHeight="40dp" />
<TextView
android:id="@+id/time_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textAppearance="@android:style/TextAppearance.Large"
android:textSize="90sp" />
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="horizontal"
android:padding="16dp">
<Button
android:id="@+id/start_button"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:background="@color/colorTimerGreen"
android:onClick="onClickStart"
android:text="@string/start" />
<Button
android:id="@+id/stop_button"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginTop="8dp"
android:background="@color/colorTimerRed"
android:onClick="onClickStop"
android:text="@string/stop" />
<Button
android:id="@+id/reset_button"
android:layout_width="90dp"
android:layout_height="50dp"
android:layout_marginTop="8dp"
android:onClick="onClickReset"
android:text="@string/reset" />
</LinearLayout>
</GridLayout>
答案 0 :(得分:0)
您的计时器/倒计时正在另一个线程上运行。这本身没问题,但附带了你错过的条款。例如,您在选择新项目时尝试设置running
和seconds
。这两个变量存在于UI线程的上下文中。当它们在Runnable
中使用时,它们被简单地放入,复制为最终变量并由新线程使用。当您在匿名类中引用变量时会发生这种情况。这会导致Runnable
看不到您正在更改的变量,因为它们不再相同。
现在该如何解决这个问题?
您应该拥有对它的引用,而不是传递匿名Runnable
。现在,你正在创建一个计时器,只是让它去。这让你很难控制它。
Runnable activeTimer;
private void runBasicTimer() {
//... first remove any other activeTimer if exists and make sure it is cancelled properly (see below)
activeTimer = new Runnable() {//Your code}
handler.post(activeTimer);
}
这样您就可以随时从activeTimer
like this中删除Handler
。
这还不完整。因为你也经常重新发布runnable。我们也想取消它,以便它不会重新发布到处理程序。为此,您应该扩展Runnable
类,以便更改其中的running
。
public class TimerRunnable extends Runnable {
public boolean mRunning = true;
@Override
public void run() {
//your code
if (mRunning) {
handler.postDelayed(this, 1000);
}
}
}
现在,当您想要创建一个新计时器时:
activeTimer = new TimerRunnable(); //instead of just new Runnable()
完美!现在您可以轻松取消它,因为您可以访问其线程,并且您可以访问线程的mRunning
变量。在onItemSelected
而不是仅仅更改running
,您可以执行此操作:
if (activeTimer != null) {
activeTimer.mRunning = false;
}