Unit 8 Customize View and Touch Event Distribution

Preface

Native View does not meet our business needs, we have to customize the View

Custom View basics

Types of definition
Custom combination control Multiple controls are combined into a new control, which is convenient for multiple reuse
Inherit the system View control Inherited from system controls such as TextView, and expand on the basic functions of system controls
Inherit View Do not reuse system control logic, inherit View for function definition
Inherit the system ViewGroup Inherited from LinearLayout and other system controls, and expand on the basic functions of system controls

View drawing process

onMeasure

Measure the width and height of the View
setMeasuredDimension() Set the measured height of the View

onLayout

Calculate the current view and the position of the child view

onDraw

View drawing work

3. The difference between getMeasureWidth() and getWidth()

The size of a View is determined by the View itself and the parent View.
getMeasureWidth: Determined in the measure stage, which is the original value in xml.
getWidth: Determined in the layout stage, and is the size of the final display. The
two values ​​may be equal or may not be equal.

How to get the height of View in onCreate?

postDelay延迟获取一下
ViewTreeObserver方式获取
ViewTreeObserver vto = view.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    
    
@Override
public void onGlobalLayout() {
    
    
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
Log.i(TAG, "width: " + view.getWidth());
Log.i(TAG, "height: " + view.getHeight());
}
});

4. The difference between View and ViewGroup drawing

onMeasure:
View only needs to determine its own size, ViewGroup needs to determine the size of the child View first

onLayout:
View does not need to be processed, ViewGroup must implement onLayout to arrange the position of child views

onDraw:
View needs to implement its own onDraw, and ViewGroup uses dispatchDraw to draw each sub-view

5 example

Customize a text control

//自定义文件控件
public class FentTextView extends View {
    
    
    //new用
    public FentTextView(Context context) {
    
    
        super(context);
    }

    //xml里面用
    public FentTextView(Context context, @Nullable AttributeSet attrs) {
    
    
        super(context, attrs);
    }
    
}

At this time, it can be introduced in xml, but there is no specific attribute.
Then you can customize the attribute
Insert picture description here

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="FentTextView">
        <attr name="android:text" format="string"></attr>
<!--        自定义name-->
        <attr name="text" format="string"></attr>
    </declare-styleable>
</resources>

The type of attribute value format

(1). reference: refer to a resource ID
(2). color: color value
(3). boolean: boolean value
(4). dimension: dimension value
(5). float: floating point value
(6). integer: Integer value
(7). string: string
(8). fraction: percentage
(9). enum: enumerated value
(10). flag: bitwise OR operation

At this time, the content of the file set by xml is not the same as the one drawn. You
need to continue to modify the content in the view.

package com.fenghongzhang.day004;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.Nullable;

//自定义文件控件
public class FentTextView extends View {
    
    
    
    private String text;

    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    //new用
    public FentTextView(Context context) {
    
    
        super(context);
    }

    //xml里面用
    public FentTextView(Context context, @Nullable AttributeSet attrs) {
    
    
        super(context, attrs);
        /**
         * 第一个参数attrs
         * 第二个参数是自定义view的名称
         */
        //取到所有属性值
        TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.FentTextView);
        //取到文本值
        text = typedArray.getString(R.styleable.FentTextView_android_text);
        
    }

    @Override
    protected void onDraw(Canvas canvas) {
    
    
        super.onDraw(canvas);
        canvas.drawText(text,200,200,paint);
    }
}

Modify font size

<attr name="size" format="dimension"></attr>
package com.fenghongzhang.day004;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.Nullable;

//自定义文件控件
public class FentTextView extends View {
    
    

    private String text;
    private float size;

    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    //new用
    public FentTextView(Context context) {
    
    
        super(context);
    }

    //xml里面用
    public FentTextView(Context context, @Nullable AttributeSet attrs) {
    
    
        super(context, attrs);
        /**
         * 第一个参数attrs
         * 第二个参数是自定义view的名称
         */
        //取到所有属性值
        TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.FentTextView);
        //取到文本值
        text = typedArray.getString(R.styleable.FentTextView_android_text);
        //获取大小
        size = typedArray.getDimension(R.styleable.FentTextView_size, 20);

    }

    @Override
    protected void onDraw(Canvas canvas) {
    
    
        super.onDraw(canvas);
        //设置字体大小
        paint.setTextSize(size);
        canvas.drawText(text,200,200,paint);
    }
}

At this time, the width and height of the view have no effect.

android:layout_height="wrap_content"   //自适应大小  
android:layout_height="match_parent"   //与父视图等高  
android:layout_height="fill_parent"    //与父视图等高  
android:layout_height="100dip"         //精确设置高度值为 100dip 

Measurement specification (MeasureSpec) = measurement mode (mode) + measurement size (size)

Insert picture description here

2. Three measurement modes

MeasureSpec represents a 32-bit integer value. The upper 2 bits represent the measurement mode SpecMode, and the lower 30 bits represent the SpecSize in a certain measurement mode.

Three measurement modes

EXACTLY : Exact measurement mode It takes effect
when the layout_width or layout_height of the view is specified as a specific value or match_parent, which means that the parent view has determined the exact size of the child view. In this mode, the measurement value of the View is the value of SpecSize.

AT_MOST : Maximum mode. It
takes effect when the layout_width or layout_height of the current view is specified as wrap_content. At this time, the size of the child view can be any size that does not exceed the maximum size of the parent view.

UNSPECIFIED : The measurement mode is not specified. The
parent view does not limit the size of the child view. The child view can be any size you want. It is usually used inside the system and is rarely used in application development. For example, listview is this mode

    //设置最重要的属性之一
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
    
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
//                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));

        //100为宽度默认值
        measureWidth = getMyViewSize(300, widthMeasureSpec);
        //200为高度默认值
        measureHeight = getMyViewSize(300, heightMeasureSpec);
        //记录子子布局确认后的尺寸
        setMeasuredDimension(measureWidth, measureHeight);

//        int widthModel = MeasureSpec.getMode(widthMeasureSpec);
//        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
//
//        int heightModel = MeasureSpec.getMode(heightMeasureSpec);
//        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
//        setMeasuredDimension(width,height);

    }
   private int getMyViewSize(int defaultSize, int measureSpec) {
    
    
        int result = defaultSize;
        //解封模式和尺寸值
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        switch (specMode) {
    
    
            case MeasureSpec.UNSPECIFIED:
                result = defaultSize;
                break;
            case MeasureSpec.AT_MOST:
                //指定一个值
                result = defaultSize;
                break;
            case MeasureSpec.EXACTLY:
                result = specSize;
                break;
        }
        return result;
    }

Reference link

https://www.jianshu.com/p/23519665ff32

https://www.jianshu.com/p/146e5cec4863

Guess you like

Origin blog.csdn.net/shuai_ge_feng/article/details/114101996
Recommended