I have this code:
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(!clicked){
clicked = true;
fab.setImageResource(R.drawable.ic_media_stop);
char[] userInput = et.getText().toString().toLowerCase().toCharArray();
compareToMap(userInput);
}else{
clicked = false;
fab.setImageResource(R.drawable.ic_media_play);
}
}
});
When the floating action button is clicked, I want it to change to a stop symbol and then execute the function afterwards. What is happening is that the code is being executed before the image on the button changes. The image only changes once all of the code has been executed from the compareToMap function even though it is placed before it in the code. Is there a way to make sure that the image changes before the compareToMap function executes?
Note: The compareToMap function contains Thread.sleep methods which is causing the UI to block (I think), but shouldn't the image change before that function is executed?
At this moment the UI didn't load yet. If you want to do some work after the UI is loaded you can use the class Handler.
long postDelay = 100; //The delay (in milliseconds) until the Runnable will be executed
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
//Code that you want to execute after the UI is loaded
compareToMap(userInput);
}
}, postDelay);
Note: You can't use Thread.sleep on the UI main thread. You can learn more here. https://developer.android.com/guide/components/processes-and-threads.html.
You can use AsyncTask to execute the compareToMap(userInput) method, this will run the compareToMap method in a separated thread and the UI will not be blocked; something like this :
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
fab.setImageResource(!clicked ? R.drawable.ic_media_stop : R.drawable.ic_media_play);
if (!clicked) {
// Cancel the executing AsyncTask if there's one already running
if (mAsyncTask != null) {
mAsyncTask.cancel(true);
}
mAsyncTask = new AsyncTask<String, Void, Void>() {
#Override
protected Void doInBackground(String... params) {
compareToMap(userInput);
return null;
}
};
mAsyncTask.execute();
}
clicked != clicked;
}
});
More info about AsyncTasks here or you can check this tutorial.
Related
I get some error. I really couldn't solve it today :( I get error after set ID data to lblID in FillData() method. It sets ID data properly but lblTitle and lblPrice always returns error like "Only the original thread that created a view hierarchy can touch its views" and program stops running.
Note : This is not my original code. I just minimized it to be more understandable and of course it gives same error like below code. Anyway in FillData() method i get data from wcf service and it returns data properly. i tried runonuithread but it didn't make any sense. Also if i write the code outside of the thread it doesn't fill the controls. Because it's originally gets the data from wcf service.
public class MainActivity extends AppCompatActivity {
LinearLayout lytData;
TextView lblTitle, lblID, lblPrice;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lytData = (TextView)findViewById(R.id.lytNewData);
lblID = (TextView)findViewById(R.id.lblID);
lblTitle = (TextView)findViewById(R.id.lblTitle);
lblPrice = (TextView)findViewById(R.id.lblPrice);
new Thread() {
public void run() {
FillData();
}
}.start();
lytData.setOnTouchListener(new OnCustomTouchListener (context) {
#Override
public void ToLeft() {
new Thread() {
public void run() {
FillData();
}
}.start();
}
#Override
public void ToRight() {
new Thread() {
public void run() {
FillData();
}
}.start();
}
});
}
void FillData() {
lblID.setText("aaa");
lblTitle.setText("aaa");
lblPrice.setText("aaa");
}
The problem is you're trying to update the UI in another thread, but the UI can only be updated in the UI thread. If you're simply updated the UI as your code is showing then you should remove the calls from FillData from the secondary thread, use a secondary thread if you're doing heavy loading inside FillData() otherwise you're better off updating the UI directly in the UI thread:
So instead of doing this:
new Thread() {
public void run() {
FillData();
pd.cancel();
}
}.start();
Just simply call FillData(); outside the new thread.
You can also call runOnUiThread to bring the update to the ui thread:
getActivity().runOnUiThread(new Runnable() {
public void run() {
FillData();
}
});
If your code inside FillData is mixed with heavy load code, then you can bring the runOnUiThread method to inside the FillData and move only the UI update code to runOnUiThread.
If you still want to keep your code the way it is you can "post" changes from your secondary thread like this:
viewElement.post(new Runnable() {
public void run() {
//update UI
}
});
}
viewElement is any UI element that extends from View.
Edit: It was suggested that it would be helpful to add my code so, my AsyncTask code is now pasted below...
I'm just learning Android and I have a UI with a few buttons. I want to animate the UI, changing the color of the buttons, in a sequence.
I shouldn't do that from the main thread of course, and it doesn't work anyway. The code manipulating the UI runs but the UI doesn't update until the end of the sequence.
So I created a thread and tried to run through the sequence from a background thread however, I would get an error trying to manipulate the UI components from the background thread. Only the main thread can touch the UI components.
Then I discovered AsyncTask. What I figured was, I could run through the sequence in doInBackground(). Every time I needed to update the UI I'd call publishProgress() which would cause onProgressUpdate() to be called from the main thread so I could access UI components without error.
Every time I call publishProgress() I would follow it with a SystemClock.sleep(500) to let time pass until the next animated UI update.
What I found though was that doInBackground() would run through the 4 UI state changes in about 2 seconds (500 ms each) but the UI would not update with each call to publishProgress(). Instead doInBackground() completes and then onProgressUpdate() is called 4 times in a row.
From the description, publishProgress & onProgressUpdate are designed to update a progress bar as doInBackground cranks through some longish running task so, obviously, onProgressUpdate must execute multiple times before doInBackground completes, right?
Am I missing something?
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void startGame(View view) {
MyTask task = new MyTask();
task.doInBackground();
}
class MyTask extends AsyncTask<Void,Void,Void> {
private int current_int;
#Override
protected Void doInBackground(Void... voids) {
this.current_int = 1;
Log.e("doInBackground","light up button "+this.current_int);
publishProgress();
SystemClock.sleep(500);
this.current_int = 2;
Log.e("doInBackground","light up button "+this.current_int);
publishProgress();
SystemClock.sleep(500);
this.current_int = 1;
Log.e("doInBackground","light up button "+this.current_int);
publishProgress();
SystemClock.sleep(500);
this.current_int = 2;
Log.e("doInBackground","light up button "+this.current_int);
publishProgress();
SystemClock.sleep(500);
return null;
}
#Override
protected void onProgressUpdate(Void... voids) {
super.onProgressUpdate(voids);
Log.e("onProgressUpdate","Updating button "+this.current_int);
Button btn1 = (Button) findViewById(R.id.button1);
Button btn2 = (Button) findViewById(R.id.button2);
if (this.current_int==1){
btn1.setBackgroundColor(getResources().getColor(android.R.color.holo_blue_light));
btn2.setBackgroundColor(getResources().getColor(android.R.color.holo_blue_dark));
} else {
btn2.setBackgroundColor(getResources().getColor(android.R.color.holo_blue_light));
btn1.setBackgroundColor(getResources().getColor(android.R.color.holo_blue_dark));
}
}
}
}
Just for reference : An Asynctask presents a systematic way to transition from main thread (calling thread) to the new thread (called thread). The onPreExecute() and onPostExecute() methods execute on the calling thread and the doInBackground() is the actual method executing on the new thread. Doing UI updates on main thread will hence lead to exception if done from doInBackground() method.
Your core background logic should hence be placed in the doInBackground() method.
If you want to update UI from background thread (Asynctask or otherwise), you can do it using this :
YourActivity.this.runOnUiThread(new Runnable(){
#Override
public void run()
{
//UI update operations here
}
});
You can use Handler for this,
public class TestClass extends AsyncTask<Void,Void,Void> {
boolean isRunning = true; //set false after executing UI logic.
Handler mHandler = new Handler();
#Override
protected Void doInBackground(Void... params) {
YourFunctionToUpdateUI();
return null;
}
public void YourFunctionToUpdateUI()
{
new Thread(new Runnable() {
#Override
public void run() {
while (isRunning) {
try {
mHandler.post(new Runnable() {
#Override
public void run() {
// your code to update the UI.
}
});
} catch (Exception e) {
//handle exception
}
}
}
}).start();
}
}
I'm an idiot. The problem is, I'm creating an AsyncTask and then calling doInBackground() directly, from my main thread instead of calling execute() which creates the background thread
I eventually managed to get my other post sorted; to create a way to update the GUI every second or so. So my runnable runs fine but now I've added a button to the GUI which is meant to stop the runnable. But how do you do it?
I've tried this code:
// Button to stop the runnable
stop = ( Button ) findViewById( R.id.stop );
stop.setOnClickListener( new View.OnClickListener()
{
#Override
public void onClick(View v)
{
handler.removeCallbacksAndMessages( timerTask.class );
}
});
I implement Runnable in order to use it, therefore I don't create a new Thread manually and add a run() method to it. So how do you do it?
Thanks
You can't just murderize the thread. What you'll need to do is add a method to your Runnable object implementation that acknowledges a request to stop. That method then flips a condition that causes your Runnable.run() method to exit.
public class YourClass implements Runnable {
private boolean keepGoing = true;
public void run() {
while(keepGoing) {
// Do important work!
}
}
public void stop() {
this.keepGoing = false;
}
}
So in your onClick(View v) implementation for your stop button, you would call yourClassInstance.stop(). That breaks the loop, the run() method ends, and the thread is cleaned up.
In my activity class i want to perform a series of long calculations that require around 5 sec to complete when i press a button. So in order to do that i have create a new class which does all the calculations in its run method(since it implements Runnable) and when finished i set a variable to true to indicate that. In the code that checks the if the button is pressed i start a new Thread passing my class in it and then cheking whether the run method has finished or not. If it finished i then print the data. The problem with this is that when i check if the calculations have finished they actually havent so it pass that line of code and never prints the data. I have tried to do the Async Class method but still i think it wont work. Is there a way to create the thread when i press the button and keep checking if had finished so i can print the data? Which piece of code in an Activity is actually get executed over and over again? Thanks for any information.
if(v.equals(this.button)) {
EditText param1 = (EditText)findViewById(R.id.param1);
EditText param2 = (EditText)findViewById(R.id.param2);
calculations = new MathCalc(param1.getText().toString(), param2.getText().toString());
new Thread(calculations).start();
while(!calculations.isReady());
Intent intent = new Intent(this,Show.class);
intent.putExtra("show1", calculations.getResult());
startActivity(intent);
}
This is want i want to achieve.
The AsyncTask is the right tool for this. The typical use case for the AsyncTask is that you want to do something small in the background and leave feedback through the UI before, during and/or after the task is done.
Be aware that running things in the background can get you in trouble if the user quits and restarts your activity a lot, since the background task will not end when the Activity is removed from screen.
An example activity is shown below. You could add the onPreExecute and onProgress methods to the AsynchTask to give the user feedback before and during the calculation.
public class CalcActivity extends Activity {
private Button button;
private TextView resultView;
public void onCreate() {
button = (Button) findViewById(R.id.my_button);
resultView = (TextView) findViewById(R.id.result);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
button.setEnabled(false);
AsyncCalculation calc = new AsyncCalculation();
calc.execute();
}
});
}
public class AsyncCalculation extends AsyncTask<Void, Void, Integer> {
#Override
protected Integer doInBackground(Void... params) {
int result = 0;
// Do some calculation
return result;
}
#Override
protected void onPostExecute(Integer result) {
// Set the result, start another activity or do something else
resultView.setText("The result was " + result);
button.setEnabled(true);
}
}
}
I don't see how this won't work with AsyncTask. You basically need to override two methods - doInBackground() and onPostExecute().
You're guaranteed that onPostExecute() will be invoked on the UI thread after the background computation finishes. You also don't have to worry how to update the UI Thread from another thread.
Here's a good example.
Use
Button someButton = (Button) findViewById(R.id.favouriteButton);
someButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
while(!isDone){
doAlotOfCalculations();
}
}
});
thread.start();
}
});
private void doAlotOfCalculations(){
...
if(whenDone){
isDone = true;
}
....
}
Which piece of code in an Activity is actually get executed over and
over again?
There is no such a thing.
It is just onResume which executes every time you start(restart) this activity
This question is not only limit for Button or this setter.
For example, here a button listener:
runButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
runButton.setEnabled(false);
runScript();//This function will take a long time!!
}
});
When this button is clicked, I want it be disabled immediately. However, practically, I see this button won't get disabled until all the methods finished, that is after the method runScript() finishes, the button actually turns grey.
So, could anyone tell me how to make the button disabled right after that line?
What happens is the runScript method blocks the UI thread. What you want to do inside your onClick method is disable the button then run the script in a different thread, like so
runButton.setEnabled(false);
new Thread(new Runnable() {
#Override
public void run() {
runScript();//This function will take a long time!!
}
}).start();
More information found here:
http://developer.android.com/guide/topics/fundamentals/processes-and-threads.html
As a general rule of thumb, you want to avoid doing anything that may take a long time on the UI thread. What you can do in your click listener is spawn a new thread that does the long-running method in the background. The android AsyncTask class provides a lot of support methods for having background threads that can send updates back to the UI via progress events.
Try calling runButton.invalidate() immediately after setEnabled(false) method
Also
You should not have long running methods in the UI thread transfer runScript() to an asynch task. Button state will get updated faster
runButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
runButton.setEnabled(false);
MyTask asyncTask = new MyTask();
asyncTask.execute();
}
});
private class MyTask extends AsyncTask<void, void, String> {
protected String doInBackground(void) {
runScripts();
//set to something relevant to your app
String result ="";
return result;
}
protected void onPostExecute(String result) {
//do something
}
}
I