Android——自定义组件知识总结

1.onMeasure方法的理解

首先我们新建一个组件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);
    }
}

我们首先测试宽和高都是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>

效果如图: 

接下来我们测试宽和高为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>

效果不变 (图2)

接下来我们指定宽和高为具体的值

<?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>

效果如下: 

我们修改MyView中的OnMeasure方法

    @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);
    }

我们发现组件的宽和高被修改了 (注意这里的200,单位是px)

同时我们通过debug得到如下信息

然后我们可以推断:onMeasure方法中的widthMeasureSpec和heightMeasureSpec是父组件View中的layout_width和layout_height中指定的宽和高(需要换算一下)

如何正确使用OnMeasure?

我们以widthMeasureSpec值举例:

widthMeasureSpec这个值由高32位和低16位组成,高32位保存的值叫specMode,可以通过如代码中所示的MeasureSpec.getMode()获取;低16位为specSize,同样可以由MeasureSpec.getSize()获取

specMode的三种可能:

MeasureSpec.EXACTLY:父视图希望子视图的大小应该是specSize中指定的。EXACTLY相当于我们设置了match_parent或一个具体的值

MeasureSpec.AT_MOST:子视图的大小最多是specSize中指定的值,也就是说不建议子视图的大小超过specSize中给定的值。AT_MOST相当于我们设置了wrap_content

MeasureSpec.UNSPECIFIED:我们可以随意指定视图的大小。

    @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;
    }

然后我们进行测试:

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"

可以看出,当我们在设置wrap_content时,该自定义组件的宽和高被限制为不能超过300px(如果按照原来的情况会铺满全屏,见图2)

猜你喜欢

转载自blog.csdn.net/hzkcsdnmm/article/details/112978370