自定义View高级知识点(一)

来源:互联网 发布:微信领取淘宝优惠券 编辑:程序博客网 时间:2024/06/07 01:00

Canvas基本Api速查

操作类型 相关API 备注 绘制颜色 drawColor, drawRGB, drawARGB 使用单一颜色填充整个画布 绘制基本形状 drawPoint, drawPoints, drawLine, drawLines, drawRect, drawRoundRect, drawOval, drawCircle, drawArc 依次为 点、线、矩形、圆角矩形、椭圆、圆、圆弧 绘制图片 drawBitmap, drawPicture 绘制位图和图片 绘制文本 drawText, drawPosText, drawTextOnPath 依次为 绘制文字、绘制文字时指定每个文字位置、根据路径绘制文字 绘制路径 drawPath 绘制路径,绘制贝塞尔曲线时也需要用到该函数 顶点操作 drawVertices, drawBitmapMesh 通过对顶点操作可以使图像形变,drawVertices直接对画布作用、 drawBitmapMesh只对绘制的Bitmap作用 画布剪裁 clipPath, clipRect 设置画布的显示区域 画布快照 save, restore, saveLayerXxx, restoreToCount, getSaveCount 依次为 保存当前状态、 回滚到上一次保存的状态、 保存图层状态、 回滚到指定状态、 获取保存次数 画布变换 translate, scale, rotate, skew 依次为 位移、缩放、 旋转、错切 Matrix(矩阵) getMatrix, setMatrix, concat 实际画布的位移,缩放等操作的都是图像矩阵Matrix,只不过Matrix比较难以理解和使用,故封装了一些常用的方法。

颜色混合模式(Alpha通道相关)

通过前面介绍我们知道颜色一般都是四个通道(ARGB)的,其中(RGB)控制的是颜色,而A(Alpha)控制的是透明度。

因为我们的显示屏是没法透明的,因此最终显示在屏幕上的颜色里可以认为没有Alpha通道。Alpha通道主要在两个图像混合的时候生效。

默认情况下,当一个颜色绘制到Canvas上时的混合模式是这样计算的:

(RGB通道) 最终颜色 = 绘制的颜色 + (1 - 绘制颜色的透明度) × Canvas上的原有颜色。

注意:

1.这里我们一般把每个通道的取值从0(ox00)到255(0xff)映射到0到1的浮点数表示。

2.这里等式右边的“绘制的颜色”、“Canvas上的原有颜色” 都是经过预乘了自己的Alpha通道的值。如绘制颜色:0x88ffffff,那么参与运算时的每个颜色通道的值不是1.0,而是(1.0 * 0.5333 = 0.5333)。 (其中0.5333 = 0x88/0xff)

使用这种方式的混合,就会造成后绘制的内容以半透明的方式叠在上面的视觉效果。

Paint.setXfermode模式

下表是各个PorterDuff模式的混合计算公式:(D指原本在Canvas上的内容dst,S指绘制输入的内容src,a指alpha通道,c指RGB各个通道),各种模式如下图所示。

porterDuff的模式

onMeasure

真正进行测量的是 setMeasuredDimension
如果对View的宽高进行修改了,不要调用 super.onMeasure( widthMeasureSpec, heightMeasureSpec); 要调用 setMeasuredDimension( widthsize, heightsize); 这个函数。

paint绘制图形

一个要点:绘制的基本形状由Canvas确定,但绘制出来的颜色,具体效果则由Paint确定
STROKE //描边
FILL //填充
FILL_AND_STROKE //描边加填充

1.圆角矩形

利用圆角矩形也可以绘制出椭圆

RectF rectF = new RectF(100, 100, 300, 300);mPaint.setColor(Color.BLACK);canvas.drawRect(rectF, mPaint);mPaint.setColor(Color.RED);canvas.drawRoundRect(rectF, 100, 50, mPaint);

2.椭圆

椭圆绘制可以理解为做一个矩形的内切圆,如果这是一个正方形那么绘制出来的就是一个圆形

RectF rectF = new RectF(100, 100, 400, 300);mPaint.setColor(Color.BLACK);canvas.drawOval(rectF, mPaint);

3.扇形

drawArc(RectF oval, float startAngle(开始角度0-360), float sweepAngle(扫过角度0-360), boolean useCenter(是否连接圆心),Paint paint) 
    // 使用中心    RectF rectF = new RectF(100, 100, 200, 200);    mPaint.setStyle(Paint.Style.FILL_AND_STROKE);     mPaint.setColor(Color.GRAY);    canvas.drawRect(rectF, mPaint);    mPaint.setStyle(Paint.Style.STROKE);    mPaint.setColor(Color.BLACK);    canvas.drawArc(rectF, 0, 90, true, mPaint);    // 不使用中心    rectF = new RectF(100, 300, 200, 400);    mPaint.setStyle(Paint.Style.FILL_AND_STROKE); // 填充 并描边    mPaint.setColor(Color.GRAY);    canvas.drawRect(rectF, mPaint);    mPaint.setStyle(Paint.Style.STROKE); // 仅描边    mPaint.setColor(Color.RED);    canvas.drawArc(rectF, 0, -90, false, mPaint);

扇形

4.画一个圆饼图

