Custom View is achieved by inheriting View or its subclass, and implementing the corresponding processing logic (overriding the corresponding methods) in the new class to achieve the desired effect.
Categories#
- Custom ViewGroup: Custom ViewGroup is generally composed of existing components according to specific layout methods to form a new component, mostly inherited from ViewGroup or various Layout, and contains child Views.
- Custom View: When there is no ready-made View and you need to implement it yourself, you can use Custom View, which is 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 with the 'new' keyword
* @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, only called in the second constructor if there is a default style
* @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, only called in the second constructor if there is a default style
* @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 the View passes the size requirements of the View to the View by calling the measure() method of the View.
- The measure() method of the 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 its own size requirements and available space). Measure the child View and temporarily save the measurement results for use in the layout phase. The ViewGroup will calculate its own 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 the View in onMeasure():
- Calculate the expected size of the View based on the size requirements of the parent View and the actual business requirements:
- Parse widthMeasureSpec;
- Parse heightMeasureSpec;
- Calculate the expected size of the View based on the "actual business requirements" and make corresponding adjustments based on the "size requirements of the parent View" (by calling the resolveSize() method).
- Save the expected size of the 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 width value
int widthmode MeasureSpec.getMode(widthMeasureSpec); //Get the measurement mode of the width
int heightsize MeasureSpec.getSize(heightMeasureSpec); //Get the exact height value
int heightmode MeasureSpec.getMode(heightMeasureSpec); //Get the measurement mode of the height
}
widthMeasureSpec and heightMeasureSpec are two int parameters that are not actually width and height, but are composed of width, height, and the corresponding measurement modes in each direction.
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 saves the actual size in the setFrame() method called in the Layout method.
- 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, passing the actual size of the child View to them and letting them save the actual size. In the custom ViewGroup, this method needs to be overridden.
Draw Phase#
- draw(): The overall scheduling method, which will call methods to draw the background, draw the main content, draw the foreground, and draw the child Views.
- onDraw(): The method to draw the main content of the View.
- dispatchDraw(): The method to draw the child Views. In the custom ViewGroup, it will call the ViewGroup.drawChild() method, which will call the View.draw() method for each child View.
- drawBackground(): The method to draw the background, cannot be overridden, can only be set through the XML layout file or setBackground() method.
- onDrawForeground(): The method to draw the foreground of the View, used to draw things above the main content.
Provide Interfaces#
Write some interfaces to control or listen to certain states of the View.