小试牛刀-onDraw方法

onDraw也有个类似的方法draw方法,还是接着以前的套路,先分析一下draw方法再去具体分析onDraw方法。

draw

  关于draw,源码中有很长一段注释,解释了draw到底做了什么事情。

        /*
         * Draw traversal performs several drawing steps which must be executed
         * in the appropriate order:
         *      1. Draw the background
         *      2. If necessary, save the canvas' layers to prepare for fading
         *      3. Draw view's content
         *      4. Draw children
         *      5. If necessary, draw the fading edges and restore layers
         *      6. Draw decorations (scrollbars for instance)
         */

  简答理解一下draw执行了六个步骤:

  1. 绘制背景
  2. 如果需要,保存canvas的图层信息
  3. 绘制View的内容
  4. 如果有子View,绘制子View
  5. 如果需要,绘制View的边缘等(如阴影)
  6. 绘制 View 的装饰(如滚动)。
   @CallSuper
    public void draw(Canvas canvas) {
        ......
     // Step 1, draw the background, if needed
        int saveCount;

        if (!dirtyOpaque) {
            drawBackground(canvas);
        }

        ......

      // Step 2, save the canvas' layers
        int paddingLeft = mPaddingLeft;

        final boolean offsetRequired = isPaddingOffsetRequired();
        if (offsetRequired) {
            paddingLeft += getLeftPaddingOffset();
        }
        ......

          if (solidColor == 0) {
            final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;

            if (drawTop) {
                canvas.saveLayer(left, top, right, top + length, null, flags);
            }

            if (drawBottom) {
                canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
            }

            if (drawLeft) {
                canvas.saveLayer(left, top, left + length, bottom, null, flags);
            }

            if (drawRight) {
                canvas.saveLayer(right - length, top, right, bottom, null, flags);
            }
        } else {
            scrollabilityCache.setFadeColor(solidColor);
        }
      
      ......

        // Step 3, draw the content
        if (!dirtyOpaque) onDraw(canvas);

        // Step 4, draw the children
        dispatchDraw(canvas);

        // Step 5, draw the fade effect and restore layers
        final Paint p = scrollabilityCache.paint;
        final Matrix matrix = scrollabilityCache.matrix;
        final Shader fade = scrollabilityCache.shader;
        if (drawTop) {
              ......
        }
        if (drawBottom) {
              ......
        }
        if (drawLeft) {
              ......
        }
        if (drawRight) {
              ......
        }
       canvas.restoreToCount(saveCount);

        drawAutofilledHighlight(canvas);

        // Overlay is part of the content and draws beneath Foreground
        if (mOverlay != null && !mOverlay.isEmpty()) {
            mOverlay.getOverlayView().dispatchDraw(canvas);
        }

        // Step 6, draw decorations (foreground, scrollbars)
        onDrawForeground(canvas);

        if (debugDraw()) {
            debugDrawFocus(canvas);
        }
      }

  step3:绘制内容的时候调用的View的onDraw(canvas);方法,由于View的内容各不相同所以onDraw(canvas)是一个空方法,需要子类自己去实现。


    /**
     * Implement this to do your drawing.
     *
     * @param canvas the canvas on which the background will be drawn
     */
    protected void onDraw(Canvas canvas) {
    }

  step4:绘制子View的时候调用dispatchDraw方法,该方法还是一个空方法需要子类自己去实现。因为当只有子View的时候才会去重写,所以看下ViewGroup是怎么实现的。

    /**
     * Called by draw to draw the child views. This may be overridden
     * by derived classes to gain control just before its children are drawn
     * (but after its own view has been drawn).
     * @param canvas the canvas on which to draw the view
     */
    protected void dispatchDraw(Canvas canvas) {

    }

  ViewGroup实现dispatchDraw dispatchDraw(Canvas canvas),由于ViewGroup的dispatchDraw方法内容实现比较多,直接看关键实现

protected void dispatchDraw(Canvas canvas) {
           ......
    for (int i = 0; i < childrenCount; i++) {
            ......
            if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
                    transientChild.getAnimation() != null) {
                more |= drawChild(canvas, transientChild, drawingTime);
            }
            ......
    }
}

  遍历了所有的子 View 并调用了 ViewGroup的drawChild 方法,在看下drawChild是怎么实现的

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

  最终发现还是调用的draw方法,因为ViewGroup已经帮我实现好了dispatchDraw(Canvas canvas)方法,所有我们开发中不需要自己去重写,只需要重写onDraw(Canvas canvas)方法即可。

   所以draw是绘制的方法,但是具体如何去绘制子View的内容就需要重写onDraw方法,再看下onDraw方法。

  由于onDraw是一个空方法,具体实现看子类,但是又个关键的参数Canvas,字面意思就是画布,绘制内容光有画布肯定是不行的,还需要画笔,所以有个关于画笔的类Paint,只有画布和画笔结合才能绘制出内容

Paint的几个最常用的方法

  • Paint.setStyle(Style style) 设置绘制模式
