1

对于我的应用程序,我想禁用/更改按下的特定按钮。

我有一个名为 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 方法中是一个振动方法,它在函数得到结果后振动。此外,如果 callApi 中有结果,则将启用 myButton 并将文本更改为搜索。

会发生以下情况:

我单击按钮,手机首先振动,然后更改其文本。

我的问题。

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

4

3 回答 3

1

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

于 2015-05-04T22:32:35.437 回答
1

NigelK 说的是真的。

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

为了避免这种“冻结”,您需要在另一个线程上进行振动。

这是它的样子:

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);
         }
     }

}

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

希望能帮助到你。

干杯

于 2015-05-04T22:41:53.657 回答
0

因为你在 UI Thread 做所有事情。您必须使用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?
    }
  }

}
于 2015-05-04T22:50:53.673 回答