Advanced UI forcibly advanced: Customize ViewGroup to achieve the invincible special effects of nearly a thousand stars on Github and top the Trending list!

Preface

Let me first admit that I brag, the title is Near Thousand Stars (although only 700), and topped the Trending list (ranked third in the Kotlin category at the time). But I still have a motivated heart, bragging is innocent!

Everyone, put down the 40m big knife in your hand, let me run 39m first, and see what kind of work I can give you this time before deciding whether to cut me.

The same reasoning is true. I know that there is no picture that can't deceive people. First put the picture to see if the final effect is really so cool.

How is it that you still feel so cool? ?

This effect is a custom ViewGroup. I believe that many friends usually write more custom Views, but there are fewer custom ViewGroups. Mainly, there are too many things to consider in custom ViewGroup, what measurement is it, what layout is it, and sometimes sliding conflicts have to be considered, which is really troublesome and troublesome.

However, this article will take you a little bit of analysis of the effect to start, to achieve the effect, and then to deal with these troubles, so that you can easily learn to customize ViewGroup from the shallower to the deeper. Mom no longer has to worry that I will not customize ViewGroup anymore.

Author: Great God Mlx
link: https: //juejin.im/post/6873653741627637774
advanced UI Advanced B Station video explanation: https://www.bilibili.com/video/BV1fE411P7PA

Don't say much, please come here.

This is the Github address of this special effect: github.com/MlxChange/W...

Special effects analysis

Because it is a custom ViewGroup, the effect is quite complicated. When I first saw the original renderings, I rejected it in my heart. I can't say that your UI design is so cool. I don't consider the hard work of our programmers at all, and I resolutely do not do this kind of thing!

So I had a fight with UI, whoever wins depends on whom. Now we see the effect achieved, I just want to say really fragrant .

Okay, I'm not skinned anymore. Let's take a closer look at the effect.

First of all, this effect can be seen with many pages, just like a list. Remember that I saw an effect before to explore the kind of effect. Sliding left like it, sliding right doesn’t like this. So what I thought at first was to see if I could LayoutManagerinherit from a custom or custom View RecyclerView, but then I gave up because they are not easy to achieve the effect of previewing to the next interface, and finally decided to customize a ViewGroup. achieve.

I think the most special effect of this effect is that you can see the content of the next interface by dragging, just like pulling a curtain, and after sliding across the center, it can automatically slide to the other side with a rebound effect, and then display the next one. page. When the next page is fully displayed, the drag button is automatically generated at this time, and the next page is previewed again. And the button can also be pressed back. After pressing it back, a new button will be generated in the opposite direction to display the content of the previous page.

In summary, I have summarized a few points:

  1. Is a customViewGroup
  2. The sub-views are displayed in a stack, just like a list, the content of the sub-views can be customized, and the content of the next or previous page can be previewed.
  3. There is a drag button. When you pull it out, the button keeps getting bigger, and when you retract it keeps getting smaller, and it has a very smooth effect.
  4. When sliding completely to the other side, it will bounce a few times

In fact, I think other things are easier to implement. The more troublesome thing is this drag button. I decided to start with the drag button.

Drag button

Although I'm talking about a custom ViewGroup, you can't do it as soon as you make a custom View. I will write a custom View first to see if this drag button can be implemented. If this is not possible, then the following If you find an excuse to throw the pot to the UI, you don’t need to write it.

Let me first look at how this drag button is implemented.

Start with the button on the left. After all, the origin of the screen is in the upper left corner.

First, it is a small distance from the left, from top to bottom is a line, but there is a round, non-pointy protrusion in the middle. I don't care about this protrusion, I will draw this line first. Because it is a test, I create a TestView

class TestView  @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {}

Then draw the line. We might as well think about how to draw this line. Although it is indeed a line in the rendering, it also has content on the left side, so I think it is more like a slender rectangle, but there is a traitor (protrusion) in this slender rectangle. So drawing a line becomes a drawing rectangle, and because of the existence of this traitor, simple canvas drawing rectangles obviously cannot add this traitor to the subsequent rectangles, so we have to draw rectangles in another way, so what is it?

That's right, that is Path, the Pathsame effect can be achieved. So go ahead.

Draw rectangle

Define a brush and Path

var path= Path()
var paint =Paint()

