Cómo dimensionar una vista de Android según las dimensiones de su padre

104

¿Cómo puedo cambiar el tamaño de una vista en función del tamaño de su diseño principal? Por ejemplo, tengo una RelativeLayoutque ocupa toda la pantalla y quiero una vista infantil, digamosImageView , ocupe toda la altura y la mitad del ancho.

He intentado anular en todo onMeasure, onLayout, onSizeChanged, etc, y no pude conseguir que funcione ....

Mitch
fuente
Es posible hacerlo usando ConstraintLayout, verifique esto: stackoverflow.com/questions/37318228/…
Ali Nem

Respuestas:

124

No sé si alguien todavía está leyendo este hilo o no, pero la solución de Jeff solo te llevará a la mitad (un poco literalmente). Lo que hará su onMeasure es mostrar la mitad de la imagen en la mitad del padre. El problema es que llamar a super.onMeasure antes de setMeasuredDimensionque medirá todos los elementos secundarios de la vista en función del tamaño original, luego simplemente cortará la vista por la mitad cuando elsetMeasuredDimension cambie tamaño.

En su lugar, debe llamar setMeasuredDimension(según sea necesario para una anulación de onMeasure) y proporcionar una nueva LayoutParamspara su vista, luego llamar super.onMeasure. Recuerde, LayoutParamsse derivan del tipo principal de su vista, no del tipo de su vista.

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
   int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
   int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
   this.setMeasuredDimension(parentWidth/2, parentHeight);
   this.setLayoutParams(new *ParentLayoutType*.LayoutParams(parentWidth/2,parentHeight));
   super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

Creo que la única vez que tendrá problemas con el padre LayoutParamses si el padre es un AbsoluteLayout(lo cual está desaprobado pero a veces es útil).

Vic
fuente
12
En mi experiencia, esto rara vez funciona, especialmente. cuando se invoca fill / match_parent o weights. Es mejor invocar a measureChildren después de setMeasuredDimension (por ejemplo, con MeasureSpec.makeMeasureSpec (newWidth, MeasureSpec.AT_MOST)).
M. Schenk
1
¿ MeasureSpec.getSize(widthMeasureSpec)Realmente da el ancho correcto del padre? Para mí, solo devuelve anchos aleatorios que están bastante cerca del ancho real, pero no exactos.
Konsumierer
1
@ M.Schenk lo tiene. Tengo un VideoView dentro de un diseño ponderado. Necesito que el ancho sea un porcentaje del tamaño de la pantalla y necesito mantener la relación de aspecto. Esto alarga el video, por wrap_contentlo que no funcionará. La respuesta de Vic no funciona para este escenario, pero el comentario de @ M.Schenk sí. También ahorra una pasada de diseño adicional. También debe publicarlo como una respuesta para la visibilidad.
dokkaebi
7
¿La llamada super.onMeasure()simplemente anulará y anulará los efectos de la llamada anterior a this.setMeasuredDimension()?
Spinner
1
También tengo curiosidad, como mencionó @Spinner, por qué la llamada a super.onMeasure()no anula los cálculos anteriores.
jwir3
39

Puede resolver esto creando una Vista personalizada y anulando el método onMeasure (). Si siempre usa "fill_parent" para layout_width en su xml, entonces el parámetro widthMeasureSpec que se pasa al método onMeasusre () debe contener el ancho del padre.

public class MyCustomView extends TextView {

    public MyCustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
        int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
        this.setMeasuredDimension(parentWidth / 2, parentHeight);
    }
}   

Su XML se vería así:

<LinearLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <view
        class="com.company.MyCustomView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>
Jeff
fuente
2
@jeff => ¿por qué parentWidth / 2 ??
Saeed-rz
6
@Leon_SFS: La pregunta pide eso: "altura total y la mitad del ancho"
EdgarT
28

Descubrí que es mejor no perder el tiempo configurando las dimensiones medidas usted mismo. En realidad, hay un poco de negociación entre las opiniones de los padres y los hijos y no desea volver a escribir todo ese código .

Sin embargo, lo que puede hacer es modificar las MeasureSpecs y luego llamar a super con ellas. Su vista nunca sabrá que está recibiendo un mensaje modificado de su padre y se encargará de todo por usted:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
    int myWidth = (int) (parentHeight * 0.5);
    super.onMeasure(MeasureSpec.makeMeasureSpec(myWidth, MeasureSpec.EXACTLY), heightMeasureSpec);
}
Phil Kulak
fuente
16

Cuando define un diseño y una vista en XML, puede especificar el ancho y el alto del diseño de una vista para que sea contenido envolvente o primario. Ocupar la mitad del área es un poco más difícil, pero si tuviera algo que quisiera en la otra mitad, podría hacer algo como lo siguiente.

   <LinearLayout android:layout_width="fill_parent"
                android:layout_height="fill_parent">
        <ImageView android:layout_height="fill_parent"
                 android:layout_width="0dp"
                 android:layout_weight="1"/>
        <ImageView android:layout_height="fill_parent"
                 android:layout_width="0dp"
                 android:layout_weight="1"/>
   </LinearLayout>

