从Async Task更新TextView,它使用自定义程序对话框

时间:2012-04-17 16:10:06

标签: android android-asynctask textview

在我的一个应用程序中,我有一个场景,我需要做一些后台任务。为此,我使用异步任务。我也在使用自定义进度对话框。以下是自定义进度对话框的布局

<?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>

一切正常但当我尝试将文本设置为TextView时,我得到java NullPointerException。

AsyncTask代码

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("");

    }
}

4 个答案:

答案 0 :(得分:13)

如果我理解正确,您可以在xml文件progressbar.xml(即TextView)中找到要设置文本的R.layout.progressbar。设置内容视图后,可以获取此TextView(使用setContentView())。在您的代码中,您在此调用之前设置它和mussharapp的代码,他正在提前调用它。也就是说,他在setContentView(R.layout.accountsummary)来电后调用它,但不包含TextView。因此,变量txtView将为NULL,您将获得NullPointerException

您应该做的是以下内容:

  • 在调用onPreExecute后,在setContentView中设置变量txtView。
  • 基于Paresh Mayani的explanation:使用runOnUiThread方法。

对于下面的代码:

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

答案 1 :(得分:12)

是的,因为你试图在doInBackground()方法中设置TextView,这是不允许的,

为什么不允许?因为只有一个Thread运行是UI主线程,并且不允许从线程进程更新UI。在此处阅读更多信息:Painless Threading

因此,如果要在doInBackground()方法中设置TextView,在runOnUiThread方法中执行UI更新操作,则有一个解决方案。

否则,建议在onPostExecute()方法中执行所有与UI显示/更新相关的操作,而不是AsyncTask类的doInBackground()方法。

答案 2 :(得分:1)

(TextView)findViewByid(R.id.progressMessage);

只应在命令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("");

}

此外,您只能更改主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");**
        }
    }

答案 3 :(得分:1)

解释是正确的:除了创建UI的线程之外,您不要在任何线程中进行UI更改。但AsyncTask有一个名为

的方法
onProgressUpdate()

总是会在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]); 
    }
}

请注意,onProgressUpdate的参数类型是AsyncTask中给出的第二种类型!

额外:为了使您的代码更加强大,您应该在设置文本之前检查进度对话框是否仍然存在。