美文网首页
自定义View基础 - onMeasure、Paint类、C

自定义View基础 - onMeasure、Paint类、C

作者: wayDevelop | 来源:发表于2018-09-27 15:08 被阅读0次

    onMeasure

    这意味着我们的自定义View到了处理自己的大小的时候。这是非常重要的方法,因为在大多数情况下,你的View需要有特定的大小以适应你的布局。

    当你重写此方法,需要记得的是,最终要设setMeasuredDimension(int width, int height) 。

    当处理自定义View的大小时候,使用者可能通过layout.xml或者动态设置了具体的大小。要正确地计算它,我们需要做几件事情。

    • 计算你的View内容所需的大小(宽度和高度)。

    • 获取你的View MeasureSpec大小和模式(宽度和高度)。

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    }
    
    • 检查MeasureSpec 设置和调整View(宽度和高度)的尺寸模式。
    int width;
    if (widthMode == MeasureSpec.EXACTLY) {
      width = widthSize;
    } else if (widthMode == MeasureSpec.AT_MOST) {
      width = Math.min(desiredWidth, widthSize);
    } else {
      width = desiredWidth;
    }
    

    注意:
    看看MeasureSpec的值:

    • MeasureSpec.EXACTLY 意味着硬编码大小值,所以你应该设置指定的宽度或高度。

    • MeasureSpec.AT_MOST 用于表明你的View匹配父View的大小,
      所以它应该和他想要的大小一样大。
      [此时View尺寸只要不超过父View允许的最大尺寸即可]

    • MeasureSpec.UNSPECIFIED 实际上是视图包装尺寸。因此,你可以使用上面计算所需的大小。

    在通过setMeasuredDimension设置最终值之前,以防万一,可以检查这些值不为负数。这可以避免在布局预览时一些问题。

    View 更新

    从View的生命周期图可以得知,可以重绘View自身有两种方法。invalidate()和requestLayout()方法会帮助你在运行时动态改变View状态。但为什么需要两个方法?

    invalidate()用来简单重绘View。例如更新其文本,色彩或触摸交互性。View将只调用onDraw()方法再次更新其状态。

    requestLayout()方法,你可以看到其将会从`onMeasure()开始更新View。这意味着你的View更新后,它改变它的大小,你需要再次测量它,并依赖于新的大小来重新绘制。

    动画

    在自定义View中,动画是一帧一帧的过程。这意味着,如果你想使一个圆半径从小变大,你将需要逐步增加半径并调用invalidate()来重绘它。

    在自定义View动画中,ValueAnimator是你的好朋友。下面这个类将帮助你从任何值开始执行动画到最后,甚至支持Interpolator(如果需要)。

    ValueAnimator animator = ValueAnimator.ofInt(0, 100);
    animator.setDuration(1000);
    animator.setInterpolator(new DecelerateInterpolator());
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      public void onAnimationUpdate(ValueAnimator animation) {
        int newRadius = (int) animation.getAnimatedValue();
      }
    });
    

    注意:
    当每一次新的动画值出来时,不要忘记调用invalidate()。

    Paint类

    Paint画笔对象,这个类中包含了如何绘制几何图形、文字和位图的样式和颜色信息,指定了如何绘制文本和图形。画笔对象右很多设置方法,大体上可以分为两类:一类与图形绘制有关,一类与文本绘制有关。

    Paint类中有如下方法:

    1、图形绘制:

      setStyle(Paint.Style s):设置画笔的样式:FILL实心;STROKE空心;FILL_OR_STROKE同时实心与空心;
      setStrokeCap(Paint.Cap c):当设置画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的图形样式;
      setStrokeWidth(float w):当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的粗细度;
      setAlpha(int a):设置绘制的图形的透明度;
      setColor(int color):设置绘制的颜色;
    
      setAntiAlias(boolean a):设置是否使用抗锯齿功能,抗锯齿功能会消耗较大资源,绘制图形的速度会减慢;
      setArgb(int a, int r, int g, int b):设置绘制的颜色,a表示透明度,r、g、b表示颜色值;
      setDither(boolean b):设置是否使用图像抖动处理,会使图像颜色更加平滑饱满,更加清晰;
      setFileterBitmap(Boolean b):设置是否在动画中滤掉Bitmap的优化,可以加快显示速度;
      setMaskFilter(MaskFilter mf):设置MaskFilter来实现滤镜的效果;
      setColorFilter(ColorFilter cf):设置颜色过滤器,可以在绘制颜色时实现不同颜色的变换效果;
      setPathEffect(PathEffect pe):设置绘制的路径的效果;
      setShader(Shader s):设置Shader绘制各种渐变效果;
      setShadowLayer(float r, int x, int y, int c):在图形下面设置阴影层,r为阴影角度,x和y为阴影在x轴和y轴上的距离,c为阴影的颜色;
      setStrokeJoin(Paint.Join j):设置绘制时各图形的结合方式;
      setXfermode(Xfermode m):设置图形重叠时的处理方式;
    

    2、文本绘制:

    1)  setTextAlign(Path.Align a):设置绘制的文本的对齐方式;
    2)  setTextScaleX(float s):设置文本在X轴的缩放比例,可以实现文字的拉伸效果;
    3)  setTextSize(float s):设置字号;
    4)  setTextSkewX(float s):设置斜体文字,s是文字倾斜度;
    5)  setTypeFace(TypeFace tf):设置字体风格,包括粗体、斜体等;
    6)  setUnderlineText(boolean b):设置绘制的文本是否带有下划线效果;
    7)  setStrikeThruText(boolean b):设置绘制的文本是否带有删除线效果;
    8)  setFakeBoldText(boolean b):模拟实现粗体文字,如果设置在小字体上效果会非常差;
    9)  setSubpixelText(boolean b):如果设置为true则有助于文本在LCD屏幕上显示效果;
    

    3、其他方法:

    1) getTextBounds(String t, int s, int e, Rect b):将页面中t文本从s下标开始到e下标结束的所有字符所占的区域宽高封装到b这个矩形中;
    2)  clearShadowLayer():清除阴影层;
    3)  measureText(String t, int s, int e):返回t文本中从s下标开始到e下标结束的所有字符所占的宽度;
    4)  reset():重置画笔为默认值。
    

    这里需要就几个方法解释一下:

    1、setPathEffect(PathEffect pe):设置绘制的路径的效果:

    常见的有以下几种可选方案:

    1)  CornerPathEffect:可以用圆角来代替尖锐的角;
    2)  DathPathEffect:虚线,由短线和点组成;
    3)  DiscretePathEffect:荆棘状的线条;
    4)  PathDashPathEffect:定义一种新的形状并将其作为原始路径的轮廓标记;
    5)  SumPathEffect:在一条路径中顺序添加参数中的效果;
    6)  ComposePathEffect:将两种效果组合起来,先使用第一种效果,在此基础上应用第二种效果。
    

    2、setXfermode(Xfermode m):设置图形重叠时的处理方式:

    关于Xfermode的多种效果,我们可以参考下面一张图:


    image.png

     在使用的时候,我们需要通过paint. setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XXX))来设置,XXX是上图中的某种模式对应的常量参数,如DST_OUT。

    这16中情况的具体解释如下:

    1.PorterDuff.Mode.CLEAR:所绘制不会提交到画布上。
    2.PorterDuff.Mode.SRC:显示上层绘制图片
    3.PorterDuff.Mode.DST:显示下层绘制图片
    4.PorterDuff.Mode.SRC_OVER:正常绘制显示,上下层绘制叠盖。
    5.PorterDuff.Mode.DST_OVER:上下层都显示。下层居上显示。
    6.PorterDuff.Mode.SRC_IN:取两层绘制交集。显示上层。
    7.PorterDuff.Mode.DST_IN:取两层绘制交集。显示下层。
    8.PorterDuff.Mode.SRC_OUT:上层绘制非交集部分。
    9.PorterDuff.Mode.DST_OUT:取下层绘制非交集部分。
    10.PorterDuff.Mode.SRC_ATOP:取下层非交集部分与上层交集部分
    11.PorterDuff.Mode.DST_ATOP:取上层非交集部分与下层交集部分
    12.PorterDuff.Mode.XOR:异或:去除两图层交集部分
    13.PorterDuff.Mode.DARKEN:取两图层全部区域,交集部分颜色加深
    14.PorterDuff.Mode.LIGHTEN:取两图层全部,点亮交集部分颜色
    15.PorterDuff.Mode.MULTIPLY:取两图层交集部分叠加后颜色
    16.PorterDuff.Mode.SCREEN:取两图层全部区域,交集部分变为透明色
    

    Canvas类

    Canvas即画布,其上可以使用Paint画笔对象绘制很多东西。

    Canvas对象中可以绘制:

    1)  drawArc():绘制圆弧;
    2)  drawBitmap():绘制Bitmap图像;
    3)  drawCircle():绘制圆圈;
    4)  drawLine():绘制线条;
    5)  drawOval():绘制椭圆;
    6)  drawPath():绘制Path路径;
    7)  drawPicture():绘制Picture图片;
    8)  drawRect():绘制矩形;
    9)  drawRoundRect():绘制圆角矩形;
    10) drawText():绘制文本;
    11) drawVertices():绘制顶点。
    
    

    Canvas对象的其他方法:

    1)  canvas.save():把当前绘制的图像保存起来,让后续的操作相当于是在一个新图层上绘制;
    2)  canvas.restore():把当前画布调整到上一个save()之前的状态;
    3)  canvas.translate(dx, dy):把当前画布的原点移到(dx, dy)点,后续操作都以(dx, dy)点作为参照;
    4)  canvas.scale(x, y):将当前画布在水平方向上缩放x倍,竖直方向上缩放y倍;
    5)  canvas.rotate(angle):将当前画布顺时针旋转angle度。
    
    其他知识点
    • onDetachedFromWindow() 方法调用时间
      如果你在自己的view中Override了这个方法。那么我们最关注的是它什么时候调用?
      从开发文档中我们可以看出,onAttachedToWindow是在第一次onDraw前调用的。也就是我们写的View在没有绘制出来时调用的,但只会调用一次。

    比如,我们写状态栏中的时钟的View,在onAttachedToWindow这方法中做初始化工作,比如注册一些广播等等……

    与onAttachedToWindow 相反的则是这个方法:

    • onAttachedToWindow() 方法调用时间
      开发文档就简单的两句。也就是我们销毁View的时候。我们写的这个View不再显示。
      这时我们就在这个方法做一些收尾工作,如:取消广播注册等等。

    • invalidate(); 调用重绘制布局的时候只会调用onDraw()方法

    • requestLayout();调用重绘制布局的时候只会调用 onMeasure OnLayout onDraw 三个方法

    相关文章

      网友评论

          本文标题:自定义View基础 - onMeasure、Paint类、C

          本文链接:https://www.haomeiwen.com/subject/uupeoftx.html