I am working on an Android calculator app not using any of the built in timer based classes in Android, but instead using Handlers and Threads to update the UI. I'm not sure if there is a problem with my logic or not, but for whatever reason when I set a time and hit the Start button, nothing happens on the screen at all. The targeted TextView does not decrease as it should. Again, I may have made a simple errors (or a few), but I am posting my java and xml files for you all to look at. Thanks in advance for any responses.
TimerActivity.java
package com.example.stins.intentsandtimer;
import android.content.DialogInterface;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.NumberPicker;
import android.widget.TextView;
import android.os.Handler;
import android.os.Message;
import android.content.Context;
import android.os.Vibrator;
public class TimerActivity extends AppCompatActivity implements View.OnClickListener {
TextView hours, minutes, seconds;
Button numberPicker;
private int hrs, min, sec;
private boolean start;
Handler timerHandler = new Handler(){
/**
* Handler for the timer class. It receives the onStart runnable to allow the textviews
* to be updated. It checks to see if all textviews are empty and only updates them if
* they follow the conditions of a traditional timer. Including moving from 1 hour to 59 minutes.
* The handler also sends the Vibrator function once the timer is complete.
* #param msg
*/
#Override
public void handleMessage(Message msg){
super.handleMessage(msg);
TextView txtSeconds = (TextView) findViewById(R.id.textview_seconds);
TextView txtMinutes = (TextView) findViewById(R.id.textview_minutes);
TextView txtHours = (TextView) findViewById(R.id.textview_hours);
int zeroCheck = Integer.parseInt(txtSeconds.getText().toString());
if (zeroCheck > 0) {
sec -= 1;
txtSeconds.setText(sec + "");
} else if (min > 0 && sec == 0) {
min -= 1;
txtMinutes.setText(min + "");
sec = 59;
txtSeconds.setText(sec + "");
} else if (hrs > 0 && min == 0 && sec == 0) {
hrs -= 1;
txtHours.setText(hrs + "");
min = 59;
txtMinutes.setText(min + "");
sec = 59;
txtSeconds.setText(sec + "");
} else {
Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
v.vibrate(1000);
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timer);
this.setTitle("Timer");
Button btnStart = (Button) findViewById(R.id.start_button);
Button btnStop = (Button) findViewById(R.id.stop_button);
Button btnReset = (Button) findViewById(R.id.reset_button);
hours = (TextView) findViewById(R.id.textview_hours);
numberPicker = (Button) findViewById(R.id.btn_set_hours);
numberPicker.setOnClickListener(this);
minutes = (TextView) findViewById(R.id.textview_minutes);
numberPicker = (Button) findViewById(R.id.btn_set_minutes);
numberPicker.setOnClickListener(this);
seconds = (TextView) findViewById(R.id.textview_seconds);
numberPicker = (Button) findViewById(R.id.btn_set_seconds);
numberPicker.setOnClickListener(this);
btnReset.setOnClickListener(new Button.OnClickListener() {
public void onClick(View view) {
TextView txtSeconds = (TextView) findViewById(R.id.textview_seconds);
TextView txtMinutes = (TextView) findViewById(R.id.textview_minutes);
TextView txtHours = (TextView) findViewById(R.id.textview_hours);
sec = 0;
min = 0;
hrs = 0;
txtSeconds.setText(sec+"");
txtMinutes.setText(min+"");
txtHours.setText(hrs+"");
}
}
);
btnStart.setOnClickListener(new Button.OnClickListener() {
public void onClick(View view) {
start = true;
onStart();
}
}
);
btnStop.setOnClickListener(new Button.OnClickListener() {
public void onClick(View view) {
start = false;
}
}
);
}
protected void onStart(){
super.onStart();
final Thread myThread = new Thread(new Runnable(){
#Override
public void run() {
while (sec > 0 || min > 0 || hrs > 0) {
if(start) {
try {
Thread.sleep(1000);
timerHandler.sendMessage(timerHandler.obtainMessage());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else{
}
}
}
});
myThread.start();
}
public void onClick (View v){
switch (v.getId()) {
case R.id.btn_set_hours:
hourPickerDialog();
break;
case R.id.btn_set_minutes:
minutePickerDialog();
break;
case R.id.btn_set_seconds:
secondPickerDialog();
break;
default:
break;
}
}
private void hourPickerDialog(){
NumberPicker myNumberPicker = new NumberPicker(this);
myNumberPicker.setMaxValue(99);
myNumberPicker.setMinValue(0);
NumberPicker.OnValueChangeListener myValChangedListener = new NumberPicker.OnValueChangeListener() {
#Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
hours.setText(""+newVal);
}
};
myNumberPicker.setOnValueChangedListener(myValChangedListener);
AlertDialog.Builder builder = new AlertDialog.Builder(this).setView(myNumberPicker);
builder.setTitle("Set Hours");
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.show();
}
private void minutePickerDialog(){
NumberPicker myNumberPicker = new NumberPicker(this);
myNumberPicker.setMaxValue(59);
myNumberPicker.setMinValue(0);
NumberPicker.OnValueChangeListener myValChangedListener = new NumberPicker.OnValueChangeListener() {
#Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
minutes.setText(""+newVal);
}
};
myNumberPicker.setOnValueChangedListener(myValChangedListener);
AlertDialog.Builder builder = new AlertDialog.Builder(this).setView(myNumberPicker);
builder.setTitle("Set Minutes");
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.show();
}
private void secondPickerDialog(){
NumberPicker myNumberPicker = new NumberPicker(this);
myNumberPicker.setMaxValue(59);
myNumberPicker.setMinValue(0);
NumberPicker.OnValueChangeListener myValChangedListener = new NumberPicker.OnValueChangeListener() {
#Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
seconds.setText(""+newVal);
}
};
myNumberPicker.setOnValueChangedListener(myValChangedListener);
AlertDialog.Builder builder = new AlertDialog.Builder(this).setView(myNumberPicker);
builder.setTitle("Set Seconds");
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.show();
}
}
activity_timer.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="16dp"
android:gravity="center_horizontal"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="2"
android:orientation="horizontal"
android:gravity="center">
<TextView
android:id="#+id/textview_hours"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="00"
android:textSize="70sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text=":"
android:textSize="70sp"/>
<TextView
android:id="#+id/textview_minutes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="00"
android:textSize="70sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text=":"
android:textSize="70sp"/>
<TextView
android:id="#+id/textview_seconds"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="00"
android:textSize="70sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center">
<Button
android:id="#+id/btn_set_hours"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hours"/>
<Button
android:id="#+id/btn_set_minutes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Minutes"/>
<Button
android:id="#+id/btn_set_seconds"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Seconds"/>
</LinearLayout>
<Button
android:id="#+id/start_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginTop="16dp"
android:background="?selectableItemBackgroundBorderless"
android:text="#string/timer_start"
style="#style/MyButton"
/>
<Button
android:id="#+id/stop_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginTop="16dp"
android:background="?selectableItemBackgroundBorderless"
android:text="#string/timer_stop"
style="#style/MyButton"/>
<Button
android:id="#+id/reset_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="?selectableItemBackgroundBorderless"
android:text="#string/timer_reset"
style="#style/MyButton"/>
</LinearLayout>
There's several things going on in your code. I won't try to address them all but just some to get your code doing about what it should. I've copied & tried your code & it actually changes the display for me. I skipped your time picker dialogs & just set sec=20 to start. If you're not getting any changing display, is the display being set initially from the time pickers?
Anyway, 1st let's talk about debugging. One way to do this is to put Log statements in your code. Start by putting this at the top of the file
private final static String TAG = "TimerActivity";
Then in the code have things like this:
// put this in the start button click listener
Log.d(TAG, "Start clicked");
// or this in handleMessage
Log.d(TAG, "handleMessage(), seconds = " + sec);
Having these Log message can help you know what your program has done & what it hasn't, plus show you some variable values. You could also use the debugger which I won't get into now.
Now for your code. onStart() is a lifecycle method. You should not call it yourself. Rename your method (maybe something like onStartButton()). As you have it now, you have 2 instances of your thread running and your counter goes down twice in each second.
In handleMessage(), you have variables (hrs, min, sec) that you use to track the time but you also have zeroCheck that you read from the text on the display. The better thing to do would be use the variables you're already keeping anyway (if(sec > 0) { sec -= 1;...). I didn't verify your logic in the rest of these conditions. Once the display is updating, I'll leave that for you.
Lastly, txtSeconds.setText(sec + ""); is not a good way to use setText() (it's probably OK for Log messages but it's better to get accustomed to using text in other ways). There is more than 1 good way to display text but for this instance, you need special formatting. That is you want your display to show a leading 0 for each number "00:09:07" not "0:9:7". You can get that with
txtSeconds.setText(String.format("%02d", sec));
This way always gives a 2 digit display, from 0 to 59. Other useful formatters are "%08x" for 32 bit hexadecimal or "%.2f" which limits display to 2 places past the decimal place like for showing dollars and cents.
So, none of these will fix the problem in your post but they will get your final code closer to what it needs to be. As I said, your code updates the display as it is for me (not using the time pickers). You can start by setting sec to a fixed number then hit the "Start" button to see what happens. If there are problems in your time pickers, you can use Log messages to track down the bugs & fix them.
EDIT:
So what's happening with your timer not starting is that, while you change the display in your number picker, you don't set the underlying variables (sec etc.) Define some variables to use as temp storage (temp_sec etc.) then set this in onValueChange(),
temp_sec = newVal;
Now in your positiveButton onClick(), you'll have
sec = temp_sec;
Related
I'm trying to make an app that reminds you to charge your phone if it is not used for a while. It works like this: you enter how long the phone should be idle before it reminds you. Then it starts a timer and reminds you when it finishes.
Here's my MainActivity.Java:
public class MainActivity extends AppCompatActivity {
//Defining UI elements
public Button changeAppStateButton;
public TextView minsEditText;
//App variables
boolean isAppRunning = false;
public int secondsPhoneIsAsleep;
public int timerDuration = secondsPhoneIsAsleep * 1000; //multiplying seconds by 100 to get milliseconds
public int tickDuration = 60000; //multiplying seconds (1) by 100 to get milliseconds
//Called when button is pressed
public void changeAppState(View view) {
Button changeAppStateButton = (Button) view;
if (isAppRunning) { //If the app is running, stop app
isAppRunning = false;
changeAppStateButton.setBackgroundColor(getColor(R.color.colorPurple));
changeAppStateButton.setText("Start Reminder");
timer.cancel();
Log.i("TIMER", "Timer interrupted");
} else { //If the app is not running, start app
secondsPhoneIsAsleep = Integer.parseInt(minsEditText.getText().toString()) * 60;
isAppRunning = true;
changeAppStateButton.setBackgroundColor(getColor(R.color.colorRed));
changeAppStateButton.setText("Stop Reminder");
timer.start();
Log.i("TIMER", "Timer started");
}
}
public CountDownTimer timer = new CountDownTimer(timerDuration, tickDuration) {
#Override
public void onTick(long millisUntilFinished) {
Log.i("TIMER", "tick");
}
#Override
public void onFinish() {
isAppRunning = false;
changeAppStateButton.setBackgroundColor(getColor(R.color.colorPurple));
changeAppStateButton.setText("Start Reminder");
Log.i("TIMER", "Timer finished");
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Setting values for UI elements
changeAppStateButton = findViewById(R.id.changeAppStateButton);
minsEditText = findViewById(R.id.minEditText);
}
This is part of my XML:
<EditText
android:id="#+id/minEditText"
android:layout_width="59dp"
android:layout_height="42dp"
android:layout_marginTop="14dp"
android:ems="10"
android:foregroundTint="#FF0000"
android:inputType="number"
android:text="30"
android:textAlignment="center"
android:textColor="#000000"
android:textSize="20sp"
app:layout_constraintStart_toEndOf="#+id/whenUntouched"
app:layout_constraintTop_toBottomOf="#+id/numberEditText" />
<Button
android:id="#+id/changeAppStateButton"
android:layout_width="0dp"
android:layout_height="45dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="110dp"
android:background="#9C27B0"
android:fontFamily="#font/alegreya_sans_sc"
android:onClick="changeAppState"
android:text="Start Reminder"
android:textAlignment="center"
android:textColor="#FFFFFF"
android:textSize="25sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent" />
This is logcat when I press the button:
I/DEV_TIMER: Timer finished
I/DEV_TIMER: Timer started
Why does the timer seem to be going backward? Why isn't it logging a message every "tick"?
Some explanation would be highly appreciated. I'm not too experienced with Android and Java.
Since the code specified in MainActivity.java
Doesn't initialize variable secondsPhoneIsAsleep, so the default value will be 0.
So timerDuration will be 0.
So timer is created to count for a duration of 0.
So when the button is clicked, even though you read new value for secondsPhoneIsAsleep, invoking timer.start() will cause it to count only till 0 based on the earlier initialized value.
Hence onFinish() gets called logging Timer finished, then the Timer started gets logged as part of button click code.
Solution
If you create timer instance on button click then it should use the correct value of secondsPhoneIsAsleep and work properly. Like below:
MainActvity.java
public class MainActivity extends AppCompatActivity {
//Defining UI elements
public Button changeAppStateButton;
public TextView minsEditText;
//App variables
boolean isAppRunning = false;
public int secondsPhoneIsAsleep;
public CountDownTimer timer;
public int timerDuration;
public int tickDuration = 1000; //multiplying 1 second by 1000 to get milliseconds
//Called when button is pressed
public void changeAppState(View view) {
Button changeAppStateButton = (Button) view;
if (isAppRunning) { //If the app is running, stop app
isAppRunning = false;
changeAppStateButton.setBackgroundColor(getColor(R.color.colorPurple));
changeAppStateButton.setText("Start Reminder");
timer.cancel();
Log.i("TIMER", "Timer interrupted");
} else { //If the app is not running, start app
secondsPhoneIsAsleep = Integer.parseInt(minsEditText.getText().toString()) * 60;
timerDuration = secondsPhoneIsAsleep * 1000;
timer = getNewTimer(); // Creates a new timer.
isAppRunning = true;
changeAppStateButton.setBackgroundColor(getColor(R.color.colorRed));
changeAppStateButton.setText("Stop Reminder");
timer.start();
Log.i("TIMER", "Timer started");
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Setting values for UI elements
changeAppStateButton = findViewById(R.id.changeAppStateButton);
minsEditText = findViewById(R.id.minEditText);
}
private CountdownTimer getNewTimer() {
return new CountDownTimer(timerDuration, tickDuration) {
#Override
public void onTick(long millisUntilFinished) {
Log.i("TIMER", "tick");
}
#Override
public void onFinish() {
isAppRunning = false;
changeAppStateButton.setBackgroundColor(getColor(R.color.colorPurple));
changeAppStateButton.setText("Start Reminder");
Log.i("TIMER", "Timer finished");
}
};
}
I am creating a simple countdown timer in android studio which has a start and a pause button. The timer countdown starts but does not pause instead it starts increasing and decreasing by clicking pause button. Here is my code.
<TextView
android:id="#+id/txtview1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Counter"
android:textSize="75sp"
android:textColor="#ff0000"
/>
<Button
android:id="#+id/push_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start Count Down"
android:onClick="perform_action"
android:textSize="35sp"
/>
<Button
android:id="#+id/cancel_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Pause Count Down"
android:onClick="perform_action"
android:textSize="35sp"
/>
Java code
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void perform_action(View view)
{
long totalMilliseconds = 90000;
long interval = 1;
Button b1 = (Button)findViewById(R.id.push_button);
String push = b1.getText().toString();
CountDownTimer timer;
timer = new CountDownTimer(40000, 1000) {
TextView t1 = (TextView)findViewById(R.id.txtview1);
#Override
public void onTick(long l) {
t1.setText(""+(l));
}
#Override
public void onFinish() {
t1.setText("Finish");
}
};
if(push.equals("Start Count Down")){
timer.start();
}
else if(push.equals("Pause Count Down")){
timer.cancel();
}
}
}
Please provide me some help
It looks like you're spawning a new timer every time you click the button, from the snippet you posted. You can just use an OnClickListener.
You can access it like so (example uses a boolean to switch states);
Boolean ButtonClicked = false;
b1.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
//Perform your click logic to start and stop the timer
if (!ButtonClicked)) {
ButtonClicked = true;
timer.start();
} else {
ButtonClicked = false;
timer.cancel();
}
}
});
This question already has answers here:
Unfortunately MyApp has stopped. How can I solve this?
(23 answers)
Closed 5 years ago.
Unfortunately, program has stopped
I was trying to calculate age in android studio. It doesn't show any error message but when I click on the "Button" , the app crashes and shows "Unfortunately, Test1 has stopped."
Why my program is crashing even though it does not show any error message during compiling?
The java code is like below
package android.example.com.test1;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import java.util.Calendar;
import java.util.GregorianCalendar;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void hi() {
Calendar now = new GregorianCalendar(2016, 3, 1);
Calendar cal = new GregorianCalendar(2016, 2, 28);
int res = now.get(Calendar.YEAR) - cal.get(Calendar.YEAR);
if ((cal.get(Calendar.MONTH) > now.get(Calendar.MONTH))
|| (cal.get(Calendar.MONTH) == now.get(Calendar.MONTH) && cal.get(Calendar.DAY_OF_MONTH) > now
.get(Calendar.DAY_OF_MONTH))) {
res--;
}
TextView s = (TextView) findViewById(R.id.message);
s.setText(" " + res);
}
}
The xml code is like following:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="android.example.com.test1.MainActivity">
<TextView
android:id="#+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="#+id/message"
android:onClick="hi"
android:text="Button" />
</RelativeLayout>
You have a wrong signature to setup click listener, it should be
public void hi(View v){//...code}
// ^^^^^^
From Docs
The method you declare in the android:onClick attribute must have a signature exactly as shown above. Specifically, the method must:
Be public
Return void
Define a View as its only parameter (this will be the View that was clicked)
This is because the onClick method must use a method that receives a view as a parameter.
public void hi(View view) {
Calendar now = new GregorianCalendar(2016, 3, 1);
Calendar cal = new GregorianCalendar(2016, 2, 28);
int res = now.get(Calendar.YEAR) - cal.get(Calendar.YEAR);
if ((cal.get(Calendar.MONTH) > now.get(Calendar.MONTH))
|| (cal.get(Calendar.MONTH) == now.get(Calendar.MONTH) && cal.get(Calendar.DAY_OF_MONTH) > now
.get(Calendar.DAY_OF_MONTH))) {
res--;
}
TextView s = (TextView) findViewById(R.id.message);
s.setText(" " + res);
}
Another way is to cast the button and overwrite the onClick method to call the hi () method. For this, you need to delete the onClick attribute of the XML.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
hi();
}
});
}
public void hi() {
Calendar now = new GregorianCalendar(2016, 3, 1);
Calendar cal = new GregorianCalendar(2016, 2, 28);
int res = now.get(Calendar.YEAR) - cal.get(Calendar.YEAR);
if ((cal.get(Calendar.MONTH) > now.get(Calendar.MONTH))
|| (cal.get(Calendar.MONTH) == now.get(Calendar.MONTH) && cal.get(Calendar.DAY_OF_MONTH) > now
.get(Calendar.DAY_OF_MONTH))) {
res--;
}
TextView s = (TextView) findViewById(R.id.message);
s.setText(" " + res);
}
I have onTouchListeners for the (custom) title area and for the message area of my AlertDialog.
I'm trying to have my dialog set up in such a way that the user can mindlessly tap in the top right 1/4th of the AlertDialog to toggle the whether background music plays or not since just having the speaker as the tappable area would result in too small of a hit box/area.
My problem is: the area in between the message and title marked in red isn't handling ontouchlistener events
Most people would suggest creating a custom dialog, but the thing is I really like the way this dialog looks (it has a very stock material design aesthetic) and already jumped through a lot of hoops to get it to look exactly the way I like (drawing leaderboard over an invisible neutral button, custom title area). I don't want to make a custom dialog unless I can make it look absolutely identical to what I have now (so hard to mimic the look of stock material dialogs, trust me i've tried and did a lot of research/wasted a lot of time trying that).
I'm assuming the on touch events for the custom title area and message area don't encompass or account for the margins or padding in between.
Pardon the disgusting code!! I'm just trying to hack everything together and tidy it up later.
Thanks in advance!
The linear layout for my custom title area of the alertdialog
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="0px"
android:paddingTop="0px"
android:paddingLeft="0px"
android:paddingRight="0px"
android:paddingBottom="0px"
android:orientation="horizontal"
android:id="#+id/st"
>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="24dp"
android:paddingTop="20dp"
android:paddingRight="5dp"
android:src="#drawable/ic_pause"/>
<TextView
android:id="#+id/leaderboard"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#800080"
android:textSize="22sp"
android:textAppearance="#android:style/TextAppearance.DeviceDefault.DialogWindowTitle"
android:layout_gravity="center"
android:paddingTop="18dp"
android:layout_weight="1"
android:text="Paused"
/>
<ImageView
android:id="#+id/soundtoggle"
style="?android:attr/panelTextAppearance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="24dp"
android:paddingTop="20dp"
android:paddingRight="?android:dialogPreferredPadding"
android:gravity="right"/>
</LinearLayout>
My android code
AlertDialog.Builder ad = new AlertDialog.Builder(new ContextThemeWrapper(AndroidLauncher.this, android.R.style.Theme_Material_Light_Dialog))
.setMessage(msg)
.setCustomTitle(myLayout)
.setCancelable(false)
.setNegativeButton("End Game", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
pauseInterface.end();
dialog.cancel();
}
})
.setNeutralButton(" ", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
//startSignInIntent();
showLeaderboard();
dialog.cancel();
}
})
.setPositiveButton("Resume", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
pauseInterface.resume();
dialog.cancel();
}
});
alertDialog = ad.create();
int titleId = getResources().getIdentifier("alertTitle", "id", "android");
if (titleId > 0) {
TextView dialogTitle = (TextView) alertDialog.findViewById(titleId);
}
alertDialog.setOnShowListener(new DialogInterface.OnShowListener() {
#Override
public void onShow(DialogInterface dialogInterface) {
Button button = alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL);
Drawable drawable = getResources().getDrawable(R.drawable.ic_leaderboard);
drawable.setBounds((int) 0,
0, (int) (drawable.getIntrinsicWidth() * 1),
drawable.getIntrinsicHeight());
button.setCompoundDrawables(drawable, null, null, null);
}
});
alertDialog.show();
final ImageView soundToggle = (ImageView) alertDialog.findViewById(R.id.soundtoggle);
if (tetrisgame.getMusicState()) {
if (tetrisgame.getMusicState()) {
soundToggle.setImageDrawable(getResources().getDrawable(R.drawable.music_on, getApplicationContext().getTheme()));
} else {
soundToggle.setImageDrawable(getResources().getDrawable(R.drawable.music_off, getApplicationContext().getTheme()));
}
TextView tv = (TextView) alertDialog.findViewById(android.R.id.message);
tv.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent m) {
if (m.getAction() == MotionEvent.ACTION_DOWN && (m.getX() >= (v.getRight() - (v.getWidth() / 4)))) {
Log.println(Log.ERROR, "Ken", "Popular Pothead");
if (tetrisgame.toggleMusic()) {
String uri = "#drawable/music_on"; // where myresource (without the extension) is the file
int imageResource = getResources().getIdentifier(uri, null, getPackageName());
Drawable res = getResources().getDrawable(imageResource);
soundToggle.setImageDrawable(res);
// soundToggle.setImageDrawable(getResources().getDrawable(R.drawable.music_off, alertDialog.findViewById(android.R.id.message).getTheme()));
} else {
soundToggle.setImageDrawable(getResources().getDrawable(R.drawable.music_off, null));
}
}
return true;
}
});
final LinearLayout as = (LinearLayout) alertDialog.findViewById(R.id.st);
as.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent m) {
if (m.getAction() == MotionEvent.ACTION_DOWN) {
Log.println(Log.ERROR, "Ken", "Tweed");
if (tetrisgame.toggleMusic()) {
String uri = "#drawable/music_on"; // where myresource (without the extension) is the file
int imageResource = getResources().getIdentifier(uri, null, getPackageName());
Drawable res = getResources().getDrawable(imageResource);
soundToggle.setImageDrawable(res);
// soundToggle.setImageDrawable(getResources().getDrawable(R.drawable.music_off, alertDialog.findViewById(android.R.id.message).getTheme()));
} else {
soundToggle.setImageDrawable(getResources().getDrawable(R.drawable.music_off, null));
}
}
return true;
}
});
I'm a beginner in android and I've written an activity. It contains a CountDownTimer that counts down from a particular value. It also contains a Button that loads text information and a textview to display count.
Below is the code for Activity1:
public class Screen extends Activity1 implements OnClickListener {
private static final int MILLIS_PER_SECOND = 1000;
private static final int SECONDS_TO_COUNTDOWN = 1;
TextView Time;
int totaltime;
Button startTimer, howTo, pause;
protected CountDownTimer MyTimer;
int PracticeCount;
long tot;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.pushupscreen);
getRefs();
getSpeed();
getCount();
setTotalTime();
startTimer.setOnClickListener(this);
pause.setOnClickListener(this);
}
private void getRefs() {
// Initialize layout resources
Time = (TextView) findViewById(R.id.tvTime);
startTimer = (Button) findViewById(R.id.bStart);
howTo = (Button) findViewById(R.id.btHowTo);
pause = (Button) findViewById(R.id.bPause);
howTo.setOnClickListener(this);
}
private void getTheCount() {
//get count from SharedPreferences
}
private void getSpeed() {
//get speed from SharedPreferences
}
private void setCount(){
totalTime=speed*count;}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (v == startTimer) {
try {
showTimer(time);
} catch (NumberFormatException e) {
// method ignores invalid (non-integer) input and waits
// for something it cant use
}
} else if (v == pause) {
MyTimer.cancel();
Timer.setText("Resume");
} else if (v == howTo) {
//Launch screen containing information
}
}
private void showTimer(long time) {
if (MyTimer != null) {
MyTimer.cancel();
}
MyTimer = new CountDownTimer(tot2, MILLIS_PER_SECOND) {
#Override
public void onTick(long millisUntilFinished) {
tot = millisUntilFinished;
long seconds = millisUntilFinished / 1000;
Time.setText(String.format("%02d", seconds / 60) + ":"
+ String.format("%02d", seconds % 60));
}
#Override
public void onFinish() {
Time.setText("KABOOM!");
}
}.start();
}
}
And here is the layout file for this:
<TextView
android:id="#+id/tvTime"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:gravity="center"
android:padding="10dip"
android:text="#string/starttime"
android:textSize="60sp" />
<Button
android:id="#+id/bStart"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_above="#+id/tvTime"
android:text="Start" />
<Button
android:id="#+id/bPause"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_above="#+id/tvTime"
android:layout_toRightOf="#+id/btHowTo"
android:text="Pause" />
<TextView
android:id="#+id/tvCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/btHowTo"
android:layout_centerHorizontal="true"
android:layout_marginTop="39dp"
android:text="25"
android:textSize="80sp"
android:textAlignment="center"/>
My questions:
1.How do I create 4 activities that use the same layout and the same timer? Each Activity loads a different content in the textview and a different screen on the click of HowTo button.
2.How can Activity1 be designed to run for 1/4th the time set and pass remaining time to Activity2? Is it possible?
I would really appreciate any help and advice that you can provide. Thanks.
A couple things here.
Its very easy to re-use layouts. In each activity's onCreate you would just call:
setContentView(R.layout.pushupscreen); The pushupscreen.xml file can be shared across all activities this way.
What you probably want to do is persist a timestamp to some common data source for all the activities. This could be a write to a SharedPreferences file: Documentation here. Then as each activity resumes, check how much time has already passed by comparing this timestamp to the current timestamp. You could also pass the timestamp as an extra in the intent to start up the subsequent activities. The documentation for that can be found here and here
You could make a custom control, which is basically a new class which inherits some other control's class (for example a LinearLayout or a RelativeLayout). You could then load a view's XML to your new layout or programmatically create new controls inside your control. More info here:
Custom components in Android
After a 1/4 of your countdown period, you can create and send an Intent to start a new activity in the onTick method. You can also put the remaining 3/4 as a millisecond value (of type long) in an intent extra. You can then obtain this value in the new activity and invoke a custom CountDownTimer child there for the rest of your countdown. Then you can finally execute what you wish after the countdown is done in the onFinish() method.