12

Oto mój scenariusz: Mam ekran logowania, który otwiera inną aktywność. W działaniu mam po prostu:Pokaż ProgressDialog podczas ładowania układu z setContentView

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

Układ jest dość ciężki, ponieważ składa się z kilku fragmentów, a jego załadowanie zajmuje około 1,5 sekundy. Teraz, jak mogę wyświetlić ProgressDialog, gdy setContentView kończy nadmuchiwanie układu? Próbowałem z AsyncTask umieszczając setContentView w doInBackground, ale oczywiście nie można tego zrobić, ponieważ interfejs można aktualizować tylko z poziomu interfejsu użytkownika. Muszę więc zadzwonić pod numer setContentView w wątku interfejsu użytkownika, ale gdzie muszę pokazać/odrzucić numer ProgressDialog?

Doceniam twoją pomoc.

Fra.

EDIT: po @ JohnBoker w poprzednim sugestii, jest to kod mam teraz:

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

Wiersz

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

daje mi błąd:

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. 
+1

Co powiecie na ręczne nadmuchiwanie w metodzie doInBackground? –

+0

Byłoby interesujące zobaczyć twój plik układu, najlepiej, aby spróbować i zoptymalizować, co myślę. Czy jest 1,5 sekundy na emulatorze, czy na telefonie/tablecie? – Marty

+0

Witam, w rzeczywistości jest to około 1,5 sekundy na transformatorze eeePad. Jak mogę nadmuchać to ręcznie? Układ jest tu dość długi, ale ma około 10-12 układów liniowych i jeden prawdziwy fragment (na razie). – frapontillo

Odpowiedz

8

Postanowiłem udzielić pełnej odpowiedzi tutaj dla przyszłych czytelników.

Po kilku godzinach spędzonych w tej sprawie, zdałem sobie sprawę, że problem jest usiłuję zrobić dwie rzeczy:

  1. napełniania układu, który jest operacja który musi być wykonany na na Wątek interfejsu użytkownika według projektu.
  2. pokazując Dialog (ProgressDialog, faktycznie, ale to nie zmienia wynikach), które mogą być wykonane z tylko wątek UI, ponieważ usługi nie może wykazywać żadnych dialogowe.

Tak więc, ponieważ oba wywołania są wykonywane z wątku UI (onCreate lub AsyncTask nie robi różnicy, to wciąż wątek UI), pierwsze bloki drugi pojawia się odpowiednio. Dolna linia to: ten problem nie może zostać rozwiązany w tej chwili przez w systemie Android. Miejmy nadzieję, że uda nam się zdobyć lepsze interfejsy API do interakcji z interfejsem użytkownika, ponieważ te, które mamy w pewnym sensie ssać.

Zamierzam rozwiązać ten problem, zmieniając układ i czyniąc go lżejszym (jeśli to możliwe!). Dziękuję wszystkim!

+1

Możesz ręcznie nadmuchać widoki w oddzielnym wątku, jeśli używasz HandlerThread. Zasadniczo wątek, w którym zawyżasz widoki, wymaga Looper'a. – techiServices

8

I Miałem ten sam problem przy tworzeniu ciężkiego widoku, co zrobiłem, umieściłem linelayout tylko w pliku xml i wywołałem setConten tView na to, a następnie stworzyłem prawdziwy widok w asynktask i dodałem widok dymarnie do linelayout.

Ta metoda wydaje się działać i mogłem umieścić okno dialogowe postępu podczas procesu.

+0

tak, to wydaje się idealną opcją! –

+0

jak mogę to zrobić? – frapontillo

+0

możesz obejrzeć http://developer.android.com/resources/articles/painless-threading.html w AsyncTask dla części wątków, umieścić okno dialogowe postępu w onPreExecute, odrzucić w onPostExecute. istnieje przykład zawyżania widoku w http://stackoverflow.com/questions/5292328/once-a-view-is-inflated-dynamically-as-a-w----button-attached-to-each- wiersz –

0

To nie jest idealne rozwiązanie, ale jak dotąd najbardziej zbliżone do tego, czego potrzebowałem: potrzebujemy trochę czasu między ustawieniem okna postępu (lub widoku komunikatu) i wywoływaniem funkcji setContentView(), aby okno dialogowe/wiadomość jest faktycznie wyświetlana.

Aby to osiągnąć, uruchamiam wątek wykonujący krótki tryb uśpienia(), a następnie wywołuję funkcję setContentView() w wątku interfejsu użytkownika.

Oto przykład z okna dialogowego postępu prac:

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

Moim głównym problemem jest to ze snu (20), który jest przybliżony i może nie wystarczyć na wszystkich maszynach. Działa to dla mnie, ale jeśli nie widzisz okna postępu, prawdopodobnie musisz wydłużyć czas snu.

Powiązane problemy