init {
    paint.color=Color.RED //为了方便辨识,我们定义红色
    paint.style=Paint.Style.STROKE
    paint.isAntiAlias=true
}

How to draw a rectangle? In fact, it is exactly the same as we draw on paper. Draw a line from the origin to the right, then draw a line to the bottom, and then draw a line to the left, and finally close to the origin. Such a rectangle came out. The soul painter is me.

In fact, this drawing method is very good for us to achieve Path, and for convenience, we still define a center point centerX and centerY

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    path.lineTo(100f,0f)//100只是个测试距离,画上图步骤1
    path.lineTo(100f,centerY*2)//画步骤2
    path.lineTo(0f,centerY*2)//画步骤3
    path.close()//闭合,也就是画步骤4
    canvas.drawPath(path,paint)
}

override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
    super.onSizeChanged(w, h, oldw, oldh)
    centerX= (w/2).toFloat()//定义屏幕中心X值
    centerY= (h/2).toFloat()//定义屏幕中心Y值
}

Let's take a look at the effect

emmm looks interesting, but what the hell is the status bar? Ah, don't worry about it, it's just a transparent status bar.

Draw bumps

Then we now consider how to draw this protrusion?

Actually, it should be like this when drawing on paper

You see, if we draw on paper, it is still very simple, just need multiple protrusions on the line on the right. Three more steps, let's see how the code is implemented

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    path.lineTo(100f,0f)//上图第一步
    path.lineTo(100f,centerY-200)//上图第二步
    path.lineTo(180f,centerY)//第三步
    path.lineTo(100f,centerY+200)//第四步
    path.lineTo(100f,centerY*2)//第五步
    path.lineTo(0f,centerY*2)//第六步
    path.close()//第七步
    canvas.drawPath(path,paint)
}

How effective is it?

image

It looks a bit interesting~ But the effect of others looks very smooth, you are sharp, a bit different.

This Xiongtai, what you said makes sense, but I can't draw round ones. What to do? Only a sharp tip can sustain a life like this, all the old men in it are talented and nice, ah, wait, it’s not right, and go to the wrong set.

What is a circle? It's a curve, right? How do we draw curves in the computer? Let’s see if anyone knows, I’ll wait for you for ten minutes

Ah, ten minutes have passed, no one knows, I will say the answer.

Yes, it's a Bezier curve!

What, have you never heard of Bezier curves? Brother, you are out.

The Bezier curve is an algorithm for simulating the curve in the computer. Through the equation balabalabla, a bunch of definitions are omitted...

In short, the Bezier curve is to help us draw the curve, and the curve is determined by the position point and the control point. If you don’t know how to do it, I suggest you learn the basic usage of Bezier curves.

Let’s take a look at the second-order Bezier curve method

/**
 * 从上一个点开始,绘制二阶Bezier曲线
 * (x1,y1)为控制点, (x2,y2)为终点
 */
public void quadTo(float x1, float y1, float x2, float y2) ;

Then again, how should I draw with Bezier curves? Let’s try to draw on paper

The Bezier curve corresponds to step 3. The starting point is point A, the control point is point B, and the end point is point C.

What about the coordinates of this point? We assume that the length of step 1 is 100, the offset from point a to point B in the X direction is 100, and if the Y coordinate of point B is the center coordinate, the coordinate of point B is (100+100, centerY). Then we set the distance from A to C to 200, which should not be difficult to understand

Let's practice on the code

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    path.lineTo(100f,0f)//步骤1
    path.lineTo(100f,centerY-100)步骤2
    path.quadTo(200f,centerY,100f,centerY+100)//步骤3
    path.lineTo(100f,centerY*2)//步骤4
    path.lineTo(0f,centerY*2)//步骤5
    path.close()//步骤6
    canvas.drawPath(path,paint)
}

What is the effect? Let's see

Emmm doesn't seem to have that hot eyes anymore, but this protrusion is really a bit abrupt. How do you look at it, it feels like you stuffed money into the crew.

When you say this, I compare it with the renderings, and I feel that I have done this, and the boss is afraid that I will be fired immediately. But how did you make such a sleek special effect?

In fact, it is very simple. The second-order Bezier curve does not work, so let it be a brother of the third-order Bezier curve, and the third-order Bezier curve is not good, just the fourth-order fifth-order. . .

In fact, the final result is a sixth-order Bezier curve, as shown below

