Custom View is to inherit View or its subclass, and implement the corresponding processing logic (override the corresponding methods) in the new class to achieve the desired effect.
Categories#
- Custom ViewGroup: Custom ViewGroup generally uses existing components to compose new components based on specific layout methods, mostly inherited from ViewGroup or various Layout, and contains child Views.
- Custom View: When there is no ready-made View and need to be implemented by oneself, custom View is used, generally inherited from View, SurfaceView or other Views, and does not contain child Views.
Constructors#
Whether we inherit the system View or directly inherit View, we need to override the constructors. There are multiple constructors, and at least one of them needs to be overridden.
public class TestView extends View {
/**
* Used when creating a new instance in Java code
* @param context
*/
public TestView(Context context) {
super(context);
}
/**
* Automatically called when used in XML layout file
* @param context
*/
public TestView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
/**
* Not automatically called, if there is a default style, it is called in the second constructor
* @param context
* @param attrs
* @param defStyleAttr
*/
public TestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* Only used when API version > 21
* Not automatically called, if there is a default style, it is called in the second constructor
* @param context
* @param attrs
* @param defStyleAttr
* @param defStyleRes
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public TestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
}
Drawing Process#
- Measure
- Layout
- Draw
- Provide interfaces
Measure Phase#
- The parent View of View passes the size requirements of the parent View to the View by calling the measure() method of View.
- The measure() method of View performs some pre-processing and optimization work.
- Call the onMeasure() method, and in this method, perform the corresponding logical processing according to the business requirements. In the onMeasure() method of the custom ViewGroup, the ViewGroup will recursively call the measure() method of the child View, and pass the size requirements of the ViewGroup to the child View through measure() (calculate the size requirements of the child View based on the size requirements of itself and its available space), measure the child View, and temporarily save the measurement result for use in the layout phase. The ViewGroup will calculate its expected size based on the actual size of the child View, and inform the parent View (the parent View of the ViewGroup) of its expected size through the setMeasuredDimension() method.
- Call the setMeasuredDimension() method to inform the parent View of the expected size.
Method to calculate the expected size of View in onMeasure():
- Calculate the expected size of View based on the size requirements of the parent View and the actual business requirements:
- Parse widthMeasureSpec;
- Parse heightMeasureSpec;
- Modify the "calculated size of View based on actual business requirements" according to the "size requirements of the parent View" to obtain the expected size of View (by calling the resolveSize() method)
- Save the expected size of View through setMeasuredDimension() (actually inform the parent View of its expected size)
onMeasure() :
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthsize MeasureSpec.getSize(widthMeasureSpec); //Get the exact value of the width
int widthmode MeasureSpec.getMode(widthMeasureSpec); //Get the measurement mode of the width
int heightsize MeasureSpec.getSize(heightMeasureSpec); //Get the exact value of the height
int heightmode MeasureSpec.getMode(heightMeasureSpec); //Get the measurement mode of the height
}
widthMeasureSpec and heightMeasureSpec are not actually width and height, but values composed of width, height, and corresponding measurement modes.
Layout Phase#
- The parent View passes the actual size of the View to the View by calling the Layout method of the View.
- The View calls the setFrame() method to save the actual size.
- The setFrame() method will call the onSizeChanged() method to inform the developer that the size of the View has been modified.
- The Layout() method of the View calls the onLayout() method of the View, which is an empty implementation. But in ViewGroup, it will call the Layout() method of the child View here, passing the actual size of the child View to them and letting them save the actual size. In custom ViewGroup, this method needs to be overridden.
Draw Phase#
- draw(), the overall scheduling method, will call the methods for drawing background, drawing theme, drawing foreground, and drawing child Views.
- onDraw(), the method for drawing the main content of the View.
- dispatchDraw(), the method for drawing child Views. In custom ViewGroup, it will call the ViewGroup.drawChild() method, which will call the View.draw() method for each child View.
- drawBackground(), the method for drawing the background, cannot be overridden, and can only be set through the XML layout file or setBackground() method.
- onDrawForegound(), the method for drawing the foreground of the View, used to draw things above the main content.
Provide Interfaces#
Write some controls or listeners for certain states of the View.