Style 效果
Paint.Style.FILL 填充
Paint.Style.FILL_AND_STROKE 描边并填充
Paint.Style.STROKE 描边
//设置透明度,范围为0~255
void  setAlpha(int a)
//是否开启抗锯齿
void  setAntiAlias(boolean aa)
//设置颜色
void  setColor(int color)
//s设置颜色过滤
ColorFilter setColorFilter (ColorFilter filter)
//设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰
void setDither(boolean dither)
//设置线条宽度
void setStrokeWidth(float width) 
//设置文字大小
void setTextSize(float textSize)

Canvas的方法都是以draw开头的

先看先Canvas的Api

  Canvas可以绘制弧线(Arc),绘制填充色(ARGB和Color),绘制Bitmap,圆(circle和oval),点(point),线(line),矩形(Rect),图片(Picture),圆角矩形 (RoundRect),文本(text),顶点(Vertices),路径(path)

在了解Canvas之前,先熟悉一下Android下的坐标系

Android中的每个View都有自己的坐标系,不会相互影响。坐标的原点在屏幕的左上角。水平方向是X轴,向右为正,向左为负。垂直方向是Y轴,向下为正,向上为负数。


View坐标系

void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,@NonNull Paint paint)


  @Override protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //绘制弧线区域
    RectF rect = new RectF(0, 0, 300, 300);
    canvas.drawArc(rect, //弧线所使用的矩形区域大小
        0,  //开始角度
        300, //扫过的角度
        true, //是否使用中心
        paint);
  }
useCenter == true
useCenter == false

  drawArc绘制的区域是起始角度和结束角度连接起来的,其中useCenter为true时圆弧开始角度和结束角度都与中心连接。

drawCircle(float cx, float cy, float radius, @NonNull Paint paint)

  @Override protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //圆心坐标点X,圆心坐标点Y,半径,画笔
    canvas.drawCircle(100,100,90,paint);
  }
drawCircle

drawColor(@ColorInt int color)

  @Override protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawColor(Color.RED);
  }
drawColor

绘制区域填充指定颜色,drawRGB(int r, int g, int b)也是同样的

drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint)

  @Override protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher);
  //bitmap对象,bitmap左侧的位置,bitmap顶部的位置,画笔
   canvas.drawBitmap(bitmap,300,300,new Paint());
  }
drawBitmap

drawLine(float startX, float startY, float stopX, float stopY,
@NonNull Paint paint)

  @Override protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //绘制直线
    //起始点X坐标,起始点Y坐标,重点X坐标,终点Y坐标,笔画
    canvas.drawLine(100,100,600,200,paint);
  }
drawLine

drawRect(@NonNull RectF rect, @NonNull Paint paint)

@Override protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //定义一个矩形区域
    //左,上,右,下
    RectF rect = new RectF(300,300,700,700);
    //绘制矩形
    canvas.drawRect(rect,paint);
  }

  @Override protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //定义一个矩形区域
    //左,上,右,下
    RectF rect = new RectF(300,300,700,700);
    //绘制带圆角的矩形
    canvas.drawRoundRect(rect,
        100,//x轴的半径
        100,//Y轴的半径
        paint);
  }
drawRect
drawRoundRect

drawOval(@NonNull RectF oval, @NonNull Paint paint)

  @Override protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //定义一个矩形区域
    RectF rect = new RectF(0,0,500,200);
    //绘制内切椭圆
    canvas.drawOval(rect,paint);
  }
drawOval

drawText(@NonNull String text, float x, float y, @NonNull Paint paint)

  @Override protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    String text = "Android";
    Rect textRect = new Rect();
    paint.getTextBounds(text, 0, text.length(), textRect);
    //获取文字的高度
    float textHeight = textRect.height();
    //文字内容,文字绘制起点X,文字绘制起点Y,画笔
    canvas.drawText(text, 0, 0 + textHeight, paint);
  }

  @Override protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //按照指定坐标 绘制文本内容
    canvas.drawPosText("安卓", new float[]{ 90,90, 200,200 }, paint);
  }
drawText

特别要注意的是文字绘制的起点是从文字的左下角开始的,实际看见文字的Y坐标需要加上文字的自身高度

drawPosText

void drawPath(@NonNull Path path, @NonNull Paint paint)

  drawPath是绘制路径,关于Path可以参考Android开发之Path详解

  • canvas.save() 将已经绘制好的图像保存起来,让后续的操作就该图层上操作
  • canvas.restore()合并图层的操作,作用是将save之后绘制的图像和save之前的图像进行合并
  • canvas.translate()坐标系的平移与翻转,默认绘图坐标原点在屏幕左上角。调用canvas.translate(x,y)之后将从坐标原点(0,0)移动到了(x,y)。之后操作以(x,y)作为原点执行
  • canvas.rotate()将坐标系旋转了一个角度
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354

推荐阅读更多精彩内容