12

este es mi caso: tengo una pantalla de inicio de sesión que abre otra actividad. En la Actividad Simplemente tengo:Mostrar progresoDialog al cargar el diseño con setContentView

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

El diseño es un poco pesado, causa que está hecha de algunos fragmentos, y tarda alrededor de 1,5 segundos en cargar. Ahora, ¿cómo puedo mostrar un ProgressDialog mientras que setContentView termina de inflar el diseño? Lo he intentado con AsyncTask poniendo el setContentView en el doInBackground, pero, por supuesto, eso no se puede hacer, ya que la interfaz de usuario solo se puede actualizar desde el subproceso de la interfaz de usuario. Necesito llamar al setContentView en el hilo de la interfaz de usuario, pero ¿dónde debo mostrar/descartar el ProgressDialog?

Agradezco su ayuda.

Fra.

EDIT: He seguido @ sugerencia anterior de JohnBoker, este es el código que tengo ahora:

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

La fila

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

me da el 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. 
+1

¿Qué hay de inflarlo manualmente en el método doInBackground? –

+0

Sería interesante ver su archivo de diseño, lo mejor es intentar y optimizar eso, creo. ¿Son los 1.5 segundos en el emulador o en el teléfono/tableta? – Marty

+0

Hola, en realidad es de aproximadamente 1,5 segundos en el transformador eeePad. ¿Cómo puedo inflarlo manualmente? El diseño es bastante largo para ser publicado aquí, pero tiene alrededor de 10-12 diseños lineales y un fragmento real (por ahora). – frapontillo

Respuesta

8

Decidí hacer una respuesta completa aquí para futuros lectores.

Después de varias horas pasadas en el tema, me di cuenta de que la cuestión es que estoy tratando de hacer dos cosas:

  1. inflar un diseño, que es una operación que necesita ser hecha en el UI thread por diseño.
  2. que muestra un cuadro de diálogo (ProgressDialog, en realidad, pero esto no cambia el resultado ), que se puede hacer desde sólo el hilo de interfaz de usuario, ya que Servicios no pueden mostrar ningún diálogo.

Por lo tanto, como ambas llamadas se hacen desde el subproceso UI (onCreate o AsyncTask no hace diferencia, sigue siendo el subproceso UI), el primero impide que el segundo se muestre correctamente. La línea inferior es: este problema no se puede resolver en Android en este momento. Esperemos que podamos obtener algunas API mejores para interactuar con la interfaz de usuario, porque las que nos encantan.

Voy a resolver este problema cambiando el diseño y haciéndolo más ligero (¡si es posible!). ¡Gracias a todos!

+1

Puede inflar manualmente las vistas en un hilo separado si usa un HandlerThread. Básicamente, el hilo para inflar las vistas requiere un Looper. – techiServices

8

I tenía el mismo problema al crear una vista gruesa, lo que hice fue poner una distribución lineal solo en el archivo xml y se llamó setConten tView sobre eso, luego creé la vista real en la asynctask y agregué la vista dinámicamente a la distribución lineal.

Este método parece funcionar y pude poner un diálogo de progreso durante el proceso.

+0

¡sí, eso parece una opción perfecta! –

+0

¿cómo puedo hacer esto? – frapontillo

+0

Puede consultar http://developer.android.com/resources/articles/painless-threading.html en AsyncTask para la parte de subprocesamiento, poner el cuadro de diálogo de progreso en onPreExecute, descartar en onPostExecute. hay un ejemplo de inflar una vista en http://stackoverflow.com/questions/5292328/once-a-view-is-inflated-dynamically-as-a-list-with-a-button-attached-to-each- fila –

0

Esta no es una solución perfecta, pero hasta ahora la más cercana a lo que necesitaba: necesitamos un tiempo entre configurar el diálogo de progreso (o vista de mensaje o lo que sea) y llamar a setContentView(), para que el diálogo/el mensaje se muestra realmente.

Para lograr eso, comienzo un subproceso que realiza un breve descanso(), luego llamando a setContentView() en el subproceso de la interfaz de usuario.

Aquí se muestra un ejemplo de un diálogo de progreso:

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

Mi principal problema con esto es el sueño (20), que es una aproximación, y puede no ser suficiente en todas las máquinas. Funciona para mí, pero si no ve el diálogo de progreso, probablemente tenga que aumentar la duración del sueño.

Cuestiones relacionadas