【问题标题】:Update current activity view behind/while showing loading dialog在显示加载对话框后/同时更新当前活动视图
【发布时间】:2012-02-29 19:06:43
【问题描述】:

我有一个应用流程与 Android 标准不同:

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

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

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

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

但没有人提供通用答案。

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

【问题讨论】:

    标签: android ui-thread


    【解决方案1】:

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

    这些是步骤:

    1. 创建一个加载对话框,将其发布到Handler 对象。您可以使用 this 之类的东西(显然是加载对话框而不是 Toast)。
    2. 开始您的AsyncTask 进行计算。
    3. 只要计算部分准备就绪,您就可以publishProgress。为代码提供标志,以便准确地告诉它需要构建 UI 的哪些部分。当加载对话框显示和旋转时,您会看到正在构建的 UI。
    4. 一切完成后,您关闭onPostExecute 中的对话框。

    以下是完全符合您描述的示例代码:

    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 之前,该对话框一直在前台。您应该在任务结束后关闭其中的对话框。

    【讨论】:

    • 如您所见,这实际上来自生产代码。为了清楚起见,我删除了一些内容,并让其他内容向您展示了需要很长时间的任务,这些任务AND 更新了 UI 的 lots 部分。顺便说一句,这应该全部包含在不断变化的活动中,因为您不能从另一个活动(在前台中的 this)操纵活动的 UI 元素(在本例中为已停止的活动)。
    • 不客气。顺便说一句,do 循环上缺少一个右括号……这是一个意外,我相信你无论如何都注意到了……
    【解决方案2】:

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

    在“我需要什么”中,您使用动词短语“在后台更新视图”。这是不可能的——您只能在主 UI 线程上更新视图。此外,一旦 A 的 onPause() 被调用(由 B 的推送触发),您确实应该认为 A 无法处理。您可以做的是在后台加载数据,而 B 做它的事情,然后 A 在其onResume() 中可以适当地更新其 UI。

    如果我错了,你让 A 在呈现 B 的同时做一些 UI 工作,那你很好,但如果你作为一个块的处理时间太长,那么我想你将不得不打破它分成更小的块并一点一点地安排它们,以免干扰用户对 B 的体验。

    更新

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

    您在上面发布的链接可以找到它 - 我认为有关使用 Handler 类的答案是您解决方案的一部分。另外,请阅读Looper 和Designing for Responsiveness。我认为,简而言之,您将希望在主线程上实现一个处理程序,它允许您以 100-200 毫秒的小块更新 UI(如 Responsiveness 中所建议的那样),并且具有高粒度,当您在后台线程上处理少量数据时,将消息发送到该处理程序。例如,如果您要更新ListView,您应该能够一次膨胀一行。同样,如果您将图像添加到 GridView。另外,考虑预-加载资源,但不显示它们,这样你就可以在时间紧迫时花更少的时间膨胀。

    这是我目前的想法,但我将其标记为最喜欢的,因为我认为我必须在几周内做类似的事情。让我们了解什么对您有用。祝你好运!

    【讨论】:

    • 谢谢 :-) 期待您的回复。
    【解决方案3】:

    A.查看 android 市场应用程序(我不确定它如何在“播放”中工作) - 当您单击应用程序时 - 仅显示循环栏的空活动,并且在完全下载后显示任何其他数据。 你想要同样的行为吗?如果是 - 以下将起作用:

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • 这将是一个不错的方法.. 问题是,如果您在 PostExecute / uiThread 上有很多视图膨胀,加载图标将停止。我只需要在收到数据后创建整个屏幕,因为我只知道数据到达后它将​​包含什么。
      • 所以你的问题是夸大你所有的观点需要太长时间?
      • 是的,构建屏幕需要很长时间......必须有一种方法可以做到这一点。
      猜你喜欢
      • 1970-01-01
      • 2020-04-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多