繁体   English   中英

在 Android 中从 Thread 更新 UI

[英]Update UI from Thread in Android

我想从更新进度条的线程更新我的 UI。 不幸的是,当从“runnable”更新进度条的 drawable 时,进度条消失了! 在另一边的onCreate()更改进度条的 drawable 是可行的!

有什么建议?

public void onCreate(Bundle savedInstanceState) {
    res = getResources();
    super.onCreate(savedInstanceState);
    setContentView(R.layout.gameone);
    pB.setProgressDrawable(getResources().getDrawable(R.drawable.green)); //**Works**/
    handler.postDelayed(runnable, 1);       
}

private Runnable runnable = new Runnable() {
    public void run() {  
        runOnUiThread(new Runnable() { 
            public void run() 
            { 
                //* The Complete ProgressBar does not appear**/                         
                pB.setProgressDrawable(getResources().getDrawable(R.drawable.green)); 
            } 
        }); 
    }
}

你应该在AsyncTask (一个智能后台线程)和ProgressDialog的帮助下做到这一点

AsyncTask 可以正确且轻松地使用 UI 线程。 此类允许在 UI 线程上执行后台操作并发布结果,而无需操作线程和/或处理程序。

异步任务由在后台线程上运行且其结果发布在 UI 线程上的计算定义。 异步任务由 3 个泛型类型(称为 Params、Progress 和 Result)和 4 个步骤(称为 begin、doInBackground、processProgress 和 end)定义。

4个步骤

当一个异步任务被执行时,任务会经过 4 个步骤:

onPreExecute() ,在任务执行后立即在 UI 线程上调用。 此步骤通常用于设置任务,例如通过在用户界面中显示进度条。

doInBackground(Params...) ,在 onPreExecute() 完成执行后立即在后台线程上调用。 此步骤用于执行可能需要很长时间的后台计算。 异步任务的参数传递到这一步。 计算的结果必须由这一步返回,并将传递回最后一步。 这一步也可以使用 publishProgress(Progress...) 来发布一个或多个进度单元。 这些值发布在 UI 线程上的 onProgressUpdate(Progress...) 步骤中。

onProgressUpdate(Progress...) ,在调用 publishProgress(Progress...) 后在 UI 线程上调用。 执行的时间是不确定的。 此方法用于在后台计算仍在执行时在用户界面中显示任何形式的进度。 例如,它可用于动画进度条或在文本字段中显示日志。

onPostExecute(Result) ,在后台计算完成后在 UI 线程上调用。 后台计算的结果作为参数传递给这一步。 线程规则

要使此类正常工作,必须遵循一些线程规则:

任务实例必须在 UI 线程上创建。 execute(Params...) 必须在 UI 线程上调用。 不要手动调用 onPreExecute()、onPostExecute(Result)、doInBackground(Params...)、onProgressUpdate(Progress...)。 任务只能执行一次(如果尝试第二次执行,将抛出异常。)

示例代码
适配器在这个例子中做什么并不重要,更重要的是理解你需要使用 AsyncTask 来显示进度对话框。

private class PrepareAdapter1 extends AsyncTask<Void,Void,ContactsListCursorAdapter > {
    ProgressDialog dialog;
    @Override
    protected void onPreExecute() {
        dialog = new ProgressDialog(viewContacts.this);
        dialog.setMessage(getString(R.string.please_wait_while_loading));
        dialog.setIndeterminate(true);
        dialog.setCancelable(false);
        dialog.show();
    }
    /* (non-Javadoc)
     * @see android.os.AsyncTask#doInBackground(Params[])
     */
    @Override
    protected ContactsListCursorAdapter doInBackground(Void... params) {
        cur1 = objItem.getContacts();
        startManagingCursor(cur1);

        adapter1 = new ContactsListCursorAdapter (viewContacts.this,
                R.layout.contact_for_listitem, cur1, new String[] {}, new int[] {});

        return adapter1;
    }

    protected void onPostExecute(ContactsListCursorAdapter result) {
        list.setAdapter(result);
        dialog.dismiss();
    }
}

我见过的最简单的解决方案是通过视图的 post() 方法为 UI 线程提供一个简短的执行。 这是必需的,因为 UI 方法不可重入。 这样做的方法是:

package android.view;

public class View;

public boolean post(Runnable action);

post() 方法对应于 SwingUtilities.invokeLater()。 不幸的是,我没有找到与 SwingUtilities.invokeAndWait() 相对应的简单东西,但是可以基于前者使用监视器和标志构建后者。

所以你通过这个保存的是创建一个处理程序。 你只需要找到你的观点,然后在上面发帖。 如果您倾向于使用 id-ed 资源,您可以通过 findViewById() 找到您的视图。 生成的代码非常简单:

/* inside your non-UI thread */

view.post(new Runnable() {
        public void run() {
            /* the desired UI update */
        }
    });
}

注意:与 SwingUtilities.invokeLater() 相比,方法 View.post() 确实返回一个布尔值,指示视图是否具有关联的事件队列。 由于我使用了 invokeLater() 响应。 post() 无论如何只是为了火而忘记,我没有检查结果值。 基本上你应该只在 onAttachedToWindow() 在视图上调用之后调用 post() 。

此致

使用AsyncTask类(而不是 Runnable)。 它有一个名为 onProgressUpdate 的方法,它可以影响 UI(它在 UI 线程中调用)。

如果您使用Handler (我看到您这样做,并且希望您在 UI 线程上创建了它的实例),那么不要在runnable使用runOnUiThread() runOnUiThread()在您从非 UI 线程执行 smth 时使用,但是Handler已经在 UI 线程上执行您的runnable

尝试这样做:

private Handler mHandler = new Handler();
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.gameone);
    res = getResources();
    // pB.setProgressDrawable(getResources().getDrawable(R.drawable.green)); **//Works**
    mHandler.postDelayed(runnable, 1);
}

private Runnable runnable = new Runnable() {
    public void run() {
        pB.setProgressDrawable(getResources().getDrawable(R.drawable.green));
        pB.invalidate(); // maybe this will even not needed - try to comment out
    }
};

您需要在 UI 线程中创建一个Handler ,然后使用它从其他线程发布或发送消息以更新 UI

如果你不喜欢 AsyncTask 你可以使用观察者模式 在该示例中,使用 ResponseHandler 作为您活动中的内部类,然后有一个字符串消息来设置进度条百分比......您需要确保在 ResponseHandler 内执行对 UI 的任何更改,以避免冻结UI,然后您的工作线程(示例中的 EventSource)可以执行所需的任务。

我会使用 AsyncTask tho,但是观察者模式可以用于自定义原因,而且它更容易理解。 我也不确定这种方式是否被广泛接受或将 100% 工作。 我现在正在下载和 android 插件来测试它

按照官方文档的建议,您可以使用AsyncTask来处理持续时间短于 5ms 的工作项 如果您的任务需要更多时间,请寻找其他替代方案。

HandlerThreadThreadAsyncTask一种替代方法。 如果您需要从HandlerThread更新 UI,请在 UI Thread Looper上发布消息,UI ThreadHandler可以处理 UI 更新。

示例代码:

Android:在线程中吐司

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM