Android Paint,Path,Canvas

发布时间 2023-07-11 11:17:05作者: petercao

1. Paint

Paint(画笔),保存了绘制几何图形文本位图样式颜色信息

关键词:coloralphastrokesolid线条圆角效果拐角风格xfermode渲染器TileMode

1. 线性渲染
2. 环形渲染
3. 扫描渲染
4. 位图渲染
5. 组合渲染


2. 图层混合模式

1. 离屏绘制
2. 刮刮卡效果实现


3. 滤镜

1. 颜色矩阵(胶片效果等)
2. 饱和度
3. 亮度值


2. Canvas

Canvas(画布),通过画笔绘制几何图形文本路径位图

1. drawLine,drawBitmap,drawPath,drawText (绘制几何图形,文本,位图等)
2. scale(缩放),rotate,translate(平移) (位置形状变换)
3. clipPath
4. Matrix
5. Canvas调用了translate,scale等变换后,后续的操作都是基于变换后的Canvas,都会受到影响;所有我们采用canvas.save(),restore等方法来保存画布各个阶段状态,避免混乱
6. 离子爆炸效果


3. PathPathMeasure

Path(路径),可用于绘制直线,曲线构成的几何路径,还可根据路径绘制文字;常用API:移动,连线,闭合,添加图形等

一阶贝塞尔曲线(法):数据点和控制点rLineToRect类QQ消息数拉拽效果

 

public class PkLeftView extends View {

    private static final String TAG = "GradientLayout";
    private int mW, mH; // 控件,宽高
    Path stampPath = new Path();
    public int startX1, startY1;
    public int startX2, startY2;
    public int startX3, startY3;
    public int startX4, startY4;
    public RectF arcRect;
    private Shader mShader;
    private Bitmap mBitmap;

    private Paint mPaint;
    private Paint mPaintLine;

    private int lineH = PixelDpUtils.dp2px(1); // 画线宽度1/2

    public int xDown = 40; // 折角宽度 X

    public PkLeftView(Context context) {
        this(context, null);
    }

    public PkLeftView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public PkLeftView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mPaintLine = new Paint(); //初始化
        mPaintLine.setColor(Color.BLACK);// 设置颜色
        mPaintLine.setStyle(Paint.Style.STROKE); //描边效果
        mPaintLine.setAntiAlias(true); // 抗锯齿
        mPaintLine.setStyle(Paint.Style.STROKE); //描边效果
        mPaintLine.setStrokeWidth(lineH * 2);//描边宽度

        mPaint = new Paint(); //初始化
        mPaint.setAntiAlias(true); // 抗锯齿
        mPaint.setStyle(Paint.Style.FILL); //描边效果
        mPaint.setStrokeWidth(lineH * 2);//描边宽度
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mW = getMeasuredWidth();
        mH = getMeasuredHeight();
        Log.i(TAG, "mW: " + mW);
        Log.i(TAG, "mW: " + mH);
        xDown = mW / 10;
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        Log.i(TAG, "left: " + left);
        Log.i(TAG, "top: " + top);
        Log.i(TAG, "right: " + right);
        Log.i(TAG, "bottom: " + bottom);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        mShader = new LinearGradient(0, 0, 500, 500, new int[]{Color.RED, Color.BLUE, Color.GREEN}, new float[]{0.f, 0.7f, 1}, Shader.TileMode.REPEAT);
        mPaint.setShader(mShader);

        // 1. 首先移动到起始点
        startX1 = mH / 2;
        startY1 = lineH;
        stampPath.moveTo(startX1, startY1);

        // 2. 画水平线
        startX2 = mW - lineH;
        startY2 = lineH;

        stampPath.lineTo(startX2, startY2);

        // 3. 画倒角
        startX3 = startX2 - xDown;
        startY3 = mH - lineH;
        stampPath.lineTo(startX3, startY3);

        // 4. 画下边
        startX4 = mH / 2;
        startY4 = startY3;
        stampPath.lineTo(startX4, startY4);

//        // 5. 画圆弧
        if (arcRect == null) {
            arcRect = new RectF(lineH, lineH, mH - lineH, mH - lineH);
        }
        stampPath.addArc(arcRect, 90, 180);

