type studio gridlayout examples ejemplo android android-layout android-widget android-ui android-xml

studio - type of layout android



Android getMeasuredHeight devuelve valores incorrectos. (7)

Haz eso:

frame.measure(0, 0); final int w = frame.getMeasuredWidth(); final int h = frame.getMeasuredHeight();

Resuelto

¡Estoy tratando de determinar la dimensión real en píxeles de algunos elementos de UI!

Esos elementos están inflados desde un archivo .xml y se inicializan con ancho y alto de inmersión para que la GUI finalmente admita múltiples tamaños de pantalla y dpi ( según lo recomendado por las especificaciones de Android ).

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="150dip" android:orientation="vertical"> <ImageView android:id="@+id/TlFrame" android:layout_width="110dip" android:layout_height="90dip" android:src="@drawable/timeline_nodrawing" android:layout_margin="0dip" android:padding="0dip"/></LinearLayout>

Este xml anterior representa un marco. Pero sí agrego muchos dinámicamente dentro de un diseño horizontal que describo aquí:

<HorizontalScrollView android:id="@+id/TlScroller" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:layout_margin="0dip" android:padding="0dip" android:scrollbars="none" android:fillViewport="false" android:scrollbarFadeDuration="0" android:scrollbarDefaultDelayBeforeFade="0" android:fadingEdgeLength="0dip" android:scaleType="centerInside"> <!-- HorizontalScrollView can only host one direct child --> <LinearLayout android:id="@+id/TimelineContent" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:layout_margin="0dip" android:padding="0dip" android:scaleType="centerInside"/> </HorizontalScrollView >

El método definido para agregar un marco dentro de mi código java:

private void addNewFrame() { LayoutInflater inflater = (LayoutInflater) _parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); ViewGroup root = (ViewGroup) inflater.inflate(R.layout.tl_frame, null); TextView frameNumber = (TextView) root.findViewById(R.id.FrameNumber); Integer val = new Integer(_nFramesDisplayed+1); //+1 to display ids starting from one on the user side frameNumber.setText(val.toString()); ++_nFramesDisplayed; _content.addView(root); // _content variable is initialized like this in c_tor // _content = (LinearLayout) _parent.findViewById(R.id.TimelineContent); }

Luego, dentro de mi código, trato de obtener el tamaño real en píxeles porque necesito que dibuje algunas cosas sobre él.

LayoutInflater inflater = (LayoutInflater) _parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); ViewGroup root = (ViewGroup) inflater.inflate(R.layout.tl_frame, null); ImageView frame = (ImageView) root.findViewById(R.id.TlFrame); frame.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); frame.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); final int w = frame.getMeasuredWidth(); final int h = frame.getMeasuredHeight();

Todo parece funcionar bien, excepto que esos valores son mucho más grandes que el tamaño de píxel real de ImageView.

Información reportada de getWindowManager (). GetDefaultDisplay (). GetMetrics (metrics); son los siguientes: densidad = 1,5 densidadDpi = 240 anchoPixel = 600 alturaPixel = 1024

Ahora, sé que la regla de android es: pixel = dip * (dpi / 160). Pero nada tiene sentido con el valor devuelto. Para ese ImageView de (90dip X 110dip), los valores devueltos del método measure () son (270 x 218) que asumí que están en pixel.

Alguien tiene alguna idea de por qué? ¿El valor se devuelve en píxeles?

Por cierto: he estado probando el mismo código pero con un TextView en lugar de un ImageView y todo parece funcionar bien. Por qué !?!?


Lamentablemente, en los métodos del ciclo de vida de la Activity , como Activity#onCreate(Bundle) , aún no se ha realizado una aprobación de diseño, por lo que aún no puede recuperar el tamaño de las vistas en su jerarquía de vistas. Sin embargo, puede pedir explícitamente a Android que mida una vista usando View # measure (int, int) .

Como señala la answer @ adamp, debe proporcionar View # measure (int, int) con los valores de MeasureSpec , pero puede ser un poco desalentador descubrir la MeasureSpec correcta.

El siguiente método intenta determinar los valores correctos de MeasureSpec y mide el aprobado en la view :

public class ViewUtil { public static void measure(@NonNull final View view) { final ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); final int horizontalMode; final int horizontalSize; switch (layoutParams.width) { case ViewGroup.LayoutParams.MATCH_PARENT: horizontalMode = View.MeasureSpec.EXACTLY; if (view.getParent() instanceof LinearLayout && ((LinearLayout) view.getParent()).getOrientation() == LinearLayout.VERTICAL) { ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams(); horizontalSize = ((View) view.getParent()).getMeasuredWidth() - lp.leftMargin - lp.rightMargin; } else { horizontalSize = ((View) view.getParent()).getMeasuredWidth(); } break; case ViewGroup.LayoutParams.WRAP_CONTENT: horizontalMode = View.MeasureSpec.UNSPECIFIED; horizontalSize = 0; break; default: horizontalMode = View.MeasureSpec.EXACTLY; horizontalSize = layoutParams.width; break; } final int horizontalMeasureSpec = View.MeasureSpec .makeMeasureSpec(horizontalSize, horizontalMode); final int verticalMode; final int verticalSize; switch (layoutParams.height) { case ViewGroup.LayoutParams.MATCH_PARENT: verticalMode = View.MeasureSpec.EXACTLY; if (view.getParent() instanceof LinearLayout && ((LinearLayout) view.getParent()).getOrientation() == LinearLayout.HORIZONTAL) { ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams(); verticalSize = ((View) view.getParent()).getMeasuredHeight() - lp.topMargin - lp.bottomMargin; } else { verticalSize = ((View) view.getParent()).getMeasuredHeight(); } break; case ViewGroup.LayoutParams.WRAP_CONTENT: verticalMode = View.MeasureSpec.UNSPECIFIED; verticalSize = 0; break; default: verticalMode = View.MeasureSpec.EXACTLY; verticalSize = layoutParams.height; break; } final int verticalMeasureSpec = View.MeasureSpec.makeMeasureSpec(verticalSize, verticalMode); view.measure(horizontalMeasureSpec, verticalMeasureSpec); } }

