【问题标题】:Android change button text takes a long timeAndroid更改按钮文本需要很长时间
【发布时间】:2015-05-04 22:25:05
【问题描述】:

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

我有一个名为 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 之前运行?

【问题讨论】:

    标签: android android-button android-vibration


    【解决方案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()方法中调用这个方法。

    这里有一些sn-ps:

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

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

    希望对你有帮助。

    干杯

    【讨论】:

    • 但我无法运行 myBytton.setText("search");在等待 api 结果的同一线程上,而不是因为它不是按钮元素的原始线程。尝试并生成:只有创建视图层次结构的原始线程才能触及其视图。
    • 好吧,我不太明白,那么您需要为此目的使用 AsyncTask。我会用一些代码编辑我的答案。
    • 感谢帮了大忙,如果能给出将查询结果发送到主线程的方法就更好了。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      因为您在 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?
          }
        }
      
      }
      

      【讨论】:

        猜你喜欢
        • 2018-04-26
        • 2014-01-14
        • 2016-10-21
        • 2014-07-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-30
        • 1970-01-01
        相关资源
        最近更新 更多