Error updating TextView from TimerTask's run method - java

I'm trying to test UI and Timer class possibilities. So I tried the following exercise:
=== TestTimerActivity.java ===
package com.tvt.TestTimer;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
import java.util.Timer;
import java.util.TimerTask;
public class TestTimerActivity extends Activity {
TextView _tv;
Timer _t;
int _count = 0;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//
// _tv = (TextView) getWindow().getDecorView().findViewById( R.id.TextViewTime );
_tv = (TextView) findViewById( R.id.TextViewTime );
UpdateTime(); // Completes OK
_t = new Timer();
_t.scheduleAtFixedRate( new TimerTask() {
#Override
public void run() {
// TODO Auto-generated method stub
_count++;
UpdateTime(); // Fails
}
}, 1000, 1000 );
}
protected void UpdateTime() {
_tv.setText( "" + _count );
}
}
=== TestTimerActivity.java ===
=== main.xml ===
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:gravity="center_horizontal" android:layout_height="wrap_content" android:layout_gravity="center" android:text="#string/header" android:id="#+id/TestViewHeader" android:textStyle="bold"/>
<TextView android:layout_height="wrap_content" android:id="#+id/TextViewTime" android:layout_width="match_parent" android:layout_gravity="center_horizontal" android:gravity="center_horizontal" android:text="-"></TextView>
</LinearLayout>
=== main.xml ===
The very first UpdateTime call (from onCreate) completes OK but the same call from TimerTask::run() fails giving the message "TestTimer class has stopped unexpectedly".
Any idea? Where is my fault?
--
SY, TVT

Do that UpateTime() operation on the UI thread.
protected void UpdateTime()
{
runOnUiThread(new Runnable()
{
public void run()
{
_tv.setText( "" + _count );
}
});
}
Hopefully resolves your problem.

Updating the UI from a thread other than the UI/main thread will fail as Android does not allow it. Try using a Handler to post messages back to the UI thread.
Or maybe try using AsyncTask which presents a set of really simple callback methods to handle the worker and UI threads. But that might not be exactly what you need since you're trying to test out the TimerTask class.

Related

Runnable code not stopping for Android

I've already checked this SO post but to no avail: removeCallbacks not stopping runnable
What my app does: I have 2 imageViews. When app runs, one imageView should change it's image from sleep image to wake up image and then back to sleep image. Then handler used for delay before doing same thing for 2nd imageView. And then I want Runnable to stop. I'm using Android's frame animation.
Please help!
public class MainActivity extends Activity {
AnimationDrawable[] animDrawList;
ImageView im;
ImageView im2;
private int curPos;
private Handler mHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
animDrawList = new AnimationDrawable[2];
curPos = 0;
im = (ImageView)findViewById(R.id.image1);
im2 = (ImageView)findViewById(R.id.image2);
im.setBackgroundResource(R.drawable.animation_to_run);
im2.setBackgroundResource(R.drawable.animation_to_run);
animDrawList[0] = (AnimationDrawable) im.getBackground();
animDrawList[1] = (AnimationDrawable) im2.getBackground();
mHandler = new Handler();
startTask();
}
Runnable myThread = new Runnable() {
volatile boolean stopMe = false;
#Override
public void run() {
if ( curPos <= 1 ) {
animDrawList[curPos].start();
curPos++;
}
if ( curPos > 1 ) {
stopMe = true;
}
if ( stopMe ) {
stopTask();
return;
}
mHandler.postDelayed(myThread, 1000);
}
};
void startTask() { myThread.run(); }
void stopTask() { mHandler.removeCallbacks(myThread); }
}//end of MainActivity class
Here is my layout file that's straighforward :
<?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="org.example.a.MainActivity">
<ImageView
android:id="#+id/image1"
android:layout_width="90px"
android:layout_height="90px"
android:layout_alignParentStart="true"
android:layout_marginStart="14dp"
android:layout_marginTop="13dp" />
<ImageView
android:id="#+id/image2"
android:layout_width="90px"
android:layout_height="90px"
android:layout_marginEnd="49dp"
android:layout_marginBottom="93dp"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true" />
</RelativeLayout>
Here is my xml file.
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="#drawable/restState" android:duration="1000" />
<item android:drawable="#drawable/upState" android:duration="1000" />
<item android:drawable="#drawable/restState" android:duration="1000" />
</animation-list>
The app runs, no crashes, but it doesn't stop. I want the Runnable to stop after animating, why isn't my flag "stopMe" doing it's job?
UPDATE FROM JUN 4, 2017: I removed the animation code inside the run method and and added logcat output insted in run method and Runnable does stop with "stopMe" boolean so it's issue with the frame animation, is it I can't combine Android's frame animation with Runnable? I'll look into it...
you just need to add a condition in the run method
public void run() {
while(!stopMe)
{
if ( curPos <= 1 ) {
animDrawList[curPos].start();
curPos++;
}
if ( curPos > 1 ) {
stopMe = true;
}
if ( stopMe ) {
stopTask();
return;
}
mHandler.postDelayed(myThread, 1000);
}
}
I've solved it! I had to set the AnimationDrawable object's setOneShow(boolean playAnimationOncePerAnimationDrawableObject) to true! Voila!

