¿Por qué mi vista personalizada de Android no es cuadrada?
He creado una vista personalizada para mostrar un tablero para un juego que estoy desarrollando. El tablero de juego debe ser un cuadrado en todo momento. Así que con y la altura debe ser la misma. He seguido este tutorial para lograr esto.
He añadido el siguiente código a mi vista personalizada:
- Personalizar una ProgressBar para convertirse en un termómetro
- Agregar varias vistas personalizadas al diseño mediante programación
- OnMeasure no se llama en mi grupo de vista personalizado android
- Android Twitter - Hacer clic en la pestaña Actualizar y ocultar
- Gráfico cardiaco para android
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = getMeasuredWidth(); int height = getMeasuredHeight(); int widthWithoutPadding = width - getPaddingLeft() - getPaddingRight(); int heigthWithoutPadding = height - getPaddingTop() - getPaddingBottom(); int maxWidth = (int) (heigthWithoutPadding * RATIO); int maxHeight = (int) (widthWithoutPadding / RATIO); if (widthWithoutPadding > maxWidth) { width = maxWidth + getPaddingLeft() + getPaddingRight(); } else { height = maxHeight + getPaddingTop() + getPaddingBottom(); } setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Paint p = new Paint(); p.setStyle(Paint.Style.STROKE); p.setStrokeWidth(3); canvas.drawRect(0, 0, getMeasuredWidth() - 1, getMeasuredHeight() - 1, p); }
El diseño con mi vista personalizada se ve así:
<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" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <view android:layout_width="wrap_content" android:layout_height="wrap_content" class="com.peerkesoftware.nanograms.controls.GameBoard" android:id="@+id/view" android:background="@android:color/holo_purple"/> </RelativeLayout>
El cuadrado que se dibuja en el método onDraw es siempre un cuadrado. Eso funciona bien.
En el diseño agrego la vista personalizada y le doy a la vista un color de fondo. Cuando la exhibo en un dispositivo en modo del retrato todo trabaja muy bien y el color de fondo está llenando para arriba el cuadrado que dibujo en el método del onDraw.
Cuando cambio el dispositivo al modo horizontal, el cuadrado sigue siendo un cuadrado, pero el área del color de fondo es más grande que eso. Tiene la misma altura, pero tiene más ancho. Pero no tengo ni idea de por qué. Los métodos getMeasuredWidth () y getMeasuredHeight () devuelven los valores correctos. ¿Cómo puede ser que la vista sea aún más grande entonces?
- ¿Cómo agregar las burbujas a textview android?
- Al dibujar fuera de la vista, el clip se borra con Android: ¿cómo evito que las vistas subordinadas se dibujen en la parte superior de mi vista personalizada?
- Pasar atributos a la vista secundaria en vistas compuestas
- ¿Cómo mostrar el número múltiple de TextViews dentro de cada fila en ListView?
- ¿Por qué el camino dibujado en escala GLES20RecordingCanvas y lienzo simple tiene cualidades diferentes?
- Atributos de vista personalizada de Android que no funcionan después de cambiar a gradle
- Cómo implementar la animación de texto de lista personalizada
- ¿Por qué EditText en una vista personalizada de compuesto está reutilizando el texto introducido en otra instancia de vista compuesta?
Por alguna razón, su vista no funciona en RelativeLayout. Para la orientación horizontal, la medición termina con las siguientes limitaciones (en mi teléfono):
Width = MeasureSpec: EXACTAMENTE 404; Height = MeasureSpec: AT_MOST 388
Debido a este ancho real termina por encima de la anchura medida (en mi teléfono medido = 388, actual = 404). Por eso ve que el fondo se extiende más allá de su rect. Puede comprobarlo usted mismo agregando un registro a onDraw y onMeasure.
Además, respetando el modo de medición no ayuda, la medición continúa con el mismo resultado que anteriormente.
La solución es usar un diseño diferente. LinearLayout y FrameLayout funcionaron para mí. También es una buena idea usar el tamaño real de la vista en onDraw, con getWidth () y getHeight ().
Si realmente necesita RelativeLayout, puede agregar un FrameLayout secundario para mantener su vista de la siguiente manera:
<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" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" > <view android:id="@+id/view" android:layout_width="wrap_content" android:layout_height="wrap_content" class="com.peerkesoftware.nanograms.controls.GameBoard" android:background="@android:color/holo_purple" /> </FrameLayout> </RelativeLayout>
Subclase ImageView y anula onMeasure
y pasa a
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
toda la clase:
public class SquaredImageView extends ImageView { public SquaredImageView(Context context) { super(context); } public SquaredImageView(Context context, AttributeSet attrs) { super(context, attrs); } public SquaredImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, widthMeasureSpec); } }
Edit: He intentado con una vista normal
public class CustomView extends View { public CustomView(Context context) { super(context); setBackgroundColor(Color.RED); } public CustomView(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = getMeasuredWidth(); int heigth = getMeasuredHeight(); int dimen = (width > heigth) ? heigth : width; setMeasuredDimension(dimen, dimen); } }
Y un TestActivity
public class TestActivity extends Activity { protected void onCreate(android.os.Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new CustomView(this)); } }
Dibuja un rectángulo rojo cuadrado en la pantalla de mi móvil
Este es mi código, estoy usando este código y está funcionando bien. No es exacly a lo que usted está buscando pero puede darle una idea. Sobre la base de mi requisito altura de la vista de la imagen debe ser la misma longitud que el ancho.
public class SquareImageView extends ImageView { private static final String TAG = "SquareImageView"; public SquareImageView(Context context) { super(context); } public SquareImageView(Context context, AttributeSet attrs) { super(context, attrs); } public SquareImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ super.onMeasure(widthMeasureSpec, heightMeasureSpec); //int h = this.getMeasuredHeight(); int w = this.getMeasuredWidth(); setMeasuredDimension(w, w); } }
Definido en xml como:
<com.something.widget.SquareImageView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/ivProfilePicture" android:layout_alignParentLeft="true" android:layout_centerVertical="true" android:layout_alignParentTop="true" android:adjustViewBounds="true" android:scaleType="fitXY" android:background="@drawable/bg_curve_white_2" android:contentDescription="@string/general_content_description" android:src="@drawable/ic_default_logo" android:layout_marginTop="@dimen/side_margin"/>
Y finalmente en Fragment es algo así como:
SquareImageView ivProfilePicture = (SquareImageView) view.findViewById(R.id.ivProfilePicture);
No estoy seguro de qué solución servirá la necesidad, pero el código del tutorial tiene un problema de diseño fundamental – no interpreta los parámetros onMeasure()
de onMeasure()
correctamente. Respuesta en la explicación de vista personalizada de onMeasure explica la correcta interpretación de estos parámetros.
Lo que sucede en este ejemplo, onMeasure()
se invoca dos veces para cada aparición de dibujo para la vista personalizada.
Para la primera llamada, ambos MeasureSpecs especifican el modo AT_MOST
y dan valores iniciales sin tener en cuenta los márgenes y el relleno. Por lo tanto, el código del tutorial legítimamente hace que el ancho y la altura coincidan.
Para la segunda llamada, sin embargo, contiene RelativeLayout toma los márgenes y el relleno en cuenta, y pasa MeasureSpec
para el Width
con el modo EXACT
, mientras que la Height
se pasa en el modo AT_MOST
.
Para la pantalla vertical, esto funciona bien porque el Width
se redujo por los márgenes, y el valor de la altura sigue siendo la altura de la pantalla (también reducido por los márgenes). Por lo tanto, durante el segundo código de llamada, legítimamente reduce la altura para que coincida ahora con un ancho menor, y todo funciona correctamente.
Sin embargo, para la orientación horizontal de la pantalla, "Ancho" se ajusta al valor definido durante la primera llamada, mientras que Height
se reduce por los márgenes. Cuando el código intenta establecer el Width
para que coincida con la Height
, esta operación de configuración no funciona correctamente porque el modo Width
es EXACT
. Como resultado, las propiedades de la vista Width
y MeasuredWidth
terminan con valores diferentes.
Hay un par de formas sencillas de hacer que el código en cuestión funcione: simplemente puede eliminar márgenes verticales:
<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" android:paddingBottom="@dimen/zero" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/zero" tools:context=".MainActivity" >
O, si los márgenes son importantes, puede cambiar el diseño que contiene a absoluto:
<AbsoluteLayout 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" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" >
En ambos casos parece tener sentido cambiar la implementación de onMeasure()
para respetar el modo EXACT
(para registrar un problema al menos).
Su código onMeasure()
no es sencillo. Cambiar el código de la siguiente manera y establecer match_parent como el ancho y la altura en xml (En realidad no importa)
void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); int widthWithoutPadding = width - getPaddingLeft() - getPaddingRight(); int heigthWithoutPadding = height - getPaddingTop() - getPaddingBottom(); int maxWidth = (int) (heigthWithoutPadding * RATIO); int maxHeight = (int) (widthWithoutPadding / RATIO); if (widthWithoutPadding > maxWidth) { width = maxWidth + getPaddingLeft() + getPaddingRight(); } else { height = maxHeight + getPaddingTop() + getPaddingBottom(); } setMeasuredDimension(width, height); }
- Administrador de Firebase NoClassDefFoundError: FirebaseOptions $ Builder
- Clear Disk / SD Card Cache de la Biblioteca de imágenes de Picasso de Android