在显示加载对话框时更新当前活动视图

时间:2012-02-29 19:06:44

标签: android ui-thread

我的应用流程与Android标准不同:

  • 标准:按下活动A上的按钮,在加载数据和构建活动B的视图时显示此活动的加载,然后才显示下一个活动。 (在活动A上调用AsyncTask,完成发送数据和调用活动B后)

  • 我需要的内容:按活动A上的按钮,转到活动B,在显示加载时,更新加载后面的活动B视图。 (从活动A移动到B,并在显示加载时调用AsyncTask。当AsyncTask完成时,在加载前面时更新视图,从而显示视图更新过程)

问题:这会使加载图标停止..有时会显示无响应的对话框(由于长时间操作)并使用UI线程更改正在构建的视图。

我已经阅读了有关同一事项的一些问题:Android: how to update UI dynamically?

但没有人提供通用答案。

如果您的回答是“这不是推荐的行为”我会同意......但这些是要求,所以我真的很感激一些帮助:-D

4 个答案:

答案 0 :(得分:4)

我在一年前的一个项目中首先做到了这一点。我遇到了同样的问题,因为我试图使用一个图标,并且有很多要更新的图标有时会冻结,所以我最终使用HandlerAsyncTask配对。它实际上是预先设置活动中非常常见的模式。

这些是步骤:

  1. 创建加载对话框,将其发布到Handler对象。您可以使用类似this的内容(显然是加载对话框而不是Toast)。
  2. 开始AsyncTask进行计算。
  3. 每当计算部分准备就绪时,你就publishProgress。为代码提供标志,以便准确地告诉它需要构建UI的哪些部分。在加载对话框显示和旋转时,您将看到正在构建的UI。
  4. 完成所有操作后,您将关闭onPostExecute中的对话框。
  5. 以下示例代码完全符合您的描述:

    private void settingsAndPlaceTaskStart() {
    
        // loading dialog
        startLoading();
    
        new AsyncTask<Context, Integer, Void>() {
            @Override
            protected Void doInBackground(Context... context) {
    
                // check and prepare settings integrity
                Settings st = new Settings(context[0], false);
                st.checkPreferencesIntegrity();
    
                // set text for each view / paired object
                // this will take a long time
                do {
                    // ... removed for clarity
                    int code = ...;
                    publishProgress(mObjects.getViewByCode(code));    
                    while (mObjects.getNext());
                return null;
            }
    
            @Override
            protected void onPostExecute(Void void) {
                // finish stuff
            }
    
            @Override
            protected void onProgressUpdate(Integer... message) {
                prepareView(message[0]);
            }
    
        }.execute(getActivity());
    }
    

    此处,当对话框中的进度旋转时,加载不会受到任何卡顿的影响。对话框保持在前景中,直到onPostExecute。在任务结束后,你应该忽略那里的对话框。

答案 1 :(得分:2)

让我确保我有这个权利:活动A是你的第一个,B是你的第二个。您希望A上的按钮触发两件事:异步数据加载和活动堆栈上的B推送。这一切都很好。

In&#34; 我需要什么&#34;你使用动词短语&#34;在后台更新视图&#34;。这是不可能的 - 您只能更新主UI线程上的视图。此外,一旦调用A onPause()(由B推动触发),您真的应该考虑A不可用于处理。你可以做的是在后台加载数据,而B做它的事情,然后在onResume()中的A可以适当地更新它的UI。

如果我错了,并且你在A出现时有A做一些UI工作,那么对你很好,但是如果你的处理时间过长,那么我想你会这样做必须将其分解成更小的块并逐步安排它们,以免中断用户对B的体验。

更新

我不确定在Activity X的范围内如何使用Activity Y的UI资源,尤其是如果Y尚未onCreate()d。我不知道是否可以在活动之间共享布局资源,尽管它可能是。

您在上面发布的链接指向它 - 我认为使用Handler类的答案是您解决方案的一部分。另请阅读LooperDesigning for Responsiveness。我想,简而言之,您需要在主线程上实现一个Handler,它允许您以100-200ms的小块更新UI(如响应性中所述),并且具有高粒度,当您在后台线程上处理少量数据时,将消息发送到该Handler。例如,如果您要更新ListView,则应该能够一次扩充一行。同样,如果您要将图片添加到GridView。另外,考虑 pre - 加载资源,但不要显示它们,以便在时间短的时候花更少的时间进行充气。

就目前我的想法而言,这是我的最爱,因为我认为我必须在几周内做一些类似的事情。让我们发布适合您的内容。祝你好运!

答案 2 :(得分:2)

一个。看看Android市场应用程序(我不确定它在“播放”中是如何工作的) - 当你点击app时 - 显示只有循环条的空活动,并且在完全下载后显示任何其他数据。 你想要同样的行为吗?如果是 - 以下内容将起作用:

您为B活动布局:

<RelativeLayout>
    <common_layout_of_your_view_that_covers_all_space />
    <layout_for_progress />
</RelativeLayout>

layout_for_progress可以是任何布局,如果你想让它透明 - 放入特殊的背景。

您活动B而不仅仅是启动后台数据加载的线程:

onCreate(...) {
    //call super
    //set content view

    new Thread(new Runnable() {
        public void run() {
            while(!downloadComplete) {
                //ask some portion of data
                ActivityB.this.runOnUIThread(new Runnable(){
                    public void run() {
                        //update your main view
                    }
                });
            }
            findViewById(R.id.layout_for_progress).setVisibility(View.GONE);
        }
    }).start();
}

答案 3 :(得分:1)

您可以在onCreate中启动asyncTask并让它完成所有计算。同时你可以显示一个加载栏。然后在onPostExecute中,您可以使用收集的信息更新视图并关闭进度条