public class PieChartView extends View {    private Paint mPaint;    private List<Percent> mPercentList;    public PieChartView(Context context) {        super(context);        init();    }    public PieChartView(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public PieChartView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    public void init() {        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);        mPaint.setStrokeCap(Paint.Cap.ROUND);        mPaint.setStrokeWidth(2);        mPercentList = new LinkedList<>();        mPercentList.add(new Percent("1", 0.4f, Color.WHITE));        mPercentList.add(new Percent("2", 0.4f, Color.BLUE));        mPercentList.add(new Percent("3", 0.2f, Color.RED));    }    @Override    protected void onDraw(Canvas canvas) {        RectF rectF = new RectF(100, 100, 400, 400);        canvas.drawOval(rectF, mPaint);        float surplus = 1.0f; // 剩余的范围        float startAngle = 0;        Percent percent;        for (int i = 0, size = mPercentList.size(); i < size; i++) {            percent = mPercentList.get(i);            if (percent.percent <= surplus) {                surplus -= percent.percent;                startAngle += percent.drawArc(mPaint, canvas, startAngle, rectF);            }        }    }    /**    * 采用封装的思想    */    public static class Percent {        String tag;        float percent;        int color;        public Percent(String tag, float percent, int color) {            this.tag = tag;            this.percent = percent;            this.color = color;        }        /**         * 绘制 这个百分比         *         * @param paint         * @param canvas         * @param startAngle         * @param rectF         * @return 返回绘制的角度大小         */        public float drawArc(Paint paint, Canvas canvas, float startAngle, RectF rectF) {            int oldColor = paint.getColor();            Paint.Style oldStyle = paint.getStyle();            paint.setColor(color);            paint.setStyle(Paint.Style.FILL);            canvas.drawArc(rectF, startAngle, 360 * percent, true, paint);            paint.setColor(oldColor);            paint.setStyle(oldStyle);            return 360 * percent;        }    }}

圆饼图

操作画布

操作画布,操作的都是坐标系,是对当前坐标系进行移动、旋转、缩放、错切等操作

操作画布只会影响接下来的绘画,而不会影响之前已经绘画完成的图像

1.移动画布

    // 移动画布    canvas.translate(getWidth() / 2, getHeight() / 2);    canvas.drawCircle(0, 0, 200, mPaint);    canvas.drawCircle(0, 0, 180, mPaint);    // 旋转画布 并画线    for (int i = 0; i < 36; i++) {        canvas.drawLine(0, 180, 0, 200, mPaint);        canvas.rotate(10);    }

通过旋转画布实现两圆之间的连线

2.旋转画布

    public void rotate(float degrees) {        native_rotate(mNativeCanvasWrapper, degrees);    }    /**     * 相对某点缩放     */    public final void rotate(float degrees, float px, float py) {        translate(px, py);        rotate(degrees);        translate(-px, -py);    }

看下图为相对某点进行缩放,图中的绘制顺序 1.红 2.黄 3.蓝 4.绿

相对某点缩放

        RectF rectF = new RectF(100, 100, 200, 200);        mPaint.setColor(Color.RED);        canvas.drawRect(rectF, mPaint);        canvas.translate(200,200); // 先将坐标系O移动到相对点P        mPaint.setColor(Color.YELLOW);        canvas.drawRect(rectF, mPaint);        canvas.rotate(90);// 在相对点P处将坐标系O进行旋转 O-->o P-->p        mPaint.setColor(Color.BLUE);        canvas.drawRect(rectF, mPaint);        canvas.translate(-200,-200);// 将旋转后的坐标系o中的p与P对齐,让以后的操作都仍然相对于P点        mPaint.setColor(Color.GREEN);        canvas.drawRect(rectF, mPaint);

3.缩放画布

    public void scale(float sx, float sy) {        native_scale(mNativeCanvasWrapper, sx, sy);    }    /**     * 相对于某点进行缩放,其相对原理与相对某点旋转相同     */    public final void scale(float sx, float sy, float px, float py) {        translate(px, py);        scale(sx, sy);        translate(-px, -py);    }

缩放比例(sx,sy)取值范围详解:

取值范围(n) 说明 [-∞, -1) 先根据缩放中心放大n倍,再根据中心轴进行翻转 -1 根据缩放中心轴进行翻转 (-1, 0) 先根据缩放中心缩小到n,再根据中心轴进行翻转 0 不会显示,若sx为0,则宽度为0,不会显示,sy同理 (0, 1) 根据缩放中心缩小到n 1 没有变化 (1, +∞) 根据缩放中心放大n倍

负值时,坐标系方向取反,scale(1,-1)这个操作将使得坐标系变为数学中常用坐标系

旋转,缩放,位移三者应用

    // 移动画布    canvas.translate(getWidth() / 2, getHeight() / 2);    mPaint.setColor(Color.BLUE);    for (int j = 0; j < 10; j++) {        canvas.drawCircle(0, 0, 200, mPaint);        canvas.drawCircle(0, 0, 180, mPaint);        canvas.rotate(j);        // 旋转画布 并画线![enter description here][6]        for (float i = j; i <= 360f; i += 10f) {            canvas.drawLine(0, 180, 0, 200, mPaint);            canvas.rotate(10);        }        canvas.scale(0.75f, 0.75f);    }

nothing

4.错切(skew)

错切是在某方向上,按照一定的比例对图形的每个点到某条平行于该方向的直线的有向距离做放缩得到的平面图形。(度娘)

    public void skew(float sx, float sy) {        native_skew(mNativeCanvasWrapper, sx, sy);    }

用法:

        canvas.translate(getWidth()/2,getHeight()/2);        RectF rectF = new RectF(0, 0, 100, 100);        mPaint.setColor(Color.RED);        canvas.drawRect(rectF, mPaint);        canvas.skew(-1,0); // 向左倾斜45度        canvas.drawRect(rectF, mPaint);

效果图:

错切

本文中很多内容学习自自定义View系列,非常感谢!同时还有一些自己发现的小tips。

0 0
原创粉丝点击