1. Comprensión del método onMeasure
Primero, creamos un nuevo componente MyView
package main.com.taiji.component;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
public class MyView extends View {
public MyView(Context context) {
super(context);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
Primero probamos el caso donde tanto el ancho como el alto son match_parent
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<main.com.taiji.component.MyView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:background="#ffff00"/>
</LinearLayout>
El efecto es el siguiente:
A continuación, probamos el ancho y el alto de wrap_content
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<main.com.taiji.component.MyView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="#ffff00"/>
</LinearLayout>
El efecto permanece sin cambios (Figura 2)
A continuación, especificamos el ancho y el alto como valores específicos.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<main.com.taiji.component.MyView
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_margin="10dp"
android:background="#ffff00"/>
</LinearLayout>
El efecto es el siguiente:
Modificamos el método OnMeasure en MyView
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int dp=px2dip(getContext(),widthSize);
setMeasuredDimension(200,200);
}
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
Descubrimos que el ancho y el alto del componente se han modificado (observe los 200 aquí, la unidad es px)
Al mismo tiempo, obtenemos la siguiente información mediante depuración
Entonces podemos inferir: el widthMeasureSpec y heightMeasureSpec en el método onMeasure son el ancho y el alto especificados en layout_width y layout_height en la Vista del componente principal (es necesario convertir)
¿Cómo usar OnMeasure correctamente?
Tomemos el valor widthMeasureSpec como ejemplo:
El valor de widthMeasureSpec se compone de 32 bits altos y 16 bits bajos. El valor guardado en los 32 bits altos se llama specMode, que se puede obtener a través de MeasureSpec.getMode () como se muestra en el código; los 16 bits bajos son specSize, que también se puede obtener mediante MeasureSpec.) Obtenga
Tres posibilidades de specMode:
MeasureSpec.EXACTLY: La vista principal espera que el tamaño de la vista secundaria se especifique en specSize. EXACTAMENTE es equivalente a establecer match_parent o un valor específico
MeasureSpec.AT_MOST: el tamaño de la subvista es como máximo el valor especificado en specSize, lo que significa que no se recomienda que el tamaño de la subvista exceda el valor dado en specSize. AT_MOST es equivalente a configurar wrap_content
MeasureSpec.UNSPECIFIED: Podemos especificar el tamaño de la vista a voluntad.
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));
}
public int measureWidth(int measureSpec){
int result=0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if(specMode==MeasureSpec.EXACTLY){
result=specSize;
}else {
result=300;
if(specMode==MeasureSpec.AT_MOST){
result=Math.min(result,specSize);
}
}
return result;
}
public int measureHeight(int measureSpec){
int result=0;
int specMode=MeasureSpec.getMode(measureSpec);
int specSize=MeasureSpec.getSize(measureSpec);
if(specMode==MeasureSpec.EXACTLY){
result=specSize;
}else {
result=300;
if(specMode==MeasureSpec.AT_MOST){
result=Math.min(result,specSize);
}
}
return result;
}
Luego probamos:
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_width="400dp"
android:layout_height="400dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
Se puede ver que cuando configuramos wrap_content, el ancho y el alto del componente personalizado están limitados a no más de 300px (si la situación original cubrirá la pantalla completa, vea la Figura 2).