【问题标题】:Show ProgressDialog while loading layout with setContentView使用 setContentView 加载布局时显示 ProgressDialog
【发布时间】:2011-09-23 12:37:11
【问题描述】:

这是我的场景:我有一个打开另一个活动的登录屏幕。在活动中我只有:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_details);
}

布局有点重,因为它是由一些片段组成的,加载大约需要 1.5 秒。 现在,如何在 setContentView 完成填充布局时显示 ProgressDialog?我已经尝试通过将setContentView 放入doInBackground 来使用AsyncTask,但当然不能这样做,因为只能从 UI 线程更新 UI。 所以我需要在 UI 线程中调用setContentView,但是我必须在哪里显示/关闭ProgressDialog

感谢您的帮助。

Fra.

编辑:我遵循@JohnBoker 之前的建议,这是我现在拥有的代码:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_empty_layout);
    new ContentSetterTask().execute("");
}

private class ContentSetterTask extends AsyncTask<String, Void, Void> {

    public ProgressDialog prgDlg;

    @Override
    protected void onPreExecute() {
        android.os.Debug.waitForDebugger();
        prgDlg = ProgressDialog.show(MultiPaneActivity.this, "", "Loading...", true);

    }

@Override
protected Void doInBackground(String... args) {
    android.os.Debug.waitForDebugger();
    ViewGroup rootView = (ViewGroup)findViewById(R.id.emptyLayout);
    LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    View inflated = inflater.inflate(R.layout.activity_details, rootView);
    return null;
}

@Override
protected void onPostExecute(Void arg) {
    android.os.Debug.waitForDebugger();
    if (prgDlg.isShowing())
        prgDlg.dismiss();
    }
  }
}

View inflated = inflater.inflate(R.layout.activity_details, rootView);

给我错误:

06-27 16:47:24.010:   
ERROR/AndroidRuntime(8830): Caused by:android.view.ViewRoot$CalledFromWrongThreadException: 
Only the original thread that created a view hierarchy can touch its views.

【问题讨论】:

  • 在 doInBackground 方法中手动充气怎么样?
  • 看看你的布局文件会很有趣,我认为最好尝试优化。 1.5秒是在模拟器上还是在手机/平板上?
  • 嗨,在 eeePad Transformer 上实际上大约是 1.5 秒。如何手动充气?布局有点长,在这里发布,但它有大约 10-12 个线性布局和一个真正的片段(目前)。
  • activity_empty_layout 上是否有一个布局(相对、线性等),对吗?膨胀activity_details并将null传递给root怎么样,所以,之后,您可以使用父布局中的addView。
  • 试过了:View inflated = inflater.inflate(R.layout.activity_details, null); rootView.addView(inflated);我仍然遇到同样的错误。

标签: android multithreading android-activity android-asynctask progressdialog


【解决方案1】:

我决定在这里为未来的读者做一个完整的答案。

在这个问题上花了几个小时后,我意识到问题在于我正在尝试做两件事:

  1. 膨胀布局,这是一个 需要进行的操作 UI 线程的设计。
  2. 显示一个对话框(ProgressDialog, 实际上,但这并没有改变 结果),这可以从 仅 UI 线程,因为服务 无法显示任何对话框。

因此,由于这两个调用都是从 UI 线程进行的(onCreate 或 AsyncTask 没有区别,它仍然是 UI 线程),因此第一个调用会阻止第二个调用正确显示。 底线是:这个问题目前在 Android 中无法解决。让我们希望我们能够获得一些更好的 API 来与 UI 交互,因为我们得到的 API 有点糟糕。

我将通过更改布局并使其更轻(如果可能的话!)来解决这个问题。 谢谢大家!

【讨论】:

  • 如果您使用 HandlerThread,您可以在单独的线程上手动扩展视图。基本上,您在其上扩展视图的线程需要 Looper。
【解决方案2】:

我在创建重视图时遇到了同样的问题,我所做的只是在 xml 文件中放置一个线性布局并在其上调用 setContentView,然后我在 asynctask 中创建了真实视图并将视图动态添加到线性布局.

此方法似乎有效,并且我能够在此过程中放置​​一个进度对话框。

【讨论】:

  • 是的,这似乎是一个完美的选择!
  • 您可以在 AsyncTask 中查看 developer.android.com/resources/articles/… 的线程部分,将显示的进度对话框放在 onPreExecute 中,在 onPostExecute 中关闭。 stackoverflow.com/questions/5292328/… 中有一个膨胀视图的示例
  • 我已经在前后进行了展示和解雇。现在我在 doInBackground 中添加了通货膨胀,这就是我得到的(明显的)错误:06-27 16:12:35.630: ERROR/AndroidRuntime(8077): Caused by: android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread创建视图层次结构的可以触及其视图。
  • 我在第一篇文章中添加了一些细节,上面有完整的代码和错误。看来我做了一些与你建议的不同的事情。 :(
【解决方案3】:

这不是一个完美的解决方案,但迄今为止最接近我需要的解决方案:在设置进度对话框(或消息视图或其他)可见和调用 setContentView() 之间需要一些时间,以便对话框/消息实际显示出来。

为了实现这一点,我启动了一个执行短暂 sleep() 的线程,然后在 UI 线程中调用 setContentView()。

这是一个带有进度对话框的示例:

private ProgressDialog  m_Hourglass = null ;
private Thread          m_LoadingThread = null ;

...

public void onCreate( Bundle savedInstanceState )
{
    super.onCreate( savedInstanceState );

    ...

    m_Hourglass = new ProgressDialog( this );
    m_Hourglass.setMessage( "Loading" );
    m_Hourglass.setIndeterminate( true );
    m_Hourglass.setCancelable( false );
}

public void SetMyView( View vvv )                   // Can be called from buttons.
{
    if( m_LoadingThread != null ) return;           // Avoid multiple press

    m_Hourglass.show();

    ////////////////////////////////////////////////////
    // Load in a thread, just to show the above message
    // before changing layout (because setContentView()
    // freezes the GUI.)
    m_LoadingThread = new Thread()
    {
            @Override
            public void run()
            {
                super.run();
                try                 {   sleep( 20 ); }
                catch (Exception e) {   System.out.println( e ); }
                finally             {   runOnUiThread( new Runnable()
                                        {
                                            public void run() 
                                            {
                                                SetMyViewThreaded();
                                            }
                                        });
                                    }
                m_LoadingThread = null ; // Now we can load again
            }
    };
    m_LoadingThread.start();
}

public void SetMyViewThreaded()
{
    setContentView( R.layout.some_gorgeous_layout );

    /////////////////////////////////////////////////////
    // Catch when to finally hide the progress dialog:
    ImageView iv            = (ImageView) findViewById( R.id.some_view_in_the_layout );
    ViewTreeObserver obs    = iv.getViewTreeObserver();      
    obs.addOnGlobalLayoutListener( new OnGlobalLayoutListener()
    {          
         @Override          
         public void onGlobalLayout()    // The view is now loaded
         {
             m_Hourglass.hide();
         }      
    });

    //////////////////////////////////////////////////////
    // Here we can work on the layout
    ...
}

我的主要问题是 sleep(20),这是一个近似值,在所有机器上可能还不够。它对我有用,但如果您没有看到进度对话框,您可能需要增加睡眠持续时间。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-17
    • 1970-01-01
    • 2021-06-28
    • 2013-06-12
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    相关资源
    最近更新 更多