Creating a progress bar for android

Hi I am new to threading in general and I am trying to set up a progress bar that increments itself every half-second up to 100 using the sleep method. I have been having trouble using a lot of the syntax I have found on line and when I launch this program it does not display a progress bar but instead displays a spinning loading icon. This is what I have:
public class main extends ActionBarActivity {
Handler mHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final Button startButton = (Button) findViewById(R.id.startbutton);
startButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
showDialog(1);
startPb();
}
});
}
private void startPb(){
final ProgressBar pb = (ProgressBar) findViewById(R.id.progressBar);
pb.setIndeterminate(false);
pb.setProgress(0);
new Thread(new Runnable(){
public void run(){
for (int i = 0; i <= 100; i++)
try {
Thread.sleep(500);
pb.setProgress(i);
System.out.println(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
pb.post(new Runnable() {
public void run() {
pb.incrementProgressBy(1);
}
});
}
}).start();
}
I have a feeling I am not implementing the progress bar properly in the above code.
Layout:
<RelativeLayout 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:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin" tools:context=".Counter"
android:id="#+id/counter"
tools:ignore="InvalidId">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="startButton"
android:id="#+id/startbutton"
android:layout_centerHorizontal="true" />
<ProgressBar
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/progressBar"
android:layout_below="#+id/startbutton"
android:layout_centerHorizontal="true" />
</RelativeLayout>
A spinning progress bar is an indeterminate progress bar. To use an actual progress bar you will want to call:
pb.setIndeterminate(false);
The progress bar style was also an issue. Here is a good reference explaining progress bar style types.
What's the meaning of android:progressBarStyle attribute in ProgressBar?

Restore dynamic UI elements of activity in android

I've been for the most part following the highest highest voted answer
Here and a little from some other answers on SO. I'm trying to handle a configuration change or anything that would call onSaveInstanceState() / onRestoreInstanceState().
So far I'm able to get TextViews restored (properly?), but I'm having issues with dynamically added ui elements like ImageButtons, in my example the buttons are not being restored. It's probably something stupid, but I've avoided asking for help long enough. I had seen somewhere mention of saving Objects to bundle.... but this seemed more straight forward.
Grateful for any suggestions.
oh also I've only been testing on emulator, rotating between landscape/portrait
EDIT: updated with a working example and suggestion from #18446744073709551615. Thanks everyone for the help, looks like saving entire objects might be a better approach for more complex code.
basic xml layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello" />
<Button
android:id="#+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="set"
android:text="Button" />
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="add"
android:text="Button" />
</LinearLayout>
This is just a slightly modified blank activity:
package com.example.savestate;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
int btnId = 0;
int myBtns;
LinearLayout panel;
TextView textView;
String myString;
ImageButton[] btnArray;
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "begin of creation, myString is " + myString);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textView1);
Log.d(TAG, "end of creation, myString is: " + myString);
}
#Override
protected void onResume() {
super.onResume();
textView.setText(myString);
restoreBtns();
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putString("MyString", "Welcome back");
savedInstanceState.putInt("MyBtns", btnId);
Log.d(TAG, "on save, myString is: " + myString);
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
myString = savedInstanceState.getString("MyString");
myBtns = savedInstanceState.getInt("MyBtns");
Log.d(TAG, "on restore, myBtns is: " + myBtns);
Log.d(TAG, "on restore, myString is: " + myString);
}
public void restoreBtns() {
if(myBtns > 0) {
panel = (LinearLayout) findViewById(R.id.LinearLayout1);
btnArray = new ImageButton[myBtns];
for (int i = 0; i < myBtns; i++){
btnId++;
btnArray[i] = new ImageButton(this);
btnArray[i].setImageResource(R.drawable.ic_launcher);
btnArray[i].setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
btnArray[i].setId(btnId);
panel.addView(btnArray[i]);
}
}
}
public void add(View view) {
btnId++;
panel = (LinearLayout) findViewById(R.id.LinearLayout1);
ImageButton imgBtn = new ImageButton(this);
imgBtn.setImageResource(R.drawable.ic_launcher);
imgBtn.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
imgBtn.setId(btnId);
panel.addView(imgBtn);
}
public void set(View view) {
textView.setText("Goodbye");
Log.d(TAG, "on set(), myString is " + myString);
}
}
When onCreate() executes, myBtns is 0. Due to the if statement, restoreBtns() is a no-op. Then, onStart() is called, and after that, onRestoreInstanceState() is called: This method is called after onStart(). So myBtns is initialized only after it is used.
Suggestion: move the button stuff to onResume().
Try this way
<activity
android:name="com.yourpackage.YourActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />

Showing circular Progressbar Against Button Click Using Timer Class

i have coded simple calculator with two input boxes and once total button , What i have to is show a circular Progressbar for 3 seconds before i show result after pressing Total button ,here is what i have done .
package com.example.calculator;
import java.util.Timer;
import java.util.TimerTask;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends Activity
{
EditText t1;
EditText t2;
TextView tv;
TextView tv1;
Timer singleTask;
int Interval = 3000;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
t1= (EditText)findViewById(R.id.editText1);
t2= (EditText)findViewById(R.id.editText2);
tv= (TextView)findViewById(R.id.textView1);
tv1= (TextView)findViewById(R.id.textView2);
singleTask= new Timer();
}
public void loadprogressbar()
{
singleTask.schedule(new TimerTask() {
#Override
public void run() {
// have to add code for progress bar but right now just caption text added
tv1.setText("This is for 3 seconds only");
}
}, 1000);
}
#Override
protected void onDestroy(){
super.onDestroy();
if(singleTask != null)
{
singleTask.cancel();
}
}
public void Clicked(View v)
{ int total;
if(v.getId()==R.id.button1)
{
int v1 = Integer.parseInt(t1.getText().toString());
int v2 = Integer.parseInt(t2.getText().toString());
total = v1 + v2;
loadprogressbar();
tv.setText(total+"");
tv.setVisibility(1);
}
else if (v.getId()==R.id.button2)
{
t1.setText("");
t2.setText("");
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
Xml File is
<RelativeLayout 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:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_world"
android:visibility="invisible"/>
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_world"
></TextView>
<EditText
android:id="#+id/editText1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/textView1"
android:layout_below="#+id/textView1"
android:layout_marginTop="32dp"
android:ems="10"
android:inputType="number" >
<requestFocus />
</EditText>
<EditText
android:id="#+id/editText2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/editText1"
android:layout_below="#+id/editText1"
android:layout_marginTop="21dp"
android:ems="10"
android:inputType="number" />
<Button
android:id="#+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/button1"
android:layout_alignBottom="#+id/button1"
android:layout_alignRight="#+id/editText2"
android:text="Clear"
android:onClick="Clicked"
/>
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="#+id/editText2"
android:layout_marginTop="35dp"
android:text="Total"
android:onClick="Clicked"
/>
</RelativeLayout>
Problem is that i am not getting any result against Timer code ? according to my thinking it should show text for 3 seconds and then display text . My logic could be wrong as well please guide me ..
If I understand correctly what you want to do is to show a ProgressDialog to simulate that your calculator is doing some calculation. The Timer class is not what you want to use. Basically, you want to wait for 3 seconds (during that time the ProgressDialog will be visible), and then update the UI by setting the TextView with the result. As you might know, you cannot modify the UI thread from a background Thread directly, and you should not make the UI thread sleep. Hence, AsyncTask is your best bet. Below is a sample code to allow you to show a ProgressDialog for 3 seconds. You should update your TextView on the OnPostExecute method since it runs on the UI thread.
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.RelativeLayout;
import android.widget.Toast;
public class MainActivity extends Activity{
ProgressDialog pd;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pd = new ProgressDialog(this);
pd.setTitle(getString("Title"));
pd.setIndeterminate(true);
pd.show();
new MyAsycnTask().execute();
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
private class MyAsycnTask extends AsyncTask <Void,Void,Void> {
#Override
protected Void doInBackground(Void... voids) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
pd.hide();
}
}
}
Go with E.Odebugg answer.
If you however need to display a TextView for 3 seconds and then hide, here's how to do it without using TimerTask.
textView.postDelayed(new Runnable() {
#Override
public void run() {
textView.setVisibility(View.GONE);
}
}, 3000);
The TextView is displayed by default, and after 3 seconds it's hidden. Also, note that postDelayed() is used, which will run the Runnable on UI thread.
So I think your code is wrong in loadprogressbar(). It should be:
public void loadprogressbar()
{
// have to add code for progress bar but right now just caption text added
tv1.setText("This is for 3 seconds only");
singleTask.schedule(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
///hide the progressbar here...
}
});
}
}, Interval);
}
And if I were you I would change the name of your function "Clicked", because it can be a reserved word (the color also indicates it)...

