GetWidth () y getHeight () de la vista devuelve 0
Estoy creando todos los elementos en mi proyecto androide dinámicamente. Estoy intentando conseguir la anchura y la altura de un botón de modo que pueda girar ese botón alrededor. Sólo estoy tratando de aprender a trabajar con el lenguaje android. Sin embargo, devuelve 0.
Hice algunas investigaciones y vi que tiene que hacerse en algún otro lugar que no sea en el método onCreate()
. Si alguien me puede dar un ejemplo de cómo hacerlo, eso sería genial.
- Carga de paquetes a / de archivos en Android
- Mapa de bits de Android: convierte píxeles transparentes a un color
- ¿Cómo puedo incluir correctamente un archivo jar externo para un plugin cordova?
- Android EXTRA_MESSAGE no reconocido dentro de Recycler View Adapter
- System.out.println en la prueba de Android
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.
- Android: cómo construir desde la fecha desde la fecha del servidor
- Desplazar ficha - NullPointerException - Android
- ¿Los hilos son lo suficientemente fiables para calcular segundos?
- Facebook, setReadPermissions y setPublishPermissions
- Recyclerview - Superposición de artículos de abajo hacia arriba
- Admob No hay relleno en el servidor de anuncios: no se pudo cargar el anuncio: 3
- Salida de un carácter por dos teclas en el teclado de Android
- Esfera 3D OpenGL
Usted está llamando a getWidth()
demasiado pronto. La interfaz de usuario no se ha dimensionado y presentado en la pantalla todavía.
Dudo que quieras hacer lo que estás haciendo, de todos modos – los widgets que se animan no cambian las áreas que se pueden hacer clic, por lo que el botón seguirá respondiendo a los clics en la orientación original independientemente de cómo se haya girado.
Dicho esto, puede utilizar un recurso de dimensión para definir el tamaño del botón y, a continuación, hacer referencia a ese recurso de dimensión desde el archivo de diseño y el código fuente para evitar este problema.
El problema básico es que hay que esperar la fase de dibujo para las mediciones reales (especialmente con valores dinámicos como wrap_content
o match_parent
), pero normalmente esta fase no se ha terminado hasta onResume()
. Por lo tanto, necesita una solución para esperar esta fase. Existen diferentes soluciones posibles a esto:
1. Escuche los Eventos de Dibujo / Diseño: 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 el código en el oyente será llamado después de la fase de diseño, por lo que las medidas 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á inmediatamente porque de lo contrario se disparará en cada evento de diseño. Si tiene que soportar aplicaciones SDK Lvl <16 use esto para anular el registro del oyente:
public void removeGlobalOnLayoutListener (ViewTreeObserver.OnGlobalLayoutListener victim)
2. Agregue un runnable a la cola de diseño: View.post ()
No muy conocido y mi solución favorita. Básicamente, sólo tiene que utilizar el método de post de la vista con su propio runnable. Esto básicamente pone en cola su código después de la medida de la vista, el diseño, etc, según lo declarado por Romain Guy :
La cola de eventos de la interfaz de usuario procesará los sucesos en orden. Después de invocar a setContentView (), la cola de eventos contendrá un mensaje solicitando un relayout, de modo que cualquier cosa que publique en la cola ocurrirá después del pase 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 sólo se ejecuta una vez y no tiene que deshabilitar el observador después de la ejecución que puede ser una molestia
- Menos sintaxis detallada
Referencias:
3. Sobrescribir el método onLayout de Views
Esto sólo es práctico en cierta situación cuando la lógica puede ser encapsulada en la propia vista, de lo contrario, esta es 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 la mente, que onLayout se llamará muchas veces, por lo que ser considerado lo que haces en el método, o deshabilitar el código después de la primera vez
4. Compruebe si ha pasado por la fase de diseño
Si tiene código que se está ejecutando varias veces al crear el ui, puede utilizar el siguiente método de soporte v4 lib:
View viewYouNeedHeightFrom = ... ... if(ViewCompat.isLaidOut(viewYouNeedHeightFrom)) { viewYouNeedHeightFrom.getHeight(); }
Devuelve true si la vista ha pasado por, al menos, un diseño desde que se adjuntó o se separó de una ventana por última vez.
Adicional: Obtención de medidas estáticamente definidas
Si basta con obtener la altura / ancho definido estáticamente, sólo puede hacer esto con:
-
View.getMeasuredWidth()
-
View.getMeasuredHeigth()
Pero tenga en cuenta que esto podría ser diferente a la anchura / altura real después del dibujo. El javadoc describe la diferencia perfectamente:
El tamaño de una vista se expresa con una anchura y una altura. Una vista realmente posee dos pares de valores de anchura y altura.
El primer par se conoce como anchura medida y altura medida. Estas dimensiones definen el tamaño de una vista que quiere estar dentro de su matriz (vea el esquema para más detalles). Las dimensiones medidas se pueden obtener llamando a getMeasuredWidth () y getMeasuredHeight ().
El segundo par se conoce simplemente como anchura y altura, o a veces el ancho de dibujo y la altura de dibujo. Estas dimensiones definen el tamaño real de la vista en pantalla, en el tiempo de dibujo y después del diseño. Estos valores pueden, pero no tienen que ser, diferentes de la anchura y altura medidas. El ancho y la altura se pueden obtener llamando a getWidth () y getHeight ().
Nosotros podemos usar
@Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); //Here you can get the size! }
He utilizado esta solución, que creo que es mejor que onWindowFocusChanged (). Si abre un DialogFragment, luego gire el teléfono, onWindowFocusChanged será llamado sólo cuando el usuario cierre el 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 :) } });
Editar: como removeGlobalOnLayoutListener está obsoleto, 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) { container.getViewTreeObserver().removeOnGlobalLayoutListener(this); } else { container.getViewTreeObserver().removeGlobalOnLayoutListener(this); } // Here you can get the size :) }
Como indica Ian en este hilo de desarrolladores de Android :
De todos modos, el trato es que el diseño del contenido de una ventana sucede después de que todos los elementos se construyen y se agregan a sus vistas de los padres. Tiene que ser de esta manera, porque hasta que sepa qué componentes contiene una vista, y lo que contienen, etc., no hay ninguna forma razonable de poder distribuirlo.
En resumen, si llama a getWidth () etc. en un constructor, devolverá cero. El procedimiento consiste en crear todos los elementos de vista en el constructor y luego esperar a que el método onSizeChanged () de View se llame. En primer lugar, averigua su tamaño real, de modo que es cuando configura los tamaños de los elementos de la GUI.
Tenga en cuenta también que onSizeChanged () a veces se llama con parámetros de cero – compruebe este caso, y regrese inmediatamente (de modo que no obtenga una división por cero al calcular su diseño, etc.). Algún tiempo después será llamado con los valores reales.
Si necesita obtener el ancho de algún widget antes de que aparezca en la pantalla, puede utilizar getMeasuredWidth () o getMeasuredHeight ().
myImage.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); int width = myImage.getMeasuredWidth(); int height = myImage.getMeasuredHeight();
Prefiero usar OnPreDrawListener()
lugar de addOnGlobalLayoutListener()
, ya que se llama anteriormente.
view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { view.getViewTreeObserver().removeOnPreDrawListener(this); // put your code here return false; } });
- Obtener la vista de raíz de la actividad actual
- ¿Cómo configurar layout_gravity mediante programación?