Entonces simplemente puede llamar:

ViewUtil.measure(view); int height = view.getMeasuredHeight(); int width = view.getMeasuredWidth();

Alternativamente, como @Amit Yadav suggested , puede usar OnGlobalLayoutListener para que se OnGlobalLayoutListener a un oyente después de que se haya realizado el pase de diseño. El siguiente es un método que maneja el anular el registro del oyente y el método para nombrar los cambios en las versiones:

public class ViewUtil { public static void captureGlobalLayout(@NonNull final View view, @NonNull final ViewTreeObserver.OnGlobalLayoutListener listener) { view.getViewTreeObserver() .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { final ViewTreeObserver viewTreeObserver = view.getViewTreeObserver(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { viewTreeObserver.removeOnGlobalLayoutListener(this); } else { //noinspection deprecation viewTreeObserver.removeGlobalOnLayoutListener(this); } listener.onGlobalLayout(); } }); } }

Entonces tú puedes:

ViewUtil.captureGlobalLayout(rootView, new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { int width = view.getMeasureWidth(); int height = view.getMeasuredHeight(); } });

Donde rootView puede ser la vista raíz de su jerarquía de vistas y view puede ser cualquier vista dentro de su jerarquía de la que desee conocer las dimensiones.


Llamas a la measure incorrectamente.

measure toma los valores de MeasureSpec que están empaquetados especialmente por MeasureSpec.makeMeasureSpec . measure ignora LayoutParams. Se espera que el padre que realiza la medición cree un MeasureSpec en función de su propia estrategia de medición y diseño y los LayoutParams del niño.

Si quiere medir la forma en que WRAP_CONTENT generalmente funciona en la mayoría de los diseños, llame a measure como este:

frame.measure(MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST));

Si no tiene valores máximos (por ejemplo, si está escribiendo algo así como un ScrollView que tiene espacio infinito), puede usar el modo UNSPECIFIED :

frame.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));


Okay ! Tipo de respuesta a mi propia pregunta aquí ... Pero no completamente

1 - Parece que en algunos dispositivos, la medición ImageView no proporciona valores exactos. He visto muchos informes sobre este suceso en los dispositivos Nexus y Galaxy, por ejemplo.

2 - Un trabajo que he encontrado:

Establezca el ancho y alto de su ImageView en "wrap_content" dentro del código xml.

Inflar el diseño dentro de su código (generalmente en la inicialización de la interfaz de usuario, supongo).

LayoutInflater inflater = (LayoutInflater) _parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); ViewGroup root = (ViewGroup) inflater.inflate(R.layout.tl_frame, null); ImageView frame = (ImageView) root.findViewById(R.id.TlFrame);

Calcule su propia proporción para su vista de imagen, según el cálculo típico de Android

//ScreenDpi can be acquired by getWindowManager().getDefaultDisplay().getMetrics(metrics); pixelWidth = wantedDipSize * (ScreenDpi / 160)

Use el tamaño calculado para configurar su ImageView dynamycally dentro de su código

frame.getLayoutParams().width = pixeWidth;

Y voilá ! su ImageView ahora tiene el tamaño de inmersión deseado;)


Probablemente, debido a lo que tiene en el archivo AndroidManifest.xml ( link ) y desde el directorio drawable-XXX el archivo xml, Android carga recursos con la operación de escalado. Decide usar la unidad de dimensión "inmersión" ( enlace ) que es virtual y el valor real (px) puede ser diferente.


Tienes que crear una vista de texto personalizada y usarla en tus diseños y usar la función de altura getActual para establecer la altura en tiempo de ejecución

public class TextViewHeightPlus extends TextView { private static final String TAG = "TextViewHeightPlus"; private int actualHeight=0; public int getActualHeight() { return actualHeight; } public TextViewHeightPlus(Context context) { super(context); } public TextViewHeightPlus(Context context, AttributeSet attrs) { super(context, attrs); setCustomFont(context, attrs); } public TextViewHeightPlus(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); actualHeight=0; actualHeight=(int) ((getLineCount()-1)*getTextSize()); } }


view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @SuppressLint("NewApi") @SuppressWarnings("deprecation") @Override public void onGlobalLayout() { //now we can retrieve the width and height int width = view.getWidth(); int height = view.getHeight(); //this is an important step not to keep receiving callbacks: //we should remove this listener //I use the function to remove it based on the api level! if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN){ view.getViewTreeObserver().removeOnGlobalLayoutListener(this); }else{ view.getViewTreeObserver().removeGlobalOnLayoutListener(this); } } });

Uno debería ir con Cómo obtener el ancho / alto de una Vista