Points P1 and P8 are location points, and the rest are control points. The red line is the generated curve, is it sleek? Is the plan very simple?

Ah, don't fall the big sword! I admitted wrong! In fact, it is not simple. When I thought about it, it took a long time to come up with it.

Although Tier 6 is difficult to make, we can decompose it. Isn't it OK to decompose into two third-order Bezier curves?

We use P1 as the starting point, P4 as the end point, and P2 and P3 as the location points. Isn’t it easy for us to draw half and half?

The upper part becomes like this:

Let’s take a look at the third-order Bezier curve method

/*
 *  x1,y1是第一个控制点的位置,对应P2
 *  x2,y2是第二个控制点的位置, 对应P3
 *  x3,y3是最后一个位置点的位置,对应p4
 */
public void cubicTo(float x1, float y1, float x2, float y2,float x3, float y3)

So how are their coordinates determined?

Let's try to continue drawing on paper

P1, P4 are position points, P2, P3 are control points. So how to determine the distance of P2, P3, P4?

There are many websites that generate Bézier curves online. You generate four points, and the pendulum is almost the same. You can know the approximate number by looking at their coordinates.

I have tried it before. If P1 is (0,0), the coordinates of several points are as follows:

p2(0,100), p3(90,75), p4(100,150). Since we have determined the Y coordinate of P4 as centerY, P1 can only be (100,centerY-150). The code is as follows:

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    path.lineTo(100f,0f)//第一步
    path.lineTo(100f,centerY-150)//第二步
    path.cubicTo(100f,centerY-150f+100,100f+90f,centerY-150+75f,100f+100f,centerY)//第三步
    path.lineTo(100f,centerY*2)//第五步
    path.lineTo(0f,centerY*2)//第六步
    path.close()//第七步
    canvas.drawPath(path,paint)
}

Since, we only spent the upper part, so the fourth step in the above picture is not drawn. We mainly want to see if the upper part is the effect we want, run it, and the effect will come.

It seems that the effect of the upper part is much smoother, so I will draw a gourd and finish the lower part. In the second half, we will start with P4

p1 is the position of p4 in the upper part, compared with the relative position of the upper part, then the coordinates of p2, p3, p4 can be easily determined at this time

p1(100f+100f,centerY),p2(100f+90f,centerY+90f),p3(100f,centerY+75)

This is the code:

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    path.lineTo(100f,0f)//第一步
    path.lineTo(100f,centerY-150)//第二步
    path.cubicTo(100f,centerY-150f+100,100f+90f,centerY-150+75f,100f+100f,centerY)//第三步
    path.cubicTo(100f+90f,centerY+90f,100f,centerY+75f,100f,centerY+150f)//第四步
    path.lineTo(100f,centerY*2)//第五步
    path.lineTo(0f,centerY*2)//第六步
    path.close()//第七步
    canvas.drawPath(path,paint)
}

There is no reason to say nothing, let's see the effect is the last word

How about, the effect is very smooth and smooth, and it's not sharp anymore. Mlx, YES!

Now that this effect has been achieved, we start the next step, animation!

Drag animation

First of all, analyzing the above animation, we can find that this small protrusion will follow the finger to move up and down, and when the finger slides to the right, the protrusion will become larger, then we first consider the situation of sliding up and down regardless of the left and right situation.

Up and down animation

In the protrusion drawn above, the Y value we set for the highest point of the protrusion is centerY. If the protrusion moves, the relative positions of these points should not change. The only change is the highest point of the protrusion.

So the question is, how should the highest point of the protrusion change?

That's right, just follow your finger up and down. This is the situation now. The highest point of the protrusion is the position of the upper and lower half. The Y value is centerY. Now the Y value should follow the finger while the other relative distances remain unchanged.

Taking this picture as an example, the Y value of P4 changes, the X coordinate of P1, P2, P3 will not change, and the Y coordinate is based on the coordinate of P4. In this way, the protrusion can only change up and down.

In other words,

P1 (100, P4.y − 150), P2 (100, P4.y − 50), P3 (190, P4.y − 75) P4 (200, Y)

The only variable here is Y. Since Y follows the finger, it is the Y value where the finger is pressed. How to record it?

Hehehe, that's the onTouchEventway

We first define a variable currentY to record the current finger position, if there is no touch, the default is the center of the screen

var currentY=0f//记录当前手指触摸位置
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        centerX= (w/2).toFloat()
        centerY= (h/2).toFloat()
        currentY=centerY //默认为屏幕中心
}

