How to disable a button while running AsyncTask? (Android)

I am trying to disable a button while a download task is running. I tried using setEnabled, setVisibility and setClickable. I seem to have tried all combinations of these options. All of them disable button click events during task execution, but events are still logged somehow, and when I react to a button, the handler is called if I pressed the button while it was disabled ... even if it was invisible or "gone "! (not sure if it is called a handler, I want to refer to the onClick method).

I also added a counter and a log to check what I said above. The code is shown below. This piece of code if(counter>1) return; designed to stop the crash, but I would like to remove it, since I want to turn this button back on and not turn it off permanently.

OnClick:

 public void downloadOnClick(View v) { counter++; Log.d(this.getLocalClassName(), "Button was clicked " + counter + " times."); if(counter>1) return; ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); if (networkInfo != null && networkInfo.isConnected()) { //mButton.setVisibility(View.GONE); mButton.setEnabled(false); //mButton.setClickable(false); mTextView.setText("Getting html file..."); // if we use simple http, we will need to handle redirect status code new DownloadWebpageTask().execute("https://www.google.com/"); } else { mTextView.setText("No network connection available."); } } 

AsyncTask:

 private class DownloadWebpageTask extends AsyncTask<String, Void, String> { private HttpURLConnection mConnection; @Override protected String doInBackground(String... urls) { try { URL url = new URL(urls[0]); mConnection = (HttpURLConnection) url.openConnection(); mConnection.setReadTimeout(10000 /* milliseconds */); mConnection.setConnectTimeout(15000 /* milliseconds */); mConnection.setRequestMethod("GET"); mConnection.setDoInput(true); mConnection.connect(); int statusCode = mConnection.getResponseCode(); if (statusCode != HttpURLConnection.HTTP_OK) { return "Error: Failed getting update notes"; } return readTextFromServer(mConnection); } catch (IOException e) { return "Error: " + e.getMessage(); } } private String readTextFromServer(HttpURLConnection connection) throws IOException { InputStreamReader stream = null; try { stream = new InputStreamReader(connection.getInputStream()); BufferedReader br = new BufferedReader(stream); StringBuilder sb = new StringBuilder(); String line = br.readLine(); while (line != null) { sb.append(line + "\n"); line = br.readLine(); } return sb.toString(); } finally { if (stream != null) { stream.close(); } } } @Override protected void onPostExecute(String result) { mTextView.setText(result); // Can not reactivate button / cancel (pending?) events.... //mButton.setVisibility(View.VISIBLE); mButton.setEnabled(true); //mButton.setClickable(true); } } 

The full project (this is a very simple, just a training example) is available for testing in this repository that I just created.

In conclusion, from what I read, there is actually a problem with disabling buttons. Basically, this is allowed using a flag to call the onClick method only when the flag is true. Although this does not solve the problem of re-enabling the button. I also tried mButton.cancelPendingInputEvents(); but it doesn’t work (and I don’t know why. Are events not registered yet? Or are they not expected?

Is there a simple solution to this problem? Any ideas? Am I missing some basic details? If not, I'm thinking of trying to create a new button programmatically to outline the problem. If I don’t refer to the old buttons, are they deleted through the garbage collection?

Thanks in advance!

[Edit] Clarification:

Since the title may be misleading at this point, I want to clarify that I can disable and enable the button again, and all functions are in order , except when the function is disabled . And note that I added the line if(counter>1) return; just for verification, but it doesn’t allow the button to work the way I wanted (why I don’t use the flag. I don’t want this line to be there when I solve the problem!). The log is enough to tell me that the method is called when the button is re-enabled , because I clicked it when it was disabled!

+5
source share
2 answers

I found that in your example, AsyncTask running so fast that there was very little time for Button not be available for click due to shutdown. So this is basically a synchronization issue.

I found that by delaying the re-enablement of Button for 4 seconds, it works as expected.

Pay attention to this change, which visually, Button turns on again a second after filling the TextView .

Here is the code change in onPostExecute() :

  @Override protected void onPostExecute(String result) { mTextView.setText(result); Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { //re-enable the button mButton.setEnabled(true); } }, 4000); } 

Note that you can remove the counter logic, and it should work as expected now:

 public void downloadOnClick(View v) { Log.d(this.getLocalClassName(), "Button was clicked"); ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); if (networkInfo != null && networkInfo.isConnected()) { mButton.setEnabled(false); mTextView.setText("Getting html file..."); // if we use simple http, we will need to handle redirect status code new DownloadWebpageTask().execute("https://www.google.com/"); } else { mTextView.setText("No network connection available."); } } 
+2
source

The error is here:

 <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="DOWNLOAD TEXT" android:id="@+id/button" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:onClick="downloadOnClick" /> 

You are missing the concept of OnClickListener ! First of all, you must modify the above xml in such a way as to remove the onClick attribute:

 <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="DOWNLOAD TEXT" android:id="@+id/button" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" /> 

Then you need to change the Activity onCreate method to set the OnClickListener to the button:

 protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = (TextView) findViewById(R.id.text); mButton = (Button) findViewById(R.id.button); mButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { counter++; Log.d(this.getLocalClassName(), "Button was clicked " + counter + " times."); if(counter>1) return; ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); if (networkInfo != null && networkInfo.isConnected()) { mButton.setEnabled(false); mTextView.setText("Getting html file..."); // if we use simple http, we will need to handle redirect status code new DownloadWebpageTask().execute("https://www.google.com/"); } else { mTextView.setText("No network connection available."); } } }); } 

This is the right way to handle a click.

More details:

Wherein:

The best way from my point of view is to implement OnClickListener () in your activity:

 public class MyActivity extends Activity implements View.OnClickListener { } 

Thus, you can write for each button where you need to install OnClickListener:

 buttonX.setOnClickListener(this); buttonY.setOnClickListener(this); buttonZ.setOnClickListener(this); 

In your onClick () activity, you must override the OnClickListener methods, therefore:

 @Override public void onClick(View v) { if(v.getId() == R.id.ButtonX)){ //do here what u wanna do. } else if(v.getId() == R.id.ButtonY){ //do here what u wanna do. } else if(v.getId() == R.id.ButtonZ){ //do here what u wanna do. } } 

Also in onClick, you can use view.getId () to get the resource identifier, and then use it in the switch / case block to identify each button and perform the corresponding action.

0
source

All Articles