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?
}
}
}
对于我的应用程序,我想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?
}
}
}