Now we need to record the location of the touch

override fun onTouchEvent(event: MotionEvent): Boolean {
    when(event.action){
        MotionEvent.ACTION_MOVE-> {
            currentY=event.y
            invalidate()//重新绘制界面
        }
    }
    return true
}

It’s not enough to just do this. The place where we draw hasn’t been changed yet, and the P4 standard has not yet been adopted. Let's change it now. Was centerY, now replacing the old one, it should be currentYa

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    path.lineTo(100f,0f)
    path.lineTo(100f,currentY-150)
    //画上半部分
    path.cubicTo(100f,currentY-150f+100,100f+90f,currentY-150+75f,100f+100f,currentY)
    //画下半部分
    path.cubicTo(100f+90f,currentY+90f,100f,currentY+75f,100f,currentY+150f)
    path.lineTo(100f,centerY*2)
    path.lineTo(0f,centerY*2)
    path.close()
    canvas.drawPath(path,paint)
}

Aha, let's be happy to see the effect~

???

Brother, I'm really not kidding you, we made a small mistake. You think, path is a path, right? We have been adding paths, but the previous paths are still there, which means that these paths are superimposed. So we need to delete the original path every time after drawing the path.

like this:

override fun onDraw(canvas: Canvas) {
    ...
    canvas.drawPath(path,paint)//画路径
    path.reset()//画完之后删除之前的路径
}

So, let's take a look again?

Is this all right? It's perfect! I didn't lie to you~

Left and right animation

After the up and down matters have been processed, is it the turn of the left and right sliding events?

Let's analyze the effect again~

If we observe the renderings again, we will find

In the process of dragging outwards, this small protrusion will become larger, but when the protrusion reaches the center of the screen, it is the largest, and will not increase any further.

What do you mean when I get bigger? I suspect you are driving. . .

The protrusion becomes larger, it is easy to understand if you use a picture, we continue to draw on paper

The protrusion is like this at the beginning, and it will be like this when it gets bigger

Let's observe what has changed?

It can be clearly observed that the distance between P1 and P4 has changed. Let's call the distance between P1 and P4 in the Y direction as the radius of the protrusion. Because the distance from P1 to P4 is half of the entire protrusion in the Y direction~

Then the figure is obviously that the radius has become larger, and P1 to P4 have also become larger in the X direction.

Use another picture to represent

You can see that there is a blue rectangle. In simple terms, the protrusion becomes larger, which means that the blue rectangle keeps getting bigger. The length of this rectangle is the length of the entire protrusion, and the width of this rectangle is the width of the protrusion. Of course, what I painted here is not very similar, after all, the soul paints hands.

So the coordinates of P1, P2, P3, and P4 can be represented by this rectangle. We can define the length dragHeightand width of this rectangle dragWidth, the length corresponds to the Y direction, and the width corresponds to the X direction.

It's like this:

The width of the rectangle in the figure above is defined as dragWidthand the length of the rectangle is defined as dragHeight. After many tests and get the best results, the coordinate relationship is as follows. And we already know that the Y coordinate of P4 is the currentY of the finger touch. Then the final coordinates are as follows:

P1(x,currentY-dragHeight) , P2(x, (P1.Y+P4.Y)/2 + 12dp)
P3((x+dragWidth)∗0.94,(P1.Y+P4.Y)/2),P4(x+dragWidth,currentY)

and,

dragHeight = dragWidth ∗ 1.5

Did you see it? It dragHeight's up dragWidthto you. The coordinates of the four points of all the dragHeightdecisions come.

So dragWidthhow is it determined? Smart friends should have found out, yes, this dragWidth is determined by the distance that the finger drags left and right. However, for a good user experience, do not let your fingers block the drag button, so the highest point of the protrusion should be a little bit to the left of the place where the finger is pressed. Defined currentXas the X value of the finger touch point, then there is the following formula:

dragWidth=currentX−12dp

Some friends may have asked, what are you all about, a whole bunch of formulas come up, and it makes it unpleasant to play.

In fact, I have the same method. These parameters are indeed adjusted by me. You can use them directly. If you feel bad, you can also manually modify the following parameters.

So now that we have these parameters, let’s modify the previous code, first define the length and width of this rectangle

var dragWidth=0
var dragHeight=0

