Android custom controls

Usually when developing the interface of an Android application, the View control is not used directly, but a subclass of the View control is used. For example, if you want to display a piece of text, you can use the TextView control, a subclass of the View control; if you want to display a button, you can use the Button control, a subclass of the View control. Although Android provides many controls inherited from the View class, in actual development, there will be situations that do not meet the needs. In this case, we can implement it through custom controls. The simplest custom control is to create a class that inherits View or its subclasses( app / java / package name / xxx.java ) and rewrite the class’s Construction method.

The example code is as follows:

public class MyView extends View {
    public MyView(Context context) {
        super(context);
        //Methods used in Java code
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        //Methods used in XML layout files
    }

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //This method is used to measure the size. In this method, you can set the width and height of the control itself or its sub-controls.
    }
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //This method is used to draw images
    }
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        //Used to specify the position of sub-controls in the layout
    }


   //Other methods of custom controls
}

The public XXX( Context context ) method is a constructor method used in Java files, and the public XXX( Context context , AttributeSet attrs ) method is used in Method used in XML layout files.

1. Implementation method

Since the system’s own controls cannot meet certain styles or functions required, we need to add additional styles and functions by overriding specified methods in custom controls. The three commonly used methods of custom controls are detailed as follows:

1.onMeasure() method

This method is used to measure the size. In this method, you can set the width and height of the control itself or its sub-controls. The specific introduction of the onMeasure() method is as follows:

onMeasure( int widthMeasureSpec , int heightMeasureSpec )

The first parameter widthMeasureSpec in the onMeasure() method means getting the width of the control specified by the parent container, and the second parameter heightMeasureSpec means getting the height of the control specified by the parent container.

The widthMeasureSpec and heightMeasureSpec parameters not only include the attribute values specified by the parent container, but also include the measurement mode of the parent container. The measurement modes are divided into three types. The details are as follows:

EXACTLY: Used when the width and height values of the custom control are set to specific values, such as 100dp, match_parent, etc. At this time, the width and height values of the control are precise dimensions.

AT_MOST: Used when the width and height value of the custom control is wrap_content. At this time, the width and height value of the control is the maximum space value available for the data content in the control.

UNSPECIFIED: Used when the parent container does not specify the width and height of the custom control.

It should be noted that although the parameters widthMeasureSpec and heightMeasureSpec are the width and height specified by the parent container for the control, the control also needs to set the specific width and height through the setMeasureDimension( int i , int i ) method.

2.onDraw() method

This method is used to draw images. The specific introduction of the onDraw() method is as follows:

onDraw( Canvas canvas )

The parameter canvas in the onDraw() method represents the canvas. The Canvas class (canvas) is often used in conjunction with the Paint class (brush), and the Paint class can be used to draw images in the Canvas class.

3.onLayout() method

The onLayout() method is used to specify the position of the child control in the layout. This method is usually overridden in a custom ViewGroup container.

The specific introduction of the onLayout() method is as follows:

onLayout( boolean changed , int left , int top , int right , int bottom )

There are 5 parameters in the onLayout() method. The first parameter changed indicates whether the size and position of the custom control have changed. The remaining 4 parameters left, top, right, and bottom indicate the child control and parent container respectively. The distance between the left and the top; that is, left and parent left, top and parent top, right and parent left, and bottom and parent top.

4.MyView(Context context, AttributeSet attrs) method

Constructor method used in XML layout files

Use context.obtainStyledAttribute(Attribute attrs,R.styleable.~) to obtain a TypeArray type attribute collection.

5.MyView(Context context) method

Constructor methods used in Java files

6.dispatchDraw(Canvas canvas) method

This method is similar to the onDraw() method, both are used for drawing; the difference is that the onDraw() method is called before drawing the sub-control, while the dispatchDraw() method is called after drawing After child control. That is, onDraw()->Draw sub-control->dispatchDraw(). If you want the sub-control to cover the drawing part, use onDraw(); if you want the sub-control to not cover the drawing part, use dispatchDraw().

2. Dimensional measurement (onMeasure() method)

1. Measurement mode

Android views provide three measurement modes:

(1)MeasureSpec.AT_MOST reaches the maximum value, that is, match_parent.

(2)MeasureSpec.UNSPECIFIED is not specified (actually adaptive), that is, wrap_content.

(3)MeasureSpec.EXACTLY Exact size, that is, specific dp value.

2. Get measurement mode

MeasureSpec.getMode( int widthMeasureSpec )

3. Get the actual size

MeasureSpec.getSize( int widthMeasureSpec )

//Example
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    //Get measurement mode
    int widthMode=MeasureSpec.getMode(widthMeasureSpec);
    //Judge mode
    if(widthMode==MeasureSpec.EXACTLY){
        //The mode is a specific dp value
        //Get the actual width
        int widthSize=MeasureSpec.getSize(widthMeasureSpec);
        Log.d("OK","Set value, actual width:" + widthSize);
    }
    else if(widthMode==MeasureSpec.UNSPECIFIED){
        //Mode is unspecified (adaptive), wrap_content
        //Get the actual width
        int widthSize=MeasureSpec.getSize(widthMeasureSpec);
        Log.d("OK","Adaptive, actual width:" + widthSize);
    }
    else if (widthMode==MeasureSpec.AT_MOST) {
        //The mode is to reach the maximum value, match_parent
        //Get the actual width
        int widthSize=MeasureSpec.getSize(widthMeasureSpec);
        Log.d("OK","Maximum value, actual width:" + widthSize);
    }

    //Reset the width and height of the sub-control based on the actual width and actual height obtained.
    ... ...

}

