¿Por qué LayoutInflater ignora los parámetros de diseño layout_width y layout_height que he especificado?

168

He tenido graves problemas para hacer que LayoutInflater funcione como se esperaba, y también lo hicieron otras personas: ¿Cómo usar layoutinflator para agregar vistas en tiempo de ejecución? .

¿Por qué LayoutInflater ignora los parámetros de diseño que he especificado? Por ejemplo, ¿por qué no se respetan los valores layout_widthy layout_heightvalores de mis recursos XML?

andig
fuente
Es cierto, ¿duele o elegí el lugar equivocado? Solo pensé en compartir mis resultados. Tengo que admitir tutoriales no se mencionan específicamente de la página de preguntas frecuentes ..
andig
3
ver el último párrafo de la primera pregunta en faq. publíquelo como una pregunta y luego publique el tutorial como respuesta. por supuesto, puede editar esta pregunta cortando y pegando el tutorial en la respuesta. Votaré si arreglas esto.
piedras grandes

Respuestas:

381

He investigado este problema, refiriéndome a los documentos de LayoutInflater y configurando un pequeño proyecto de demostración de muestra. Los siguientes tutoriales muestran cómo llenar dinámicamente un diseño usando LayoutInflater.

Antes de comenzar, vea cómo se ven los LayoutInflater.inflate()parámetros:

  • recurso : ID para cargar un recurso de diseño XML (por ejemplo, R.layout.main_page)
  • root : Vista opcional para ser el padre de la jerarquía generada (si attachToRootes true), o simplemente un objeto que proporciona un conjunto de LayoutParamsvalores para la raíz de la jerarquía devuelta (si attachToRootes false).
  • attachToRoot : ¿Se debe adjuntar la jerarquía inflada al parámetro raíz? Si es falso, la raíz solo se usa para crear la subclase correcta de LayoutParamsla vista raíz en el XML.

  • Devuelve : La vista raíz de la jerarquía inflada. Si la raíz fue suministrada y attachToRootes true, esta es la raíz; de lo contrario, es la raíz del archivo XML inflado.

Ahora para el diseño de muestra y el código.

Diseño principal ( main.xml):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</LinearLayout>

Se agrega a este contenedor un TextView separado, visible como un pequeño cuadrado rojo si los parámetros de diseño se aplican con éxito desde XML ( red.xml):

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="25dp"
    android:layout_height="25dp"
    android:background="#ff0000"
    android:text="red" />

Ahora LayoutInflaterse usa con varias variaciones de parámetros de llamada

public class InflaterTest extends Activity {

    private View view;

    @Override
    public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);

      setContentView(R.layout.main);
      ViewGroup parent = (ViewGroup) findViewById(R.id.container);

      // result: layout_height=wrap_content layout_width=match_parent
      view = LayoutInflater.from(this).inflate(R.layout.red, null);
      parent.addView(view);

      // result: layout_height=100 layout_width=100
      view = LayoutInflater.from(this).inflate(R.layout.red, null);
      parent.addView(view, 100, 100);

      // result: layout_height=25dp layout_width=25dp
      // view=textView due to attachRoot=false
      view = LayoutInflater.from(this).inflate(R.layout.red, parent, false);
      parent.addView(view);

      // result: layout_height=25dp layout_width=25dp 
      // parent.addView not necessary as this is already done by attachRoot=true
      // view=root due to parent supplied as hierarchy root and attachRoot=true
      view = LayoutInflater.from(this).inflate(R.layout.red, parent, true);
    }
}

Los resultados reales de las variaciones de los parámetros están documentados en el código.

SINOPSIS: Llamar LayoutInflatersin especificar la raíz conduce a inflar la llamada ignorando los parámetros de diseño del XML. Llamar a inflar con raíz no es igual nully attachRoot=truecarga los parámetros de diseño, pero devuelve el objeto raíz nuevamente, lo que evita más cambios de diseño en el objeto cargado (a menos que pueda encontrarlo usando findViewById()). Por lo tanto, la convención de llamadas que probablemente le gustaría usar es esta:

loadedView = LayoutInflater.from(context)
                .inflate(R.layout.layout_to_load, parent, false);

Para ayudar con los problemas de diseño, se recomienda encarecidamente el Inspector de diseño .

andig
fuente
20
Excelente respuesta He estado trabajando en Android durante 3 años y contando y todavía tenía algo que aprender de esto.
Reuben Scratton
Hola, ¿hay alguna manera de hacer lo mismo con el código java en lugar de inflar con R.layout? Porque no tengo diseño. He creado la vista con código java y quiero agregar la misma vista 300 veces.
Raj
1
@andig: Activityes una subclase de Contexty los ejemplos de la respuesta están en el alcance de Activity, por lo que para simplificar, reemplacé getBaseContext()por this, en aras de esta respuesta, es equivalente.
Marcin Orlowski
55
container tiene layout_width y layout_height establecido en "match_parent"; luego el comentario dice "// resultado: layout_height = wrap_content layout_width = match_parent". ¿Me estoy perdiendo algo?
Marian Paździoch
1
¡Gracias por investigar y compartir los resultados! Además, secundo la pregunta de Marian.
LarsH
5

andig tiene razón en que una razón común para que LayoutInflater ignore sus layout_params sería porque no se especificó una raíz. Mucha gente piensa que puede pasar nulo por raíz. Esto es aceptable para algunos escenarios, como un diálogo, en el que no tiene acceso a root en el momento de la creación. Sin embargo, una buena regla a seguir es que si tiene root, déselo a LayoutInflater.

Escribí una publicación de blog en profundidad sobre esto que puedes consultar aquí:

https://www.bignerdranch.com/blog/understanding-androids-layoutinflater-inflate/

seanjfarrell
fuente
1
This is acceptable for a few scenarios such as a dialogeso era todo lo que necesitaba
rookieDeveloper
0

Quiero agregar a la respuesta principal anterior
Intenté seguirlo, pero mi recyclerView comenzó a estirar cada elemento a una pantalla.
Tuve que agregar la siguiente línea después de inflar para alcanzar el objetivo

itemLayoutView.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT));

Ya agregué estos parámetros por xml pero no funcionó correctamente
y con esta línea todo está bien

Kirguduck
fuente