Small note: yesterday friends together, talk very happy, midnight wine scattered, thirsty wake up, thinking divergent. Get up early, a ray of sunshine, a cigarette, a cup of green tea, a simple table, quiet, comfortable, free.

Writing in the front

Original article, reprint please retain the source! www.jianshu.com/p/e2ddf2ae2…

This article will cover the details of custom controls. You will learn about custom controls as follows:

  • Several common custom controls
  • Why use it
  • Usage scenarios of onMeasure, onLayout, and onDraw
  • Overall writing process
  • What do custom attributes do and how do they work
  • Several cases of personal writing

Several common custom controls

  1. Composite property extension controls that leverage common apis to provide multiple functions for a single control (e.g., an immersive toolbar; A TextView with a click event;)
  2. Combined control, control pieces according to business related to each other, external use as a whole control, to achieve independent use (such as: click to hide the extensible TextView; SwipeRefreshLayout + RecyclerView
  3. Controls that have specific display effects (dashboards; Player’s audio bounce bar;)

Why use custom controls

There are two reasons why I use it:

  1. From a product function standpoint – the system’s existing apis do not achieve a specific effect
  2. Starting from the code structure – make the control package combination, external single exposure, to achieve object-oriented effect, convenient maintenance and reuse

Usage scenarios of onMeasure(), onLayout() and onDraw()

Note: the above three methods are used according to the specific situation, not at the same time. The following will explain what each method is used for and how to use it

– onMeasure (introduce its responsibilities and how you use the operation)

OnMeasure – Measures the control to set its size. Popularize a common sense, easy to understand: For example, if you write a textView, you need to set the width and height, you can set wrap_content, you can set match_parent, you can set a specific value, and then when you compile the textView, the system will call onMeasure to measure the size of the control according to the value you set. (According to the text size, the system default control spacing) note that this is only measured, not including where it is displayed and draw (display) so how do you use it? 1 – Override onMeasure(int widthMeasureSpec, int heightMeasureSpec). Ps: To clarify, here is an explanation. OnMeasure measurement, the system determines the size by two aspects: mode and size (specMode and specSize). SpaceMode has three values — 1, which is MeasureSpec.EXACTLY when the control is match_parent or dp; When the width and height of the control is wrAP_content, the value is MeasureSpec. UNSPECIFIED values as MeasureSpec.UNSPECIFIED values represent the UNSPECIFIED size of the control. This is unusual because sliding the control 2 – obtains the width and height of the control according to its parameters

int defaultSize = 200; @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int height = measureSize(defaultSize, heightMeasureSpec); int width = measureSize(defaultSize, widthMeasureSpec); SetMeasuredDimension (width, height); setMeasuredDimension(width, height); } private int measureSize(int defaultSize, int measureSpec) { int result = defaultSize; int specMode = View.MeasureSpec.getMode(measureSpec); int specSize = View.MeasureSpec.getSize(measureSpec); If (specMode == view.measurespec.EXACTLY) {// When the layout width is match_parent or a specific dp result = specSize; } else if (specMode == view.measurespec.AT_MOST) {// When the layout width is wrap_content, The default value defaultSize} else if (specMode == MeasureSpec.UNSPECIFIED) {} return result; }Copy the code

– onLayout (describes its responsibilities and how you use the operation)

  1. OnLayout is responsible for the position of the control in the layout. This is used to determine the position of the subview in the ViewGroup. This is called when the View sets the size and position of its children.
  2. How to use it? The child View must override the onLayout(Boolean, int, int, int, int) method and call the respective Layout(int, int, int, int) method.

API: onLayout(boolean changed, int left, int top, int right, int bottom); Parameter explanation:

1) The changed parameter indicates that the view has a new size or position; 2) The parameter L represents the Left position relative to the parent view; 3) The parameter t represents the Top position relative to the parent view; 4) The r parameter represents the Right position relative to the parent view; 5) The parameter b represents the Bottom position relative to the parent view;Copy the code

Note that the Y-axis is positive downward





Coordinate diagram. PNG


2 – General usage:

Int childCount = getChildCount(); View for (int I = 0; i < childCount; i++) { View childView = getChildAt(i); / / get the height of the child View int childViewHeight = childView. GetMeasuredHeight (); / / get the width of the child View int childViewWidth = childView. GetMeasuredWidth (); // The core API calls Layout to position each child View (depending on UI requirements, Childview. layout(mLeft, height, mLeft + childViewWidth, height + childViewHeight); }Copy the code

– onDraw (describe its responsibilities and how you use the operation)

  • OnDraw is responsible for drawing and displaying controls

    API : onDraw(Canvas canvas)

    There are many methods in Canvas. Canvas can draw objects as follows:

    Arc (arcs) canvas.

    Fill colors (ARGB and color)

    Bitmap

    Round (circle and oval),

    Point (point)

    Line (line)

    The rectangle (the Rect)

    Image (Picture)

    RoundRect

    Text (text)

    Vertex (are)

    Path (path)
  • Draw the circle as follows:
rectPaint = new Paint(); rectPaint.setStyle(Paint.Style.STROKE); // Just stroke rectpaint.setstrokeWidth (barWidth); rectPaint.setColor(Color.parseColor("#F2935B")); @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawArc(mRectF, 0, 360, false, circleBgPaint); }Copy the code

Both invalidate() and postInvalidate() call OnDraw() to actively update the UI. The former is used in the UI thread itself, while the latter is used in non-UI threads.

Overall writing process

  • There is no specific writing process for custom controls; it depends on the specific product and UI requirements. Sometimes you don’t need to override onMeasure, onLayout, and onDraw.Customize the Toolbar with an immersive status bar, as in the demo below), sometimes you need to rewrite some of the methods (For example, the custom dynamic audio jump bar in demo and a ring to drive the drawing progress). However, custom controls have a certain drawing process, that is, the execution sequence.

    Drawing process:

    onFinishInflate()

    onAttachedToWindow()

    onMeasure(int widthMeasureSpec, int heightMeasureSpec)

    onLayout(boolean changed, int l, int t, int r, int b)

    onDraw(Canvas canvas)

    onDetachedFromWindow()

What do custom attributes do and how do they work

  • Function or meaning, so you know when to use it and why.
  1. Role 1: Extends properties that the system does not have. For example, if your business scenario needs to define a progress bar that determines the arc Angle by value, you need to define the color of the progress bar, the width of the progress bar, the starting Angle, etc., then you can define these properties of the control. Of course, more seriously you might find that properties like these can be implemented through the system API, and yes, they are, which is why function 2 is introduced.
  2. Function 2: Custom properties can make your controls dynamically configurable, allowing your controls to dynamically control size, width, height, speed, color, etc. Because your control now holds properties of specific meaning, you can configure the values of different properties in XML to make your control “live”. Bottom line: What you end up doing is setting the value of the property in XML, retrieving the value of the property in your custom control code, and executing the logic based on that value to achieve your effect
  • How does it work?

Cut the crap. Go to the code.

  1. Define the attributes you need (in res-values-hattri.xml)
    <declare-styleable name="CircleBarView">
        <attr name="circle_color" format="color"></attr>
        <attr name="circle_bg_color" format="color"></attr>
        <attr name="circle_stoken_width" format="dimension"></attr>
        <attr name="start_angle" format="float"></attr>
    </declare-styleable>
Copy the code

ps: declare-styleable name = ***; This represents the overall reference name for the attribute you define

  1. Sets control property values in the XML
<? The XML version = "1.0" encoding = "utf-8"? > <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content"> <ui.customview.CircleBarView android:id="@+id/circle_bar_view" android:layout_width="match_parent" android:layout_height="match_parent" app:circle_bg_color="@android:color/darker_gray" app:circle_color="@android:color/holo_green_dark" app:circle_stoken_width="20dp" app:start_angle="90" /> </RelativeLayout>Copy the code

: under this statement about custom attributes, you need to introduce namespace XMLNS: introduction in with layout app = "http://schemas.android.com/apk/res-auto", control index attributes defined by namespace app

  1. Gets the value of the property in the code
public CircleBarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } private void init(Context context, @ Nullable AttributeSet attrs) {/ / get all attributes by overall properties reference name TypedArray TypedArray = context. ObtainStyledAttributes (attrs. R.styleable.CircleBarView); // Parameter 1- the value of the corresponding attribute in XML, Parameter 2 - fails to get the default display the value of the int circleBgColor = typedArray. GetColor (R.s tyleable CircleBarView_circle_bg_color, Color. GRAY); int circleColor = typedArray.getColor(R.styleable.CircleBarView_circle_color, Color.GREEN); barWidth = typedArray.getDimension(R.styleable.CircleBarView_circle_stoken_width, px2dip(context, 10)); startAngle = typedArray.getFloat(R.styleable.CircleBarView_start_angle, 0); // Remember to recycle typeDarray.recycle (); }Copy the code

Through the above three steps to define the property and value process, you actually end up setting the value of the related property in XML, then in the custom control code to obtain the value of the property, based on the value of the related logic to achieve your effect

Note: The format of the attR attribute is as follows: 1. Reference: Reference the resource ID specified in the Theme. This type means that the value you pass can be a reference resource 2. The string: a string, if you want people can directly write value also can use a similar "@ string/test" reference resources, can be written as the format = "string | reference" 3. Color: Color 4. Boolean: the Boolean value 5. Dimension: Size value 6. Float: floating point 7. Integer: integer 8. <attr name="language"> <enum name=" China "value="1"/> <enum name="English" Value ="2"/> </attr> 10. Flag: bit or operationCopy the code

Several cases of personal writing

Ps: Demo has been adding and adding, which includes a little complex things, but also simple according to the function of the subcontracting. Including thread interruption, application restart, dynamic and static proxy, immersion and custom control three, if only see the relevant cases in this article, please move to the project UI package, which notes the detailed project source code Demo address, if there is help please star, the demo long-term update

Dull coquettish code male, code word is not easy, please raise your little hands, a praise ~