Android 更改按钮文本需要很长时间

Android change button text takes a long time

对于我的应用程序,我想disable/change按下一个特定的按钮。

我有一个名为 btnClicked 的 onclick 方法,简化后如下所示:

Public class MainActivity extends Activity{
     Button myBytton;

     @Override
     protected void onCreate(Bundle savedInstanceState) {
        myBytton = (Button)findViewById(R.id.buttonCall);
     } 
     public void btnClicked(View view)
     {
          myBytton.setText("loading");
          myBytton.setEnabled(false);
          myBytton.setClickable(false);
          // Do a call to an external api
          callApi();
     }

     public void callApi(){
          // run querys
          if(succesullyCalledApi){
                 vibrator.vibrate(500);
                 // I tried commenting out the below part, 
                 // it is than visible that the phone vibrates before it 
                 // has changed the text (atleast a quarter of a second).
                 myBytton.setText("search");
                 myBytton.setEnabled(true);
                 myBytton.setClickable(true);
          }
     }  
}

在callApi方法中有一个vibrate方法,在函数得到结果后振动。 此外,如果 callApi 中有结果,myButton 将被启用并且文本更改为搜索。

发生的情况如下:

我点击按钮,phone 首先振动,然后更改其文本。

我的问题。

为什么在myBytton.setText之前callApi/震动运行?

这是因为您对 API 的调用是在 UI 线程上完成的。即使您对 UI 进行了更改,屏幕也不会刷新,直到从按钮单击事件调用的处理完成。在新线程上或通过异步任务调用您的 API 以获得您想要的行为。

NigelK 说的是真的。

当您到达 btnClicked 方法时,所有指令都在 UI 线程上执行。因此,当您要求系统振动时,它会被阻止 XX 时间,具体取决于您传递给方法的时间 vibrator.vibrate(XX);.

为了避免这种情况"freeze"你需要在另一个线程上进行振动。

这是它的样子:

Public class MainActivity extends Activity 
{
    Button myBytton;

     @Override
     protected void onCreate(Bundle savedInstanceState) 
     {
         myBytton = (Button)findViewById(R.id.buttonCall);
     } 

     public void btnClicked(View view)
     {
         myBytton.setText("loading");
         myBytton.setEnabled(false);
         myBytton.setClickable(false);
         // Do a call to an external api
         callApi();
     }

     public void callApi() 
     {
         // run querys
         if(succesullyCalledApi) 
         {
             // here you create and run the Thread.
             // put anything you want to do inside the run method
             new Thread(
                 new Runnable() 
                 {
                     public void run() 
                     {
                         // here you start the vibration
                         vibrator.vibrate(500);
                     }
                 }
             ).start();


             // I tried commenting out the below part, 
             // it is than visible that the phone vibrates before it 
             // has changed the text (atleast a quarter of a second).
             myBytton.setText("search");
             myBytton.setEnabled(true);
             myBytton.setClickable(true);
         }
     }  
}

就是这样。它将启动另一个线程来处理振动并且不会冻结您的 UI 线程。


编辑

这是 AsyncTask 版本:

扩展 AsyncTask 时询问的三个元素是:

  • 你传递给doInBackground()方法的参数类型
  • onProgressUpdate() 方法中传递的元素的类型。
  • doInBackground() 方法返回的元素类型,也是 onPostExecute() 方法的参数。

这是它的样子:

public class MyTask extends AsyncTask<Void, Integer, Boolean> 
{
    private Button mButton;

    public MyTask(Button button) 
    {
        mButton = button;
    }     

     // Here everything will run on a background Thread
     protected Boolean doInBackground(Void... voids) 
     {
        boolean succesullyCalledApi = false;

        // do your long querys here
        // ...

        return succesullyCalledApi;
     }

     // Here everything will run on the UI Thread
     protected void onProgressUpdate(Integer... progress) {
         // here you can make some update to the UI like updating a 
         // progress bar
     }

     // Here everything will run on the UI Thread
     protected void onPostExecute(Boolean succesullyCalledApi) 
     {
         if(succesullyCalledApi) 
         {
             mButton.setText("search");
             mButton.setEnabled(true);
             mButton.setClickable(true);

             // here you start the vibration
             vibrator.vibrate(500);
         }
     }
 }

而在您的 callApi() 方法中,您只需要这样做 :

public void callApi() 
{
    new MyTask(myButton).execute();
}

编辑 2

为了将查询检索回您的主线程(或 UI 线程),您所要做的就是......什么都不做。

调用 onPostExecute() 方法时,您在 UI 线程中。

但我假设您想将查询检索回 MainActivity。为此:

  • MyTask构造函数的参数中传递MainActivity,
  • MainActivity 中创建一个名为 processQuery() 的方法(或任何你想要的),
  • 最后在onPostExecute()方法中调用这个方法

以下是一些片段:

Public class MainActivity extends Activity 
{
    Button myBytton;

    ...


    public void callApi() 
    {
        // add this to the constructor
        new MyTask(this, myButton).execute();
    }

    // I put String here but adapt it to your query Type.
    public void processQuery(String query)
    {
        // process your query here.
    }
}


public class MyTask extends AsyncTask<Void, Integer, Boolean> 
{
    private Button mButton;
    private MainActivity mMainActivity;

    public MyTask(MainActivity mainActivity, Button button) 
    {
        mButton = button;
        mMainActivity = mainActivity;
    }

    ...

    // Here everything will run on the UI Thread
     protected void onPostExecute(Boolean succesullyCalledApi) 
     {
         if(succesullyCalledApi)
         {
             // process your query
             mMainActivity.processQuery("THE QUERY YOUR WANT TO PROCESS");

             mButton.setText("search");
             mButton.setEnabled(true);
             mButton.setClickable(true);

             // here you start the vibration
             vibrator.vibrate(500);
         }
     }

}

可能有更好的方法来做到这一点,但这个方法简单有效:)

希望对您有所帮助。

干杯

因为您正在 UI 线程中执行所有操作。您必须为长 运行 操作使用 AsyncTask

尝试以下实现:

public void callApi() {
  MyTask myTask = new MyTask();
  myTask.execute();
}


private class MyTask extends AsyncTask<Void, Void, Boolean> {
  protected void doInBackground(Void... params) {
    // This runs on a separate background thread

    boolean succesullyCalledApi = false;
    // run querys
    // do your long running query here and return its result.

    return succesullyCalledApi;
  }

  protected void onPostExecute(Boolean succesullyCalledApi) {
    // this runs on UI Thread

    if(succesullyCalledApi){
      vibrator.vibrate(500);
      myBytton.setText("search");
      myBytton.setEnabled(true);
      myBytton.setClickable(true);
    } else {
      // You should better think this part also. what will happen if result is false?
    }
  }

}