Dar el mismo peso a dos cosas significa que se estirarán para ocupar la misma proporción de la pantalla. Para obtener más información sobre diseños, consulte los documentos de desarrollo.

Cheryl Simon
fuente
No recomiendo esto, pero ... como un truco, si usa el enfoque anterior, podría convertir una de esas vistas en "ficticia" y configurar android: visibilidad = "invisible" para llenar la mitad del área
RickNotFred
¿Cuál es el propósito de usar solo la mitad del área? ¿Está pensando en mostrar algo en la otra mitad? Lo que haga con el área restante informará el mejor enfoque
RickNotFred
esto definitivamente debería ser el n. ° 1: si puede hacerlo en su xml, ¿por qué hacerlo en su java?
fandang
9

Creo que el enfoque XML de Mayras puede venir bien. Sin embargo, es posible hacerlo más preciso, con una sola vista configurando el valor weightSum. Ya no llamaría a esto un truco, pero en mi opinión, el enfoque más sencillo:

<LinearLayout android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:weightSum="1">
    <ImageView android:layout_height="fill_parent"
               android:layout_width="0dp"
               android:layout_weight="0.5"/>
</LinearLayout>

De esta manera, puede usar cualquier peso, 0.6 por ejemplo (y centrado) es el peso que me gusta usar para los botones.

pinkwerther
fuente
3

Prueba esto

int parentWidth = ((parentViewType)childView.getParent()).getWidth();
int parentHeight = ((parentViewType)childView.getParent()).getHeight();

entonces puede usar LinearLayout.LayoutParams para configurar los parámetros de chileView

LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(childWidth,childLength);
childView.setLayoutParams(params);
Abhishek Gupta
fuente
2

Ahora puede usar PercentRelativeLayout. ¡Auge! Problema resuelto.

androidguy
fuente
2
PercentRelativeLayout quedó obsoleto en el nivel de API 26.0.0-beta1.
dzikovskyy
¿Qué quiere decir "en el nivel de API"? ¿Te refieres al número de versión de la biblioteca? PRL está en la biblioteca de soporte, no tiene nivel de API.
androidguy
Por nivel de API me refiero a la versión de Android. Información de aquí developer.android.com/reference/android/support/percent/… . Más información sobre el nivel de API aquí developer.android.com/guide/topics/manifest/…
dzikovskyy
Aún puede usar esta clase a menos que, por alguna razón, necesite la versión 26.0.0 o superior.
androidguy
1

Roman, si desea hacer su diseño en código Java (descendiente de ViewGroup), es posible. El truco es que debe implementar los métodos onMeasure y onLayout. El onMeasure se llama primero y necesita "medir" la subvista (dimensionándola efectivamente al valor deseado) allí. Necesita cambiar su tamaño nuevamente en la llamada onLayout. Si no logra hacer esta secuencia o no llama a setMeasuredDimension () al final de su código onMeasure, no obtendrá resultados. Por qué está diseñado de una manera tan complicada y frágil se me escapa.

Pavel Lahoda
fuente
1

Si el otro lado está vacío. Supongo que la forma más sencilla sería agregar una vista vacía (por ejemplo, un diseño lineal) y luego establecer los anchos de ambas vistas en fill_parent y ambos pesos en 1.

Esto funciona en un LinearLayout ...

jpm
fuente
0

Es algo como esto:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.company.myapp.ActivityOrFragment"
    android:id="@+id/activity_or_fragment">

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/linearLayoutFirst"
    android:weightSum="100">   <!-- Overall weights sum of children elements -->

    <Spinner
        android:layout_width="0dp"   <!-- It's 0dp because is determined by android:layout_weight -->
        android:layout_weight="50"
        android:layout_height="wrap_content"
        android:id="@+id/spinner" />

</LinearLayout>


<LinearLayout
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:layout_below="@+id/linearLayoutFirst"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true"
    android:id="@+id/linearLayoutSecond">

    <EditText
        android:layout_height="wrap_content"
        android:layout_width="0dp"
        android:layout_weight="75"
        android:inputType="numberDecimal"
        android:id="@+id/input" />

    <TextView
        android:layout_height="wrap_content"
        android:layout_width="0dp"
        android:layout_weight="25"
        android:id="@+id/result"/>

</LinearLayout>
</RelativeLayout>
Mario
fuente
0

He estado luchando con esta pregunta durante mucho tiempo, quiero cambiar el ancho del cajón de mi DrawerLayout, que es el segundo hijo de su padre. Recientemente lo descubrí, pero no sé si hay una idea mejor.

override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
    super.onLayout(changed, l, t, r, b)
    (getChildAt(1).layoutParams as LayoutParams).width = measuredWidth/2
}
Juan 6
fuente