way una two tutorial studio example databindingutil databinding data crear clase java android android-layout getter

java - una - databinding android



La vista getWidth() y getHeight() devuelve 0 (10)

Como afirma Ian en este hilo de desarrolladores de Android :

De todos modos, el trato es que el diseño del contenido de una ventana ocurre después de que todos los elementos se construyen y se agregan a sus vistas principales. Tiene que ser así, porque hasta que sepa qué componentes contiene una vista y qué contienen, y así sucesivamente, no hay una forma sensata de distribuirla.

Línea inferior, si llama a getWidth (), etc. en un constructor, devolverá cero. El procedimiento consiste en crear todos los elementos de su vista en el constructor, luego esperar a que se llame al método onSizeChanged () de su Vista: ahí es cuando descubre su tamaño real, y así es cuando configura los tamaños de sus elementos GUI.

Tenga en cuenta también que a veces se llama a onSizeChanged () con parámetros de cero; verifique este caso y regrese inmediatamente (para que no obtenga una división por cero al calcular su diseño, etc.). Algún tiempo después se llamará con los valores reales.

Estoy creando dinámicamente todos los elementos de mi proyecto de Android. Estoy tratando de obtener el ancho y la altura de un botón para poder rotar ese botón. Sólo estoy tratando de aprender a trabajar con el lenguaje Android. Sin embargo, devuelve 0.

Investigué un poco y vi que es necesario hacerlo en otro lugar que no sea el método onCreate() . Si alguien me puede dar un ejemplo de cómo hacerlo, sería genial.

Aquí está mi código actual:

package com.animation; import android.app.Activity; import android.os.Bundle; import android.view.animation.Animation; import android.view.animation.LinearInterpolator; import android.view.animation.RotateAnimation; import android.widget.Button; import android.widget.LinearLayout; public class AnimateScreen extends Activity { //Called when the activity is first created. @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); LinearLayout ll = new LinearLayout(this); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); layoutParams.setMargins(30, 20, 30, 0); Button bt = new Button(this); bt.setText(String.valueOf(bt.getWidth())); RotateAnimation ra = new RotateAnimation(0,360,bt.getWidth() / 2,bt.getHeight() / 2); ra.setDuration(3000L); ra.setRepeatMode(Animation.RESTART); ra.setRepeatCount(Animation.INFINITE); ra.setInterpolator(new LinearInterpolator()); bt.startAnimation(ra); ll.addView(bt,layoutParams); setContentView(ll); }

Cualquier ayuda es apreciada.


El problema básico es que tiene que esperar la fase de dibujo para las mediciones reales (especialmente con valores dinámicos como wrap_content o match_parent ), pero generalmente esta fase no se ha completado hasta onResume() . Así que necesitas una solución para esperar esta fase. Existen diferentes soluciones posibles para esto:


1. Escuche los eventos Draw / Layout: ViewTreeObserver

Un ViewTreeObserver se activa para diferentes eventos de dibujo. Por lo general, el OnGlobalLayoutListener es lo que desea para obtener la medición, por lo que se llamará al código del oyente después de la fase de diseño, para que las mediciones estén listas:

view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { view.getViewTreeObserver().removeOnGlobalLayoutListener(this); view.getHeight(); //height is ready } });

Nota: el oyente se eliminará de inmediato, ya que de lo contrario se activará en cada evento de diseño. Si tiene que admitir aplicaciones SDK Lvl <16, use esto para anular el registro del oyente:

public void removeGlobalOnLayoutListener (ViewTreeObserver.OnGlobalLayoutListener victim)


2. Agregue un ejecutable a la cola de diseño: View.post ()

No muy conocida y mi solución favorita. Básicamente, simplemente use el método de publicación de la Vista con su propio ejecutable. Básicamente, esto pone en cola su código después de la medida de la vista, el diseño, etc., como lo indica Romain Guy :

La cola de eventos de IU procesará los eventos en orden. Después de invocar setContentView (), la cola de eventos contendrá un mensaje solicitando una retransmisión, por lo que todo lo que publique en la cola sucederá después del paso de diseño

Ejemplo:

final View view=//smth; ... view.post(new Runnable() { @Override public void run() { view.getHeight(); //height is ready } });

La ventaja sobre ViewTreeObserver :

  • su código solo se ejecuta una vez y no tiene que deshabilitar el Observer después de la ejecución, lo que puede ser una molestia
  • sintaxis menos verbosa

Referencias:

  • https://.com/a/3602144/774398
  • https://.com/a/3948036/774398


3. Sobrescribir el método onLayout de Views

Esto solo es práctico en ciertas situaciones cuando la lógica se puede encapsular en la vista misma, de lo contrario, se trata de una sintaxis bastante detallada y engorrosa.

view = new View(this) { @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); view.getHeight(); //height is ready } };

También tenga en cuenta que se llamará a onLayout muchas veces, así que considere lo que hace en el método o desactive su código después de la primera vez.


4. Comprobar si ha pasado por la fase de diseño.

Si tiene un código que se está ejecutando varias veces al crear la interfaz de usuario, puede usar el siguiente método de soporte v4 lib:

View viewYouNeedHeightFrom = ... ... if(ViewCompat.isLaidOut(viewYouNeedHeightFrom)) { viewYouNeedHeightFrom.getHeight(); }

Devuelve verdadero si la vista ha pasado por al menos un diseño desde que se adjuntó o se separó por última vez de una ventana.


Adicional: Obtención de mediciones definidas estáticamente.

Si basta con obtener la altura / anchura definida estáticamente, puede hacerlo con:

Pero tenga en cuenta que esto podría ser diferente al ancho / alto real después de dibujar. El javadoc describe perfectamente la diferencia:

El tamaño de una vista se expresa con un ancho y una altura. Una vista en realidad posee dos pares de valores de ancho y alto.

El primer par se conoce como ancho medido y altura medida. Estas dimensiones definen el tamaño que desea que tenga una vista dentro de su elemento primario (consulte Diseño para obtener más detalles). Las dimensiones medidas se pueden obtener llamando a getMeasuredWidth () y getMeasuredHeight ().

El segundo par se conoce simplemente como ancho y alto, o a veces ancho de dibujo y alto de dibujo. Estas dimensiones definen el tamaño real de la vista en pantalla, en el momento del dibujo y después del diseño. Estos valores pueden, pero no tienen que ser, diferentes del ancho y la altura medidos. El ancho y la altura se pueden obtener llamando a getWidth () y getHeight ().


Está llamando a getWidth() demasiado pronto. La interfaz de usuario aún no se ha dimensionado ni colocado en la pantalla.

De todos modos, dudo que quieras hacer lo que estás haciendo, ya que los widgets que se animan no cambian sus áreas donde se puede hacer clic, por lo que el botón seguirá respondiendo a los clics en la orientación original, independientemente de cómo haya girado.

Dicho esto, puede usar un recurso de dimensión para definir el tamaño del botón, luego hacer referencia a ese recurso de dimensión desde su archivo de diseño y su código fuente, para evitar este problema.


Extensión para obtener altura en Kotlin dinámicamente.

Uso:

view.height { Log.i("Info", "Here is your height:" + it) }

Implementación:

fun <T : View> T.height(function: (Int) -> Unit) { if (height == 0) viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { override fun onGlobalLayout() { viewTreeObserver.removeOnGlobalLayoutListener(this) function(height) } }) else function(height) }


Gone vistas devuelve 0 como altura si la aplicación en el fondo. Este mi código (1oo% funciona)

fun View.postWithTreeObserver(postJob: (View, Int, Int) -> Unit) { viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { override fun onGlobalLayout() { val widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) val heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) measure(widthSpec, heightSpec) postJob(this@postWithTreeObserver, measuredWidth, measuredHeight) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { @Suppress("DEPRECATION") viewTreeObserver.removeGlobalOnLayoutListener(this) } else { viewTreeObserver.removeOnGlobalLayoutListener(this) } } }) }