Then modify the coordinates of the previous control points and data points, but is it troublesome to fill in the data directly? I don't know which point is which point. So according to our graph, we define seven points, which correspond to points 1 to 8 in the figure below.

As usual, we can't define it in ondraw~ In order to make it easier for you to understand the picture, I named it directly according to the point in the picture. Everyone, look at the point I marked in red, that is the real point. And everyone, don't talk about my name. It should be noted that our curve is composed of two third-order Bezier curves,

//上半部分
private val point1=PointF(0f,0f)
private val point2=PointF(0f,0f)
private val point3=PointF(0f,0f)
//最右边
private val point4=PointF(0f,0f)
//下半部分
private val point5=PointF(0f,0f)
private val point6=PointF(0f,0f)
private val point7=PointF(0f,0f)

So we modify the drawing code:

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    //下面分别是设置对应的七个点的坐标
    point1.x=100f
    point1.y=currentY-dragHeight

    point2.x=100f
    point2.y=(currentY+point1.y)/2 + 30

    point3.x=(100f+dragWidth)*0.94f
    point3.y=(point1.y+currentY)/2

    point4.x=100f+dragWidth
    point4.y=currentY

    point7.x=100f
    point7.y=currentY+dragHeight

    point5.x=(100f+dragWidth)*0.94f
    point5.y=(currentY+point7.y)/2

    point6.x=100f
    point6.y=currentY+dragHeight/2 - 30

    //第一步
    path.lineTo(100f,0f)
    //第二步
    path.lineTo(point1.x,point1.y)
    //第三步,画上半部分
    path.cubicTo(point2.x,point2.y,point3.x,point3.y,point4.x,point4.y)
    //第四步,画下半部分
    path.cubicTo(point5.x,point5.y,point6.x,point6.y,point7.x,point7.y)
    //第五步
    path.lineTo(100f,centerY*2)
    //第六步
    path.lineTo(0f,centerY*2)
    //第七步
    path.close()
    canvas.drawPath(path,paint)
    path.reset()
}

OK, perfect, let’s run it to see the effect

You will find that there is no effect. . Hahaha, I am fooled by you, are you stupid?

Just make a joke to invigorate the atmosphere, after all, everyone has been watching for so long, maybe they are all tired, otherwise I will give you something to eat?

In fact, the effect is very simple. All our formulas are based on the length and width of the rectangle. Note that the rectangle refers to the half-protruding rectangle, which is the rectangle in the formula.

And now the length and width of our rectangle are 0, so of course it has no effect. Therefore, we need to set an initial value for the length and width of the rectangle. We know that the length of the rectangle is 1.5 times the width, so we only need to set an initial value for the width. Here the initial value is temporarily set to 100, that is

var dragWidth=100f
var dragHeight=0f
override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        dragHeight = dragWidth*1.5f
        ...
}

Having said that, the effect is not the same as before? What's the use

You have misunderstood brother dei. Now the size of our protrusion is determined entirely by the length and width of the rectangle, and the length of the rectangle is also determined by the width of the rectangle. In other words, the width of the rectangle determines the size of this protrusion. Then let us recall that if the right sliding protrusion becomes larger, in essence, the width of the rectangle becomes larger. We also mentioned this above.

So just change the width of the rectangle on the touch event, and the protrusion will change. If you don’t believe me, let’s try

override fun onTouchEvent(event: MotionEvent): Boolean {
    when(event.action){
        MotionEvent.ACTION_MOVE-> {
            currentY=event.y
            //更新矩形宽度
            dragWidth=event.x-30
            invalidate()
        }
    }
    return true
}

See how the effect is:

Hey, that's interesting. The effect is basically the same, but it's still two points.

  • Can only slide down the center of the screen, no more swipes
  • If it does not slide to the center of the screen, it will automatically return to the initial position.

We solve one by one

Limit sliding distance

In fact, this is very simple. If the X value of the current touch event exceeds centerX, let it remain centerX

override fun onTouchEvent(event: MotionEvent): Boolean {
    when(event.action){
        MotionEvent.ACTION_MOVE-> {
            currentY=event.y
            //判断当前触摸事件的X值是否超过了屏幕中心
            dragWidth = if(event.x>centerX){
                centerX-30
            }else{
                event.x-30
            }
            invalidate()
        }
    }
    return true
}

The change is very simple, the effect is not put here. It just can’t slide across the center of the screen.

