2012-02-06 25 views
9

Sé que hay docenas de preguntas sobre este error, pero ninguna de las soluciones propuestas parece aplicarse a mi problema, al menos eso veo.Android: el hijo especificado ya tiene un padre. Debe llamar a removeView() en el padre del hijo primero

Aquí está mi registro:

java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first. 
    at android.view.ViewGroup.addViewInner(ViewGroup.java:1976) 
    at android.view.ViewGroup.addView(ViewGroup.java:1871) 
    at android.view.ViewGroup.addView(ViewGroup.java:1851) 
    at com.android.internal.app.AlertController.setupView(AlertController.java:365) 
    at com.android.internal.app.AlertController.installContent(AlertController.java:206) 
    at android.app.AlertDialog.onCreate(AlertDialog.java:251) 
    at android.app.Dialog.dispatchOnCreate(Dialog.java:307) 
    at android.app.Dialog.show(Dialog.java:225) 
    at com.company.MyApp.MyActivity$7.onItemClick(MyActivity.java:240) 
    at android.widget.AdapterView.performItemClick(AdapterView.java:284) 
    at android.widget.ListView.performItemClick(ListView.java:3513) 
    at android.widget.AbsListView$PerformClick.run(AbsListView.java:1812) 
    at android.os.Handler.handleCallback(Handler.java:587) 
    at android.os.Handler.dispatchMessage(Handler.java:92) 
    at android.os.Looper.loop(Looper.java:130) 
    at android.app.ActivityThread.main(ActivityThread.java:3683) 
    at java.lang.reflect.Method.invokeNative(Native Method) 
    at java.lang.reflect.Method.invoke(Method.java:507) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597) 
    at dalvik.system.NativeStart.main(Native Method) 

Aquí es MyActivity.java onCreate(). Puede ver que se muestra un generador de cuadros y le dan algunos valores. Tanto myDialogLayout y myDialogBuilder son miembros de la clase privados

@Override 
public void onCreate(Bundle savedInstanceState) 
{ 
    // ... a bunch of init code... 

    // Create a dialog builder 
    myDialogLayout = getLayoutInflater().inflate(R.layout.leaving, null); 
    myDialogBuilder = new AlertDialog.Builder(this) 
     .setTitle("My Title") 
     .setView(myDialogLayout) 
     .setNegativeButton("Cancel", new DialogInterface.OnClickListener() 
     { 
      public void onClick(DialogInterface dialog, int which) {} 
     }); 

    // ... more code ... 
} 

Más tarde, cuando se hace clic en un botón determinado en mi actividad, la myDialogBuilder se personaliza un poco y luego se crea un cuadro de diálogo y muestra. myDialog es una variable de clase/miembro privado:

// Builder customized a bit 
myDialogBuilder 
    .setMessage("custom message here"); 

// Dialog created from Builder 
myDialog = myDialogBuilder.create(); 

// Dialog shown 
myDialog.show(); // <---- MyActivity.java Line: 240 

Así que en mi problema, al hacer clic en el botón de mi, el diálogo se ha creado correctamente. Pero después de presionar Cancelar en el cuadro de diálogo y luego presionar el botón nuevamente, aparece el error en el registro. Por alguna razón, la actividad no quiere que reutilice mi cuadro de diálogo myDialog. Es un miembro de la clase, por lo que es accesible desde el controlador onClick. Y el myDialog es un nuevo cuadro de diálogo cada vez que se hace clic en el botón, ya que se crea desde cero por myDialogBuilder.create() todo el tiempo.

¿Alguien sabe cuál es el problema? También intenté agregar en myDialog.dismiss() cuando se presiona el botón Cancelar, pero eso no hizo la diferencia.

Además, puede ver en mi myDialogBuilder que se utiliza un diseño XML personalizado para la vista del cuadro de diálogo. De acuerdo con el mensaje de error, sonaba como si quisiera que usara removeView() para evitar que la vista se use en el cuadro de diálogo. Pero myDialogBuilder.removeView() no es un método válido.

Respuesta

17

es myDialogLayout ¿variable de miembro de clase? Si es así, ya tiene un elemento primario desde la primera vez que muestra el cuadro de diálogo y luego crea un segundo cuadro de diálogo que también intenta ser el elemento primario de myDialogLayout. Intente crear una nueva instancia de myDialogLayout cada vez que abra el cuadro de diálogo.

+2

Esa fue la clave.Eliminé el 'myDialogLayout' de ser una variable de miembro de clase y, en su lugar, lo hice local para el método de controlador' onClick'. De esta forma, se trata de un nuevo diseño que se infla cada vez. ¡Gracias! –

3

Este es un ejemplo principal de por qué se recomienda el uso de los métodos onCreateDialog() onPrepareDialog().

Si está instanciando myDialog cada vez que se presiona su botón, está tratando de adjuntar las vistas desde el objeto de diálogo creado anteriormente (el anterior) al nuevo cuadro de diálogo.

Un cuadro de diálogo que se ha instanciado, mostrado y descartado, solo necesita mostrarse nuevamente, no recrearse por completo.

Suponiendo que myDialog es un miembro de la clase, intente comprobar si myDialog es nulo primero o no. Si no es nulo, muéstrelo de nuevo en lugar de ejemplificarlo por completo.

EDITAR - Aquí hay un ejemplo de lo que quiero decir, usando onPrepareDialog() y onCreateDialog(). On Create crea el diálogo, y cada vez que se muestra, se prepara y se obtiene una referencia de EditText utilizando findViewById y se borra el texto. Asumimos aquí que DIALOG_TEST es una constante entera, y que en layout/dialog_test.xml hay una vista EditText con id edittext.

@Override 
protected void onPrepareDialog(int id, Dialog dialog) { 

    switch(id){ 

    case DIALOG_TEST: 

     EditText mEditText = (EditText) dialog.findViewById(R.id.edittext); 
     mEditText.setText(""); 
     break; 
    } 

} 

@Override 
protected Dialog onCreateDialog(int id) { 

    AlertDialog.Builder builder = new AlertDialog.Builder(this); 

    switch(id){ 

    case DIALOG_TEST: 

     LinearLayout layout = (LinearLayout) ((LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.dialog_test, null); 

     builder.setTitle("Enter Something").setView(layout); 
     return builder.create(); 
    } 
} 
+0

El diálogo debe crearse porque su mensaje (y otros aspectos del mismo) son diferentes cada vez. –

+0

Los cuadros de diálogo tienen un método findViewById. Debe usar eso para obtener una referencia a las vistas y luego actualizarlas. – Maximus

+0

Sí, soy consciente de eso. También estoy alterando el comportamiento 'onClick' del botón positivo cada vez. No mencioné esto en la publicación original anterior solo para poder simplificar el ejemplo. –

Cuestiones relacionadas