Nosotros podemos usar

@Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); //Here you can get the size! }


Preferiría usar OnPreDrawListener() lugar de addOnGlobalLayoutListener() , ya que se llama antes.

view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { if (view.getViewTreeObserver().isAlive()) view.getViewTreeObserver().removeOnPreDrawListener(this); // put your code here return false; } });


Si necesita obtener el ancho de algún widget antes de que se muestre en la pantalla, puede usar getMeasuredWidth () o getMeasuredHeight ().

myImage.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); int width = myImage.getMeasuredWidth(); int height = myImage.getMeasuredHeight();


Un trazador de líneas si está utilizando RxJava y RxBindings . Enfoque similar sin el repetitivo. Esto también resuelve el truco para suprimir las advertencias como en la respuesta de Tim Autin .

RxView.layoutChanges(yourView).take(1) .subscribe(aVoid -> { // width and height have been calculated here });

Eso es todo. No es necesario cancelar la suscripción, incluso si nunca se llama.


Utilicé esta solución, que creo que es mejor que enWindowFocusChanged (). Si abre un DialogFragment, luego gira el teléfono, solo se llamará a WindowsWindFocusChanged cuando el usuario cierre el cuadro de diálogo):

yourView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { // Ensure you call it only once : yourView.getViewTreeObserver().removeGlobalOnLayoutListener(this); // Here you can get the size :) } });

Edición: como removeGlobalOnLayoutListener está en desuso, ahora debe hacer:

@SuppressLint("NewApi") @SuppressWarnings("deprecation") @Override public void onGlobalLayout() { // Ensure you call it only once : if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) { yourView.getViewTreeObserver().removeOnGlobalLayoutListener(this); } else { yourView.getViewTreeObserver().removeGlobalOnLayoutListener(this); } // Here you can get the size :) }