4.Measure text size

(1)Measure text width
//Create a Paint (brush) object
Paint paint=new Paint();

//Set the text size of the brush
paint.setTextSize(textSize);

//Measure text width
paint.measureText(text);
(2)Measure text height

Using the FontMetrics class

Distance property of FontMetrics class:

top The distance between the top of the row and the baseline.

ascent The distance between the top of the character and the baseline.

descent The distance between the bottom of the character and the baseline.

bottom The distance between the bottom of the row and the baseline.

leading Line spacing.

//Create a Paint (brush) object
Paint paint=new Paint();

//Set the text size of the brush
paint.setTextSize(textSize);

//Get the FontMetrics object
Paint.FontMetrics fm=paint.getFontMetrics();

//Text height
float h=fm.descent-fm.ascent;

3. Sub-control layout (onLayout() method)

Override common methods in onLayout() method:

getChildCount() Get the number of child controls

getChildAt( int index ) Gets the child control of the specified index and returns View

view.getVisibility() Gets the existence status of the control, GONE, TRUE, FALSE

view.getMeasuredHeight() Gets the control height px

view.getMeasuredWidth() Gets the width of the control in px

view.layout( int l , int t , int r , int b ) Set the left and top distances between the border around the control and the parent container, left and parent left, top and parent top, right and parent left, and bottom and parent top.

//Example
protected void onLayout(boolean changed, int l, int t, int r, int b) {

    for (int i=0;i< getChildCount() ;i + + ){

        View view= getChildAt(i);

        if( view.getVisibility() !=GONE){

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

            //Reset the sub-control layout position
            view.layout(~,~,~,~);

        }
    }
}

Using requestLayout() in other methods will call onLayout() again to redefine the layout.

4. Attributes and XML layout

1. XML layout file of custom control (res/layout/xxx.xml)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
 
</RelativeLayout>

2. XML file for setting the attributes of the combined control (res/value/attrs.xml)

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--Declaration-style custom control name-->
    <declare-styleable name="MyView">
        
        <attr name="attrN1" format="string"/>
        <!--Attribute name Attribute type -->
        
        <attr name="attrN2">
            <flag name="FLAG_N1" value="1"/>
            <!--Attribute name Attribute value (this value can only be int) -->
            <flag name="FLAG_N2" value="1"/>
        </attr>
 
        </attr name="attrN3" format="reference"/>
        <!--Attribute name Attribute value (resource R.~.~) -->
        
    </declare-styleable>
</resources>

declare-statement styleable-style attr-attribute flag-identity, flag

format=”reference”The identification type is resource ( R.~.~ )

3. Create a custom combination control class (app/java/package name/xxx.java)

public class MyView extends RelativeLayout { //Inherit existing controls (RelativeLayout, LinearLayout, etc.)
    private int i;
    private TextView textView;
    public MyView(Context context) {
        super(context);
        //Constructor for Java files
    }
    
    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        //Constructor used in XML layout file
        getAttrs(context,attrs);
        setView(context);
    }
    
    private void getAttrs(Context context,AttributeSet attrs){
        //Get attribute value
        TypedArray typedArray=context.obtainStyledAttributes(attrs,R.styleable.MyView);
        
        i=typedArray.getInt(R.styleable.MyView_attrN2,1);
        int id=typedArray.getResourceId(R.styleable.MyView_attrN3,R.~.~);
        //typedArray.getString typedArray.getBoolean
 
        //Recycle
        typedArray.recycle();
    }
    
    private void setView(Context context){
        LayoutInflater.from(context).inflate(R.layout.myview,this);
        
        textView=(TextView) findViewById( ~ );
        textView.setText(i);
    }
}

Be sure to recycle TypedArray after obtaining the custom control property value

Use getResourceId() to get the resource ID of type reference

Custom combination controls need to inherit existing controls (RelativeLayout, LinearLayout, etc.)

LayoutInflater.from( context ).inflate( R.layout.myview , this ); The root is this

4. Use custom combination controls

<MyView
    app:attrN1="testString"
    app:attrN2="FLAGN1"
    ... ...
/>

Custom attribute usage: app: attribute name = ” attribute value ”

5. Note: Custom control classes need to implement methods

public class MyView extends RelativeLayout {
    public MyView(Context context) {
        super(context);
        //Constructor for Java files
    }
 
    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        //Constructor used in XML layout file
 
        TypedArray ta=context.obtainStyledAttributes(attrs,R.styleable.MyView);
        LayoutInflater.from(context).inflate(R.layout.myview,this);
    }
 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //This method is used to measure the size. In this method, you can set the width and height of the control itself or its sub-controls.
    }
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //This method is used to draw images
    }
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        //Used to specify the position of sub-controls in the layout
    }
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        //This method is used to draw images, similar to the onDraw() method
    }
}