A basic custom View should do:

  1. From the perspective of drawing, there are three points: measuring, placing, drawing themselves and sub-views (for ViewGroup).
  2. Save the UI state.
  3. Handle touch events.

Constructor ()->onMeasure()->onLayout()->onDraw()

Before we start, let’s take a look at the hierarchy of Android’s default views:


1. ByonMeasure()Method to tell how much space a View occupies based on the size and constraints of the parent container.This is a bottom-up execution method, that is, the measurement is performed starting with the RootView at the lowest level, and then the numerous sub-views in the measurement viewGroup are distributed.

  • To understand onMeasure(), let’s take a look at the method prototype.

    @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {.... }Copy the code

    WidthMeasureSpec, heightMeasureSpec, widthMeasureSpec, widthMeasureSpec, heightMeasureSpec, widthMeasureSpec MeasureSpec. GetSize (), MeasureSpec. GetMode (), MeasureSpec. So why go through MeasureSpec. GetMode ()? That’s the point of the rest.

    Here’s how getMode() is implemented with some code removed from MeasureSpec.

    private static final int MODE_SHIFT = 30; private static final int MODE_MASK = 0x3 << MODE_SHIFT; // MODE_MASK = 11(binary number); // MODE_MASK = 11(binary number); public static final int UNSPECIFIED = 0 << MODE_SHIFT; public static final int EXACTLY = 1 << MODE_SHIFT; public static final int AT_MOST = 2 << MODE_SHIFT; . Public static int measureSpec (widthMeasureSpec) {// widthMeasureSpec (widthMeasureSpec) {// widthMeasureSpec (widthMeasureSpec) // measureSpec = measureSpec = measureSpec // Compare the returned value with UNSPECIFIED, EXACTLY, and AT_MOST to obtain the measureSpec mode.return (measureSpec & MODE_MASK);
    }
        
    Copy the code

    MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec What does size have to do with it? UNSPECIFIED, EXACTLY, and AT_MOST, the View’s MeasureSpec modes are UNSPECIFIED, with the rules as follows: UNSPECIFIED, UNSPECIFIED, AT_MOST

    • UNSPECIFIED: Parent View has no restrictions on child views. Child views can be of any size, but they must be.Occurrence scene: For aScorllViewIn general, it doesn’t constrain the height of the subviews, in other words, how tall the subviews add up to, soScrollViewThat’s how high. In this case, the mode of the subview will beUNSPECIFIED.
    • EXACTLY: The parent view already has an exact size. The child view cannot exceed the size of the parent.Occurrence scene: Specify the size in parent View or asmatch_parentThe subview will get this mode.
    • AT_MOST: In this mode, the child view can be as large as possible to the set size or original size.Occurrence scene: Specify the size in parent View ormatch_parentIn the case of, the subview iswrap_content, then mode will beAT_MOST.

    Based on the above analysis of mode rules, we can know that to get the size in onMeasure, it needs to be made according to mode.

    So we can write the following code according to the rules:

    public static int getDefaultSize(int size, int measureSpec) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
    
        switch (specMode) {
        caseMeasureSpec.UNSPECIFIED: result = size; // If size is not specified, then we use the default sizebreak;
        case MeasureSpec.AT_MOST:
        caseMeasureSpec.EXACTLY: result = specSize; // Both modes allow us to use the measured size directlybreak;
        }
        return result;
    }
    Copy the code

2. ByonLayout()Method knows where the control should be placed. * Again, a bottom-up approach. * Usually we only have to rewriteViewGroupYou need to deal with it yourselfonLayout()Method, because the method is mainlyViewGroupUsed for placing sub-view positions (such as: horizontal or vertical placement, here students can refer to itLinearLayouttheonLayout()Method implementation), generally we only inherit the View to customize our custom View, do not need to override the method.It is important to note that the margin property of the child view is dependent on whether the parent handles the margin property in its own onLayout method, whereas the padding property of the view is dependent on the onDraw method.

Here are the highlights!!

Through 3.onDraw(Canvas canvas)Method to draw the control. Mainly throughPaintAnd parameterscanvasAnd various kinds ofAnimationAs well asinvalidate(),postInvalidate()These two methods are used to redraw the view to achieve dynamic effects.canvasThe method of drawing various shapes in: rect(rectangle), circle(circle), etc. Of course you can use itPath.PathMeasureTo do more detailed animations. The following combined with a very good example of the effect of source code, to illustrate the dynamic custom View.

@Override protected void onMeasure(int widthMeasureSpec, Int widthMode = MeasureSpec. GetMode (widthMeasureSpec);if( widthMode == MeasureSpec.EXACTLY )
            width = MeasureSpec.getSize(widthMeasureSpec);
        else width = ViewGroup.LayoutParams.MATCH_PARENT;


        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if( heightMode == MeasureSpec.EXACTLY )
            height = MeasureSpec.getSize(heightMeasureSpec);
        elseheight = ViewGroup.LayoutParams.MATCH_PARENT; // X leftX = margin; // rightX = width-margin; Y = height / 2.0 f; Distance = width - (2 * margin + 2 * radius); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @override protected void onDraw(Canvas Canvas) {// Draw the left Canvas. DrawCircle (leftX, y, radius * factor, mPaint); DrawCircle (rightX, y, radius * (1-factor), mPaint); drawCircle(rightX, y, radius * (1-factor), mPaint); mPaint.setStrokeWidth(5); DrawLine (margin, y, margin + radius+ ((radius + distance)*(1-factor)), y, mPaint); drawLine(margin, y, margin + radius+ (radius + distance)*(1-factor)), y, mPaint); } public voidstartAnimation(){
        mAnimator = ValueAnimator.ofFloat(1, 0);
        mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                factor = (float) animation.getAnimatedValue(); postInvalidate(); }}); mAnimator.setDuration(1500); Manimator.setrepeatcount (valueanimator.infinite); REVERSE: manimator.setrepeatMode (valueanimator.reverse); mAnimator.start(); }Copy the code

rendering


RequestLayout (), postInvalidate(), invalidate(

  1. In fact, the latter two do the same thing, exceptpostInvalidateInternally, the redraw operation is put into the child thread, andinvalidateRedraw the view in the calling thread.
  2. requestLayoutWhen will it be used? This method can be called to get the parent View to call the view again when the view’s measurement property changesonMeasure.onLayoutMethod to reevaluate the size and location of the view.

  • Enjoy Android 🙂 if there is any mistake, gently spray, welcome correction.