Android 从使用自定义程序对话框的异步任务更新 TextView
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10194931/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Updating TextView from Async Task which use custom program dialog
提问by CrazyCoder
In one of my app, I have a scenario where I need to do some background task. For doing that I am using Async Task. Also I am using custom progress dialog. Below is the layout of the custom progress dialog
在我的一个应用程序中,我有一个场景,我需要执行一些后台任务。为此,我正在使用异步任务。我也在使用自定义进度对话框。下面是自定义进度对话框的布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical|center_horizontal"
android:orientation="vertical" >
<ProgressBar
android:layout_width="60dp"
android:layout_height="60dp"
android:indeterminateDrawable="@drawable/progressloader"
android:layout_gravity="center"/>
<TextView
android:id="@+id/progressMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="18sp"
android:text="Please wait...." />
</LinearLayout>
Everything works fine but when I try to set text to TextView then I am getting java NullPointerException.
一切正常,但是当我尝试将文本设置为 TextView 时,我收到了 java NullPointerException。
AsyncTask code
异步任务代码
private class InitialSetup extends AsyncTask<String, Integer, Long> {
ProgressDialog dialog = new ProgressDialog(getParent(),R.style.progressdialog);
@Override
protected void onPreExecute() {
dialog.show();
dialog.setContentView(R.layout.progressbar);
}
@Override
protected Long doInBackground(String... urls) {
// txtView.setText("Testing"); here I am getting the error
fetchDetails();
return 0;
}
@Override
protected void onPostExecute(Long result) {
if (this.dialog.isShowing()) {
this.dialog.dismiss();
}
populateUI(getApplicationContext());
}
}
MainActivity
主要活动
public class SummaryActivity extends Activity {
final TextView txtView = (TextView)findbyid(R.id.progressMessage);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.accountsummary);
new InitialSetup().execute("");
}
}
回答by dennisg
If I understand correctly, your TextView
of which you want to set the text can be found in the xml file progressbar.xml (i.e. R.layout.progressbar
). This TextView
can be obtained once the content view has been set (using setContentView()
). In your code you set it before this call is been and the code of mussharapp, he is calling it to early. Namely, he calls it after the setContentView(R.layout.accountsummary)
call which does not contain the TextView
. Consequently, the variable txtView
will be NULL and you will get a NullPointerException
.
如果我理解正确,您TextView
可以在xml文件progressbar.xml(即R.layout.progressbar
)中找到您要设置的文本。这TextView
可以一旦内容视图(使用被设置来获得setContentView()
)。在您的代码中,您在此调用之前设置了它,而 mussharapp 的代码,他提前调用了它。也就是说,他在setContentView(R.layout.accountsummary)
不包含 的调用之后调用它TextView
。因此,该变量txtView
将为 NULL,您将获得一个NullPointerException
.
What you should do is the following:
你应该做的是:
- Set the variable txtView in
onPreExecute
, aftersetContentView
is called. - Based on Paresh Mayani's explanation: Use the runOnUiThread method.
- 在 中设置变量 txtView
onPreExecute
,在setContentView
调用之后。 - 基于 Paresh Mayani 的解释:使用 runOnUiThread 方法。
For the code look down below:
代码往下看:
private class InitialSetup extends AsyncTask<String, Integer, Long> {
ProgressDialog dialog = new ProgressDialog(getParent(),R.style.progressdialog);
// The variable is moved here, we only need it here while displaying the
// progress dialog.
TextView txtView;
@Override
protected void onPreExecute() {
dialog.show();
dialog.setContentView(R.layout.progressbar);
// Set the variable txtView here, after setContentView on the dialog
// has been called! use dialog.findViewById().
txtView = dialog.findViewById(R.id.progressMessage);
}
@Override
protected Long doInBackground(String... urls) {
// Already suggested by Paresh Mayani:
// Use the runOnUiThread method.
// See his explanation.
runOnUiThread(new Runnable() {
@Override
public void run() {
txtView.setText("Testing");
}
});
fetchDetails();
return 0;
}
@Override
protected void onPostExecute(Long result) {
if (this.dialog.isShowing()) {
this.dialog.dismiss();
}
populateUI(getApplicationContext());
}
}
回答by Paresh Mayani
Yes, because you are trying to set the TextView inside the doInBackground() method, and this is not allowed,
是的,因为您试图在 doInBackground() 方法中设置 TextView,这是不允许的,
Why not allowed? Because There is a only one Thread running which is UI Main Thread, and it doesn't allowed to update UI from thread process. read more info here: Painless Threading
为什么不允许?因为只有一个线程在运行,即 UI 主线程,并且不允许从线程进程更新 UI。在此处阅读更多信息:无痛线程
So there is a solution if you want to set the TextView inside the doInBackground() method, do the UI updating operations inside the runOnUiThreadmethod.
因此,有一个解决方案,如果你想设置的doInBackground()方法中TextView的,做内部的UI更新操作runOnUiThread方法。
Otherwise,suggestion is to do all the UI display/update related operations inside the onPostExecute() method instead of doInBackground() method of your AsyncTask class.
否则,建议在 onPostExecute() 方法中执行所有 UI 显示/更新相关操作,而不是 AsyncTask 类的 doInBackground() 方法。
回答by Mark Pazon
(TextView)findViewByid(R.id.progressMessage);
should only be executed after the command setContentView().
应该只在命令 setContentView() 之后执行。
TextView txtView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.accountsummary);
**txtView = (TextView)findbyid(R.id.progressMessage);**
new InitialSetup().execute("");
}
Also you can only change UI elements in the main UI thread. doInBackground() is not in the main UI thread. Make UI changes in onPostExecute
此外,您只能在主 UI 线程中更改 UI 元素。doInBackground() 不在主 UI 线程中。在 onPostExecute 中更改 UI
public class InitialSetup extends AsyncTask<String, Integer, Long> {
private Activity activity;
ProgressDialog progressDialog;
public InitialSetup(Activity activity) {
this.activity = activity;
}
@Override
protected void onPreExecute() {
progressDialog = new ProgressDialog(activity);
progressDialog.setMessage("Starting task....");
progressDialog.show();
}
@Override
protected Long doInBackground(String... urls) {
// do something
//
return 0;
}
@Override
protected void onPostExecute(Long result) {
progressDialog.dismiss();
//Perform all UI changes here
**textView.setText("Text#2");**
}
}
回答by ToBe_HH
The explanations are correct: You are not to make UI changes in any thread except the thread which create the UI. But AsyncTask has a method called
解释是正确的:除了创建 UI 的线程之外,您不得在任何线程中更改 UI。但是 AsyncTask 有一个方法叫做
onProgressUpdate()
which always will run in the UI Thread. So based on the modifications by dennisg your code should look like this:
它将始终在 UI 线程中运行。因此,根据 dennisg 的修改,您的代码应如下所示:
private class InitialSetup extends AsyncTask<String, String, Long> {
ProgressDialog dialog = new ProgressDialog(getParent(),R.style.progressdialog);
// The variable is moved here, we only need it here while displaying the
// progress dialog.
TextView txtView;
@Override
protected void onPreExecute() {
dialog.show();
dialog.setContentView(R.layout.progressbar);
// Set the variable txtView here, after setContentView on the dialog
// has been called! use dialog.findViewById().
txtView = dialog.findViewById(R.id.progressMessage);
}
@Override
protected Long doInBackground(String... urls) {
publishProgress("Testing");
fetchDetails();
return 0;
}
@Override
protected void onPostExecute(Long result) {
if (this.dialog.isShowing()) {
this.dialog.dismiss();
}
populateUI(getApplicationContext());
}
@Override
protected void onProgressUpdate(String... update) {
if (update.length > 0)
txtView.setText(update[0]);
}
}
Note that the type of the parameter of onProgressUpdate is the second type given in AsyncTask!
注意onProgressUpdate的参数类型是AsyncTask中给出的第二种类型!
Extra:To make your code more robust you should check if the progress dialog still exists before setting the text.
额外:为了使您的代码更健壮,您应该在设置文本之前检查进度对话框是否仍然存在。