自定义View 流程

一、绘制基础

1、onDraw()

 override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas.drawXX(x,x,paint)
    }

负责自身内容主体绘制。super.onDraw()为空实现,写与不写都没影响

2、Paint 画笔

可实现多种效果,如渐变。参考:https://juejin.cn/post/6844903487570968584

 drawText,为什么TextView的宽度比实际要宽?

参考:https://juejin.cn/post/6844903488460177416#heading-23

 二、绘制顺序

1、super.onDraw() 前或后编写绘制代码?

写在下面,类似

override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas.drawXX(x,x,paint)
    }

把绘制代码写在 super.onDraw() 的下面,由于绘制代码会在原有内容绘制结束之后才执行,所以绘制内容就会盖住控件原来的内容。

相反,写在上面,会先绘制代码内容,然后在绘制原有内容

2、dispatchDraw():绘制子View的方法

public class SpottedLinearLayout extends LinearLayout { ... 
    protected void onDraw(Canvas canvas) { 
      super.onDraw(canvas); ... // 绘制斑点 
   } 
}

添加子view后,发现绘制斑点代码无效

<SpottedLinearLayout
    android:orientation="vertical"
    ... >

    <ImageView ... />

    <TextView ... />

</SpottedLinearLayout>

在绘制过程中,每一个ViewGroup会先绘制onDraw()主体内容,然后绘制子view,dispatchDraw()。上面的例子就是先绘制斑点,后绘制子view,导致斑点被覆盖。

怎样让主体绘制内容不被子view覆盖呢?方法是在子view绘制完后,再绘制斑点

public class SpottedLinearLayout extends LinearLayout {
    ...

    // 把 onDraw() 换成了 dispatchDraw()
    protected void dispatchDraw(Canvas canvas) {
       super.dispatchDraw(canvas);

       ... // 绘制斑点
    }
}

3、draw()方法

此方法是绘制过程中的总方法,执行顺序

View --- > darw(Canvas canvas)

  1. Draw the background 绘制背景
  2. Draw view's content 绘制自己的内容
  3. Draw chidren 绘制子View的内容
  4. Draw decorations(scrollbars for instance) 给View添加自己的装饰
View.java
 draw(){
   ······
   drawBackgroud()
   onDraw()
   dispathDraw()
   ······
}

在View中,dispatchDraw()为空实现,在ViewGroup中dispatchDraw()方法如下:

ViewGroup.java
····· dispathchDraw(){
   ······
   drawChild(canvas, child, drawingTime);
}

protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        return child.draw(canvas, this, drawingTime);
    }

重新调用view的draw方法,进行绘制。

ViewGroup的绘制过程,draw():
  1. ······
  2. drawBackgroup()
  3. onDraw()
  4. dispatchDraw()
  5. ``````

ViewGroup绘制方法执行顺序

onFinishInflate、onAttachedToWindow、onMeasure、onLayout、draw、onDraw、dispatchDraw

 

View绘制顺序:

onFinishInflate、onAttachedToWindow、onMeasure、onLayout、draw、onDraw、dispatchDraw

log发现,两者调用顺序一致。如果viewgroup中嵌套view,那么调用顺序如何?

子onFinishInflate、父onFinishInflate、父onMeasure、子onMeasure、父onLayout,子onLayout,父draw、ondraw、diapatchDraw,子draw、ondraw。

父布局测量、子布局测量;父布局定位、子布局定位;父布局绘制、子布局绘制

可在activity里onWindowFocusChanged方法里获取View的宽高

 override fun onWindowFocusChanged(hasFocus: Boolean) {
        super.onWindowFocusChanged(hasFocus)
        Log.e("====", " onWindowFocusChanged:" + binding.sdownView.width)
    }
E/====:  onWindowFocusChanged:1080

结合Activity的生命周期就是

                      onCreate -> View onFlinshInflate

                      -> onStart -> onResume->View onMeasure->View onLayout -> View onDraw

View.invalidate : 请求重绘View树,会调用onDraw()和computeScroll(),在UI线程执行

说明:请求重绘View树,即draw()过程,假如视图发生大小没有变化就不会调用layout()过程,并且只绘制那些“需要重绘的”
视图,即谁(View的话,只绘制该View ;ViewGroup,则绘制整个ViewGroup)请求invalidate()方法,就绘制该视图

View.postInvalidate:在非UI线程执行

View.requestLayout:依次调用onMeasure、onLayout、不会调用onDraw

三、onMeasure参数含义

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        Log.e("====", "==onMeasure:$widthMeasureSpec")
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    }

这两个参数,每一个都包含了两方面的信息,一个是测量模式(mode)信息,另一个尺寸大小信息。我们通过重写onMeasure方法决定视图的宽和高。一般,自定义View都应该要重写这个方法。重写了这个方法onMeasure后,记得调用setMeasuredDimension(int, int)保存该View、ViewGroup宽和高。

参考:

Android 自定义View之onMeasure、onLayout、onDraw

HenCoder Android 自定义 View 1-5: 绘制顺序

onSizeChanged、onDraw、onMeasure、onLayout 执行顺序和作用

自定义View详细解析

 


posted @ 2023-01-26 17:23  随易来了  阅读(217)  评论(0编辑  收藏  举报