Показать ProgressDialog при загрузке макета с помощью setContentView - PullRequest
12 голосов
/ 27 июня 2011

это мой сценарий: у меня есть экран входа, который открывает другое действие.В Упражнении у меня просто есть:

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

Макет довольно тяжелый, потому что он состоит из нескольких фрагментов, и для его загрузки требуется около 1,5 секунд.Теперь, как я могу отобразить ProgressDialog, пока setContentView заканчивает раздувать макет?Я попробовал с AsyncTask, поместив setContentView в doInBackground, но, конечно, это невозможно, поскольку пользовательский интерфейс может быть обновлен только из потока пользовательского интерфейса.Поэтому мне нужно позвонить 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.

Ответы [ 3 ]

8 голосов
/ 29 июня 2011

Я решил дать полный ответ здесь для будущих читателей.

После нескольких часов, потраченных на проблему, я понял, что проблема в том, что я пытаюсь сделать две вещи:

  1. раздувать макет, который является операция, которая должна быть сделана на UI поток по дизайну.
  2. отображение диалога (ProgressDialog, на самом деле, но это не меняет результат), что можно сделать из только пользовательский интерфейс, так как службы не может отображать диалоги.

Таким образом, поскольку оба вызова выполняются из потока пользовательского интерфейса (onCreate или AsyncTask не имеют значения, это все еще поток пользовательского интерфейса), первый блокирует отображение второго соответствующим образом. Итог: эта проблема не может быть решена в Android прямо сейчас. Будем надеяться, что мы сможем получить более совершенные API для взаимодействия с пользовательским интерфейсом, потому что те, которые у нас есть, отстой.

Я собираюсь решить эту проблему, изменив макет и сделав его светлее (если это возможно!). Спасибо всем!

8 голосов
/ 27 июня 2011

У меня была такая же проблема при создании тяжелого представления, что я сделал, поместил linearlayout только в XML-файл и вызвал setContentView для этого, затем я создал реальное представление в asynctask и добавил представление dymanically к linearlayout.

Этот метод, кажется, работает, и я смог вывести диалоговое окно прогресса во время процесса.

0 голосов
/ 23 сентября 2014

Это не идеальное решение, но пока самое близкое к тому, что мне было нужно: нам нужно некоторое время между установкой видимого диалогового окна прогресса (или представления сообщения или чего-либо еще) и вызовом setContentView (), чтобы диалог / сообщениена самом деле отображается.

Для этого я запускаю поток, выполняющий короткий сон (), затем вызываю 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), который является приблизительным и может быть недостаточным на всех машинах.Это работает для меня, но если вы не видите диалог прогресса, вам, вероятно, придется увеличить продолжительность сна.

...