使用 setContentView 加载布局时显示 ProgressDialog

发布于 2024-11-17 03:24:00 字数 1992 浏览 4 评论 0原文

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

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

我很感激你的帮助。

弗拉。

编辑:我遵循了@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.

this is my scenario: I've got a login screen that opens another activity. In the Activity I simply have:

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

The layout is kind of heavy, cause it is made of some fragments, and takes about 1.5 seconds to load.
Now, how can I display a ProgressDialog while setContentView finishes inflating the layout? I've tried with AsyncTask by putting the setContentView in the doInBackground, but of course that cannot be done, as the UI can be updated from the UI thread only.
So I need to call setContentView in the UI thread, but where do I have to show/dismiss the ProgressDialog?

I appreciate your help.

Fra.

EDIT: I followed @JohnBoker's previous suggestion, this is the code I have now:

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

The row

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

gives me the error:

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

好多鱼好多余 2024-11-24 03:24:00

我决定在这里为未来的读者做出完整的回答。

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

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

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

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

I decided to make a full answer here for future readers.

After several hours spent on the issue, I realized that the issue is I'm trying to do two things:

  1. inflating a layout, which is an
    operation that NEEDS to be made on
    the UI thread by design.
  2. showing a Dialog (ProgressDialog,
    actually, but this does not change
    the outcome), that can be done from
    the UI thread only, since Services
    can't show any Dialog.

So, as both calls are made from the UI thread (onCreate or AsyncTask makes no difference, it's still the UI thread), the first blocks the second one from showing up appropriately.
Bottom line is: this problem can't be solved in Android right now. Let's hope we can get some better APIs for interacting with the UI, because the ones we've got kind of suck.

I'm going to solve this problem by changing the layout and making it lighter (if possible!).
Thanks everyone!

治碍 2024-11-24 03:24:00

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

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

I had the same issue when creating a heavy view, what I did was put a linearlayout only in the xml file and called the setContentView on that, then i created the real view in the asynctask and added the view dymanically to the linearlayout.

This method seems to work and i was able to put a progress dialog up during the process.

旧人九事 2024-11-24 03:24:00

这不是一个完美的解决方案,但到目前为止最接近我需要的解决方案:我们需要在设置进度对话框(或消息视图或其他)可见和调用 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
    ...
}

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

This is not a perfect solution, but so far the closest one to what I needed: We need some time between setting the progress dialog (or message view or whatever) visible and calling setContentView(), so that the dialog/message is actually displayed.

To achieve that, I start a Thread performing a short sleep(), then calling setContentView() in the UI thread.

Here is an example with a progress dialog:

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
    ...
}

My main issue with this is the sleep( 20 ), which is an approximation, and may not be enough on all machines. It works for me, but if you don't see the progress dialog, you probably have to increase the sleep duration.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文