        // 6. 将Path利用Paint绘制出来
        canvas.drawPath(stampPath, mPaint);

        canvas.save();

        // 7.
        canvas.drawPath(stampPath, mPaintLine);

    }

}
public class GradientLayout extends View {

    private Paint mPaint;
    private Shader mShader;
    private Bitmap mBitmap;

    public GradientLayout(Context context) {
        this(context, null);
    }

    public GradientLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public GradientLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {

        mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.beauty);

        mPaint = new Paint(); //初始化
//        mPaint.setColor(Color.RED);// 设置颜色
//        mPaint.setARGB(255, 255, 255, 0); // 设置 Paint对象颜色,范围为0~255
//        mPaint.setAlpha(200); // 设置alpha不透明度,范围为0~255
        mPaint.setAntiAlias(true); // 抗锯齿
        mPaint.setStyle(Paint.Style.FILL); //描边效果
//        mPaint.setStrokeWidth(4);//描边宽度
//        mPaint.setStrokeCap(Paint.Cap.ROUND); //圆角效果
//        mPaint.setStrokeJoin(Paint.Join.MITER);//拐角风格
//        mPaint.setShader(new SweepGradient(200, 200, Color.BLUE, Color.RED)); //设置环形渲染器
//        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DARKEN)); //设置图层混合模式
//        mPaint.setColorFilter(new LightingColorFilter(0x00ffff, 0x000000)); //设置颜色过滤器
//        mPaint.setFilterBitmap(true); //设置双线性过滤
//        mPaint.setMaskFilter(new BlurMaskFilter(10, BlurMaskFilter.Blur.NORMAL));//设置画笔遮罩滤镜 ,传入度数和样式
//        mPaint.setTextScaleX(2);// 设置文本缩放倍数
//        mPaint.setTextSize(38);// 设置字体大小
//        mPaint.setTextAlign(Paint.Align.LEFT);//对其方式
//        mPaint.setUnderlineText(true);// 设置下划线
//
//        String str = "Android高级工程师";
//        Rect rect = new Rect();
//        mPaint.getTextBounds(str, 0, str.length(), rect); //测量文本大小,将文本大小信息存放在rect中
//        mPaint.measureText(str); //获取文本的宽
//        mPaint.getFontMetrics(); //获取字体度量对象

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
       /**
        * 1.线性渲染,LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[], @Nullable float positions[], @NonNull TileMode tile)
        * (x0,y0):渐变起始点坐标
        * (x1,y1):渐变结束点坐标
        * color0:渐变开始点颜色,16进制的颜色表示,必须要带有透明度
        * color1:渐变结束颜色
        * colors:渐变数组
        * positions:位置数组,position的取值范围[0,1],作用是指定某个位置的颜色值,如果传null,渐变就线性变化。
        * tile:用于指定控件区域大于指定的渐变区域时,空白区域的颜色填充方法
        */
//        mShader = new LinearGradient(0, 0, 500, 500, new int[]{Color.RED, Color.BLUE, Color.GREEN}, new float[]{0.f,0.7f,1}, Shader.TileMode.REPEAT);
//        mPaint.setShader(mShader);
//        canvas.drawCircle(250, 250, 250, mPaint);
//        canvas.drawRect(0,0,1000,1000, mPaint);

       /**
        * 环形渲染,RadialGradient(float centerX, float centerY, float radius, @ColorInt int colors[], @Nullable float stops[], TileMode tileMode)
        * centerX ,centerY:shader的中心坐标,开始渐变的坐标
        * radius:渐变的半径
        * centerColor,edgeColor:中心点渐变颜色,边界的渐变颜色
        * colors:渐变颜色数组
        * stoops:渐变位置数组,类似扫描渐变的positions数组,取值[0,1],中心点为0,半径到达位置为1.0f
        * tileMode:shader未覆盖以外的填充模式。
        */
//        mShader = new RadialGradient(250, 250, 250, new int[]{Color.GREEN, Color.YELLOW, Color.RED}, null, Shader.TileMode.CLAMP);
//        mPaint.setShader(mShader);
//        canvas.drawCircle(250, 250, 250, mPaint);

