Do your own job well, learn to think independently, find your true self in the vast sea of people, do not pay too much attention to the voice of the outside world, continue to learn and enrich yourself, everything will be better.Copy the code

Tip: At the beginning, I was at a loss about custom View. I couldn’t get started with it, especially when I was just doing my internship. I was afraid of being assigned tasks related to custom View. I here is not so much to introduce, as to my learning process and summary, I hope everyone can learn. First of all, I will introduce some of the best blogs and content I have read, so that you can learn.

  • CSDN rev ship blog.csdn.net/harvic88092…
  • HenCoder throw line hencoder.com/tag/hui-zhi…

Foreword: HERE I will not teach you how to draw, how to draw, and all kinds of canvas SAO operation, I will only teach you the relationship between measure, layout, draw, how to measure, how to place and how to draw View, and custom View we need to pay attention to what? People ask, can you customize everything? I will answer you very directly, I can not, but I can learn how to copy, we just need to know the principle, understand other people’s code, customize their own things will become very simple.

Custom View classification

  • Inherit directly from a View or ViewGroup:
    • This kind of difficulty will be more difficult, the View measure, layout (if it is ViewGroup) and draw are all controlled by ourselves, but the benefits are also obvious, the flexibility will be very high, the general development really not a lot, most of the use of GitHub to find a change on the line.
  • Inheriting existing controls (e.g. TextView, FrameLayout)
    • This kind of semi-finished product is quite common. For example, we often inherit LineaLayouut or FrameLayout, because the measure, layout (if it is ViewGroup) and draw of this View do not need to be controlled by ourselves, they have been realized. Keep the attributes and expand their capabilities. We often use a parent class, ViewGroup, where we put a bunch of views in there to provide methods that can be used and reused by other people.
  • Combine existing View controls.
    • The existing control according to the layout, synthesis of a, for the outside world to use.

The second and third methods above are often used in combination, such as inheriting FrameLayout, which puts some native controls inside and wraps them into a View for external calls.

The main method of custom View is introduced

  • onMeasure
    • Suitable for the View and ViewGroup, this method just as its name implies is to measure the View, used to determine the size of the current View, when after this method has been completed, we can obtain its wide by getMeasuredWidth or getMeasuredHeight high. Of course, this size is only the theoretical size of the measurement, is generally accurate, the final size needs to be determined by onLayout. (The onMeasure of a ViewGroup is actually a measure of all the sub-views. If all the sub-views are measured, then the ViewGroup can be measured. It’s easy to understand that the measurement of a ViewGroup is actually the measurement of a View.)
  • onLayout
    • This is the method that the ViewGroup needs to override to place its child views, and when the ViewGroup is measured, we know how big the ViewGroup is, how much space it takes up, and then we can place its child views, This method is used to implement emplacement. GetWidth or getHeight is used to measure the measuredWidth, which is usually the same as getMeasuredWidth.
  • onDraw
    • So with viewGroups and Views, you can rewrite this method and draw whatever you want, and I’m going to talk a little bit about how the order of drawing affects the View, foreground drawing, background drawing, etc.

Understand the MeasureSpec

A MeasureSpec is a MeasureSpec, and a MeasureSpec is a MeasureSpec, and a MeasureSpec is a MeasureSpec, and a MeasureSpec is a MeasureSpec. A MeasureSpec is a MeasureSpec, and a MeasureSpec is a MeasureSpec. The size of the View is 20dp, match_parent, match_parent, match_parent, match_parent, match_parent, match_parent.

  • UNSPECIFIED

The parent container has no restrictions on the child View, so it should be used as little as possible. I use very little.

  • EXACTLY

When the child View is match_parent or a specific value, this mode corresponds to the child View. Some people ask, why is match_parent also accurately measured?

  • AT_MOST

The parent View has an available size, and the child View cannot exceed the size specified by the parent View, which corresponds to wrap_content. If not, can the above two modes exceed the size, I usually write specific size, I also do not see the child View can exceed the parent View ah? What’s the difference? You’re so abstract, I don’t understand.

Before we can solve this problem, we need to understand the relationship between MeasureSpec and LayoutParams.

MeasureSpec and LayoutParams
  • MeasureSpec is generated based on the parent View’s constraints and its own layout parameters. There are two cases. For a top-level View, i.e. a DecorView is a more window size and its own layout parameters. A normal View is determined by its parent’s MeasureSpec and its own parameters.

First is Decorview, we can trace to ViewRootImpl measureHierarchy, get MeasureSpec by getRootMeasureSpec, core code is as follows:

private static int getRootMeasureSpec(int windowSize, int rootDimension) { int measureSpec; Switch (rootDimension) {// The current layout parameter is to fill the parent layout, the size of the parent layout, is windowSize, then the size can be determined, / / is exactly is easy to understand case ViewGroup. LayoutParams. MATCH_PARENT: // Window can't resize. Force root view to be windowSize. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); break; // The size of the current Wind is the maximum size of the current View, which corresponds to at_most. Most from this we can understand the case ViewGroup. LayoutParams. WRAP_CONTENT: // Window can resize. Set max size for root view. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST); break; default: // Window wants to be an exact size. Force root view to be that size. So also is exactly measureSpec = measureSpec. MakeMeasureSpec (rootDimension, measureSpec. Exactly); break; } return measureSpec; }Copy the code

Next, let’s look at the general way we get a MeasureSpec, and how the code works. Let’s look at getChildMeasureSpec, which is usually used to measure child Views when we customize components like viewGroups.

Int specMode = MeasureSpec. Int specSize = MeasureSpec. GetSize (spec); 1 int size = math.max (0, specsie-padding); int resultSize = 0; int resultMode = 0; switch (specMode) { // Parent has imposed an exact size on us case MeasureSpec.EXACTLY: // 2 if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size. So be it. resultSize = size; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parent has imposed a maximum size on us case MeasureSpec.AT_MOST: // 3 if (childDimension >= 0) { // Child wants a specific size... so be it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size, but our size is not fixed. // Constrain child to not be bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parent asked to see how big we want to be case MeasureSpec.UNSPECIFIED: // 4 if (childDimension >= 0) { // Child wants a specific size... let him have it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size... find out how big it should // be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how // big it should be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } break; } // NoInspection ResourceType // Determine the MeasureSpec return MeasureSpec. MakeMeasureSpec (resultSize resultMode); }Copy the code

A MeasureSpec is a MeasureSpec for a child View. Strive for the best results by combining LayoutParams with parent ViewMeasureSpec and child Views. For example, if the current child View knows its size (because its measurement mode is exactly), or if the word View wants to be the same size as the parent View, then the parent View should give the child the exact size.

Give a child View a good MeasureSpec to determine the size of the child View, and at the same time, as much as possible to meet the needs of the child View, don’t compromise it 😝

Detailed explanation:

  • 1- This size is the remaining space available to the child View. The Spec is the parent View’s MeasureSpec passed by the caller. The padding is the size of the parent View’s padding or Margin. This is the size of the parent View, and this size-padding is the remaining control, which is the maximum space available to the child View.

  • 2- If the parent View’s specMode is exactly

    • ChildDimension >= 0, the size of the child View is written manually. In this case, the child View needs this size, OK, I’ll give you this childDimension size, then your size is exactly what it is.
    • ChildDimension = = LayoutParams. MATCH_PARENT. The child View needs to fill the parent View, so the child View’s size should fill all the remaining space, =size, the size is also determined, again exactly.
    • ChildDimension == layoutParams.wrap_content; childDimension == layoutParams.wrap_content; childDimension == layoutparams.wrap_content; childDimension == layoutParams.wrap_content; The measurement mode is AT_MOST.
  • 3- If the parent View’s specMode is AT_most

    • ChildDimension >= 0, also, the child View needs to make its own size, so it will give you as much childDimension as possible. (Add size
    • ChildDimension == layoutparams.match_parent. If the parent View wants to be full, make it at_most, no larger than the parent View.
    • ChildDimension == layoutparams.wrap_content, also the parent View size is not determined, so let the child View be at_most, not larger than the parent View size, also size.
  • UNSPECIFIED 4- The parent View is a MeasureSpec.UNSPECIFIED (The parent View has no restrictions on the child, however it is UNSPECIFIED)

    • ChildDimension >= 0, the size of the child View is manually specified to meet your requirements, and the size of the child View is childDimension, still exactly.
    • ChildDimension = = LayoutParams. MATCH_PARENT. The child View needs to be loaded with the parent View, which has no limits on the size of the child View. As such, you can still have unlimited size. Your measurement mode is UNSPECIFIED.
    • ChildDimension == LayoutParams.wrap_content, the child View has an UNSPECIFIED size, the parent View is UNSPECIFIED, and the measurement mode is UNSPECIFIED.

MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec

Three ways to understand the core

onMeasure

First of all, let’s look at the onMeasure method of the default View class (ViewGroup is not analyzed, simple to understand is to Measure each child View through the for loop, and then sum the size, the core still needs to focus on the View’s Measure method, I will have a practical analysis later, can ignore for now).

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }
Copy the code

SetMeasuredDimension: This method sets the calculated target dimension to the View.

public static int getDefaultSize(int size, int measureSpec) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
        }
        return result;
    }
Copy the code

This method is the one we care more about, providing the default measurement of the View, and then analyzing the measurement mode of the sub-view.

  • UNSPECIFIED or UNSPECIFIED, the UNSPECIFIED child View gets the default size, size. (Don’t ask me why, the source code is written this way, why not get specSize, HOW do I know.) UNSPECIFIED, this is currently unavailable, and as it is still at the View level, you don’t need to pay special attention to it.
  • A child View is a parent View’s MeasureSpec size, which is EXACTLY the same as the parent View’s MeasureSpec size. It’s also fairly accurate.

Summary (refer to the above analysis method) :

When the View is exactly, there are several cases where parent View arbitrary mode + child View determine Dimension parent View is exactly+ child View is match_parent

If the current View is at_most, then we also have the following situation where the parent View is at_most, the child View is match_parent,wrap_content and the parent View is exactly, Child View wrap_content will cause child View to be AT_MOST, will fill parent View

If you want the custom to be able to control your own size and not be full of parent layout (if you don’t want to be full, if you define match_parent as you expect), you need to override the onMeasure to determine your own size. Use setDimension to finally set your size.

onLayout

So this is the ViewGroup method, which is used to place a child View that you’re measuring, and it’s very easy to place a View using the view. layout method. Why is getMeasureWidth obtained after onMeasure different from getWidth obtained after onLayout?

Normally, the measuredWidth and getWidth are the same size, because if you measure the measuredWidth, you can set the measuredWidth directly. If you measure the measuredWidth, your getWidth is right-left, so the measuredWidth and getWidth are equal. But if you have any special needs, your right or left changes, it can cause getWidth! = measuredWidth.

onMeasure

This method is used by both views and viewgroups to draw what you need (I won’t go into the details of how to use a canvas), but let me show you that drawing in different order will have different effects.

  • Insert the draw code below super.ondraw () so that the draw content covers the original body content
  • Move the drawing code below above super.ondraw () to cover your drawing code with the original body content
  • Insert setWillNotDraw(False) here to enable the full drawing process
  • Change onDraw() to dispatchDraw() so that the drawing content can cover the child View, and the setWillNotDraw(false) above can also be deleted after the change
  • Insert draw code below super.ondrawforeground () so that the draw content covers the foreground
  • Insert draw code above super.ondrawforeground () so that the draw content is covered by foreground

conclusion

Here, basically finished, I focus on the core process, how to draw and measure, you need to understand to try, more attention is measurement, how to generate various MeasureSpec, and LayoutParams relationship is what, when you need to define the width and height of the line, understand these is enough, I’m going to have a demo and the source code.