Let go and return to the initial position

Before reaching the center of the screen, if we let go, we return to the initial position. Isn't it simple? Wouldn't it be enough to set the rectangle width to the initial value of 100?

That's right, let's judge first and let go. Where can you let it go? It must be in the touch event method.

override fun onTouchEvent(event: MotionEvent): Boolean {
    when(event.action){
        ...
        //手指离开屏幕
        MotionEvent.ACTION_UP->{
                //如果矩形宽度小于屏幕中心
                if(dragWidth < centerX){
                    dragWidth=100f //回到初始位置
                    invalidate() //更新界面
               }
        }
    }
    return true
}

Isn't it simple? Hand is coming~

Emmm, it's back to the initial position, but the other person went back a little bit. Your sudden return just caught me off guard. Without a little precaution~~~

How can I go back a little bit?

Ah, our old friend attribute animation has popped up again. Yes, it can only be achieved by relying on our old friend's attribute animation. Attribute animation is familiar to everyone, we first customize a attribute animation, and then let it slowly return to the initial position according to time. And here, we need to pay attention to the fact that the original effect has a retro effect, which is actually an interpolator OvershootInterpolator. Due to the length and effect of the custom estimator and interpolator, and these two customizations are very simple, I will not introduce them.

We started to define such a rebound animation

private val dragReboundAnimator = ValueAnimator.ofFloat(0f, 1f)
private var reboundLength = 0f //需要回弹的距离是多少
private var dragReboundX = 0f // 初始回弹地点的X值
init{
    ...
    dragReboundAnimator.doOnStart {
            reboundLength = dragWidth -100
            dragReboundX = dragWidth
   }
   dragReboundAnimator.duration = 700
   dragReboundAnimator.interpolator = OvershootInterpolator(3f)
   dragReboundAnimator.addUpdateListener {
      dragWidth = dragReboundX - it.animatedValue as Float * reboundLength
      invalidate()
  }
}

First, let me explain the meaning of these variables

  • dragReboundAnimatorIt defines an attribute animation, from 0 to 1, such as 0 0.1 0.2 to 1 like this.
  • reboundLengthWell, it is the distance that needs to rebound. For example, my dragWidth is the rectangle width is 400, and the initial position is 100, so the rebound distance is 300, which means that the distance of 300 needs a little rebound. If the width is 600, then the distance of 500 will go back a little bit.
  • dragReboundXWhen you first let go, I need to know where you started rebounding.

The whole process is like this. When a rebound is needed, I first record the initial position of the rebound and the distance that needs to be rebound, and then update the initial position of the rebound minus the distance of the rebound * animation value. What does that mean? That is, the initial distance is 600, the rebound distance is 600-100=500, and the initial value of the animation value is 0. That is as follows

600−500∗0=600
600- 5000.1 =550
600 -500
1 =100.

This achieves a little rebound. And we applied an OvershootInterpolator(3f)interpolator, which allows you to head over and then back to the correct position.

Let's take a look at the effect

Ok! The effect is already very good! It feels almost the same as the original picture. I believe it will be okay to optimize some details. This article will not be optimized for the time being, and I will talk about it in the next article.

summary

Yelling has written more than 7,000 words, but ViewGroupthere are too many things to customize , and there is a lot to talk about. So I have tried to be as concise as possible, it is not realistic to finish an article. Moreover, I believe that my friends are also tired after seeing this place and need to rest.

So I will make a summary here

If you customize the View, you really need to analyze the effect a little bit. First, make a similar one, and then slowly modify and optimize. This kind of thing is very important especially for beginners. So, everyone can slowly experience it based on my thinking.

So the final effect is ViewGroup, but most of what we are talking about in this article are custom Views. This is because the ultimate goal of custom ViewGroup is still the effect of custom View. If the effect cannot be achieved, everything is empty talk~

In the next section, I will explain how to pop to the other side and how to customize some knowledge of ViewGroup~ If you like, you can give me a thumbs up, follow me, and give me some more motivation.

Welcome to follow me, a little meow who likes to customize View and NDK and study the source code~

More Android series of tutorials, Android development advanced advanced study notes, golden nine silver ten interview topic series materials upload on GitHub: https://github.com/Timdk857/Android-Architecture-knowledge-2-

Guess you like

Origin blog.csdn.net/Androiddddd/article/details/108713489