Changing the position of the thumb of the SeekBar using a different Button

I'm trying to move the position of the seekbar using a button. Basically I have a seekbar from 0 to 100. and I have button presents set up at arbitrary values (40,50,60 etc). When I try to set the progress on the seekbar via button, I get a fault.. I've already initialized the seekBar in the onCreate() method.
SeekBar seekBar = (SeekBar) findViewById(R.id.seekBar1);
currentProgress = 40;
seekBar.setMax(100);
seekBar.setProgress(currentProgress);
button40.setOnClickListener(button40Listener);
But when use the below, it crashes.
private OnClickListener button40Listener = new OnClickListener() {
public void onClick(View v) {
currentProgress = 40;
seekBar.setProgress(currentProgress);
}
}
This seems straight-forward. Any ideas?
You shouldn't need to put up another Seekbar. The initial one should be fine. Without the exception message and stack trace I'm not sure what is causing the crash. However, I just coded an example and works as you would expect. Perhaps by looking at my example you can identify your issue.
SeekbarTest.java:
package com.example.seekbartest;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.SeekBar;
public class SeekbarTest extends Activity {
/** Called when the activity is first created. */
private SeekBar seekBar;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
seekBar = (SeekBar)findViewById(R.id.seekBar1);
Button fortyPctButton = (Button)findViewById(R.id.buttonFortyPct);
fortyPctButton.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
seekBar.setProgress(40);
}
});
Button sixtyPctButton = (Button)findViewById(R.id.buttonSixtyPct);
sixtyPctButton.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
seekBar.setProgress(60);
}
});
Button eightyPctButton = (Button)findViewById(R.id.buttonEightyPct);
eightyPctButton.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
seekBar.setProgress(80);
}
});
}
}
And here is the main.xml it is referencing for the layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:layout_width="fill_parent"
android:text="#string/hello"
android:id="#+id/textView1"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"/>
<SeekBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/seekBar1"
android:layout_below="#+id/textView1"
android:layout_alignLeft="#+id/textView1"
android:layout_alignRight="#+id/textView1"/>
<Button
android:layout_width="wrap_content"
android:text="40%"
android:id="#+id/buttonFortyPct"
android:layout_height="wrap_content"
android:layout_below="#+id/seekBar1"
android:layout_alignLeft="#+id/seekBar1"/>
<Button
android:layout_width="wrap_content"
android:text="60%"
android:id="#+id/buttonSixtyPct"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/buttonFortyPct"
android:layout_alignTop="#+id/buttonFortyPct"
android:layout_alignBottom="#+id/buttonFortyPct"/>
<Button
android:layout_width="wrap_content"
android:text="80%"
android:id="#+id/buttonEightyPct"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/buttonSixtyPct"
android:layout_alignTop="#+id/buttonSixtyPct"
android:layout_alignBottom="#+id/buttonSixtyPct"/>
</RelativeLayout>
Just create a new android app and replace the generated code + layout with the example above. It should work for you.
Good luck,
Craig

Categories

Resources