        /**
        * 扫描渲染,SweepGradient(float cx, float cy, @ColorInt int color0,int color1)
        * cx,cy 渐变中心坐标
        * color0,color1:渐变开始结束颜色
        * colors,positions:类似LinearGradient,用于多颜色渐变,positions为null时,根据颜色线性渐变
        */
//        mShader = new SweepGradient(250, 250, Color.RED, Color.GREEN);
//        mPaint.setShader(mShader);
//        canvas.drawCircle(250, 250, 250, mPaint);

        /**
        * 位图渲染,BitmapShader(@NonNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY)
        * Bitmap:构造shader使用的bitmap
        * tileX:X轴方向的TileMode
        * tileY:Y轴方向的TileMode
              REPEAT, 绘制区域超过渲染区域的部分,重复排版
              CLAMP, 绘制区域超过渲染区域的部分,会以最后一个像素拉伸排版
              MIRROR, 绘制区域超过渲染区域的部分,镜像翻转排版
        */
//        mShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT, Shader.TileMode.MIRROR);
//        mPaint.setShader(mShader);
//        canvas.drawRect(0,0,500, 500, mPaint);
//        canvas.drawCircle(250, 250, 250, mPaint);

        /**
         * 组合渲染,
         * ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB, Xfermode mode)
         * ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB, PorterDuff.Mode mode)
         * shaderA,shaderB:要混合的两种shader
         * Xfermode mode: 组合两种shader颜色的模式
         * PorterDuff.Mode mode: 组合两种shader颜色的模式
         */
        BitmapShader bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
        LinearGradient linearGradient = new LinearGradient(0, 0, 1000, 1600, new int[]{Color.RED, Color.GREEN, Color.BLUE}, null, Shader.TileMode.CLAMP);
        mShader = new ComposeShader(bitmapShader, linearGradient, PorterDuff.Mode.MULTIPLY);
        mPaint.setShader(mShader);
        canvas.drawCircle(250, 250, 250, mPaint);
    }
}
1. 划线两端圆角效果
mPaint.setStrokeCap(Paint.Cap.ROUND); // 圆角效果

 

refs:
https://blog.csdn.net/zmm911zmm/article/details/89784920
https://www.codercto.com/a/43619.html
https://juejin.cn/post/6844903487570968584 | HenCoder Android 开发进阶: 自定义 View 1-2 Paint 详解 - 掘金
https://juejin.cn/post/6844903488460177416 | HenCoder Android 开发进阶:自定义 View 1-3 文字的绘制 - 掘金
https://juejin.cn/post/6844903489789755406 | HenCoder Android 开发进阶:自定义 View 1-4 Canvas 对绘制的辅助 - 掘金
https://juejin.cn/post/6844903491031269383 | HenCoder Android 自定义 View 1-5: 绘制顺序 - 掘金
https://juejin.cn/post/6844903494256689165 | HenCoder Android 自定义 View 1-6: 属性动画(上手篇) - 掘金
https://juejin.cn/post/6844903494940360711 | 【HenCoder Android 开发进阶】自定义 View 1-7:属性动画(进阶篇) - 掘金
https://juejin.cn/post/6844903496064434183 | # HenCoder Android 自定义 View 1-8 硬件加速 - 掘金
https://rengwuxian.com/ui-2-1/ | HenCoder Android 自定义 View 2-1 布局基础
https://rengwuxian.com/ui-2-2/ | HenCoder Android 自定义 View 2-2 全新定义 View 的尺寸
https://rengwuxian.com/ui-2-3/ | HenCoder Android 自定义 View 2-3 定制 Layout 的内部布局
https://rengwuxian.com/ui-3-1/ | HenCoder 自定义 View 3-1 触摸反馈,以及 HenCoder Plus