Android绘图

本文详细介绍了 Android 中 Canvas 类的基本使用方法及如何利用 Xfermode 实现复杂的图像混合效果。涵盖 Canvas 的核心组件介绍、绘制图形示例、Xfermode 的 16 种模式解释与应用实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Canvas类Android.graphics.Canvas
先来看官网的一句话吧 The Canvas class holds the “draw” calls. To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles for the drawing).
Canvas类就像一块画布,可以在上面画画,如果我们想画的话,需要四个基本组件
1、Bitmap ,位图 :包含像素
2、canvas ,画板
3、a drawing primitive ,原始图(e.g. Rect, Path, text, Bitmap)
4、Paint: 画笔 :提供了颜色和样式
这里写图片描述

public class DrawView extends View {
    public DrawView(Context context) {
        super(context);
    }

    public DrawView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //创建画笔
        Paint p = new Paint();
        p.setColor(Color.RED);
        p.setTextSize(50); //设置字体大小
        /**
         * 画圆
         */
        canvas.drawText("画圆", 10, 100, p);
        canvas.drawCircle(200, 90, 15, p);
        /**
         * 画线
         */
        canvas.drawText("画线", 10, 200, p);
        p.setColor(Color.BLUE);
        canvas.drawLine(300, 200, 600, 200, p);  //只需要两个点的坐标
        /**
         * 画矩形
         */
        canvas.drawText("画矩形", 10, 400, p);
//        p.setStyle(Paint.Style.FILL);
        canvas.drawRect(200, 300, 500, 400, p);  //这个矩形是按边来画的   left:左边到X轴的距离(x坐标),top:上边到Y轴的距离(y的纵"zong"坐标),right:(右边到X的距离(X坐标),bottom:底边到Y轴的距离(Y坐标)
        canvas.drawRect(600, 300, 1000, 500, p);

        /**
         * 画弧形
         */
        canvas.drawText("画弧形", 10, 700, p);
        canvas.drawRect(200, 600, 600, 800, p);
        p.setColor(Color.YELLOW);
        RectF rectF = new RectF(200, 600, 600, 800);  //这个弧形就是以矩形的重心为原点,对角线的的一半为半径,以顺时针画
        canvas.drawArc(rectF, 0, 160, true, p);   //useCenter 为true就覆盖所有画的区域

        /**
         * 三角形
         */
        p.setColor(Color.RED);
        canvas.drawText("画三角形", 10, 900, p);
        p.setStyle(Paint.Style.STROKE);//设置空心
        //绘制三角形,也可以绘制任意多边形
        Path path = new Path();
        path.moveTo(300, 900); //第一个点为起点
        path.lineTo(800, 100); //注意这里是lineto
        path.lineTo(300, 970);
        path.close();   //封闭
        canvas.drawPath(path, p);

        //画圆角矩形
        p.setStyle(Paint.Style.FILL);
        p.setColor(Color.LTGRAY);
        p.setAntiAlias(true); //设置画笔的锯齿效果
        canvas.drawText("画圆角矩形",10,1000,p);
        RectF rectF1= new RectF(200,1000,500,1100);
        canvas.drawRoundRect(rectF1,50,200,p);  //第二个参数和第三个x\y方向上的半径,不是很理解
    }
}

这里写的也挺详细的 http://blog.youkuaiyun.com/rhljiayou/article/details/7212620
二 接着看下经典之图 (很多博客第一个和我的不同)
这里写图片描述
ApiDemos导入各种报错,浪费了不少时间,干脆把代码拎出来,“点我进图”可以看到

1、这么多图是啥意思呢? 其实就花了两个图,
先画个圆(黄色)
再画个矩形(蓝色)
两个叠加通过属性判断要显示哪部分,最后就可以构成我们需要的各种图 形了,感觉和红绿蓝三原色似的。

2、这里面不得不提到 PorterDuff.Mode16种模式
从上面我们可以看到PorterDuff.Mode为枚举类,一共有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 两图层全部区域,交集部分变为透明色
3、ApiDemos 示例

public class Xfermodes extends Activity {
    public static final Xfermode[] sModes = {
            new PorterDuffXfermode(PorterDuff.Mode.CLEAR),
            new PorterDuffXfermode(PorterDuff.Mode.SRC),
            new PorterDuffXfermode(PorterDuff.Mode.DST),
            new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),
            new PorterDuffXfermode(PorterDuff.Mode.DST_OVER),
            new PorterDuffXfermode(PorterDuff.Mode.SRC_IN),
            new PorterDuffXfermode(PorterDuff.Mode.DST_IN),
            new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),
            new PorterDuffXfermode(PorterDuff.Mode.DST_OUT),
            new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),
            new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),
            new PorterDuffXfermode(PorterDuff.Mode.XOR),
            new PorterDuffXfermode(PorterDuff.Mode.DARKEN),
            new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),
            new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),
            new PorterDuffXfermode(PorterDuff.Mode.SCREEN)
    };
    // create a bitmap with a circle, used for the "dst" image  画一个圆
    static Bitmap makeDst(int w, int h) {
        Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bm);
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);

        p.setColor(0xFFFFCC44);
        c.drawOval(new RectF(0, 0, w*3/4, h*3/4), p);
        return bm;
    }

    // create a bitmap with a rect, used for the "src" image     画一个矩形
    static Bitmap makeSrc(int w, int h) {
        Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bm);
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);

        p.setColor(0xFF66AAFF);
        c.drawRect(w/3, h/3, w*19/20, h*19/20, p);
        return bm;
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new SampleView(this));
    }
    private static class SampleView extends View {
        private static final int W = 200;
        private static final int H = 200;
        private static final int ROW_MAX = 4;   // number of samples per row

        private Bitmap mSrcB,mOvalB;
        private Shader mBG;     // background checker-board pattern
        private static final String[] sLabels = {
                "Clear", "Src", "Dst", "SrcOver",
                "DstOver", "SrcIn", "DstIn", "SrcOut",
                "DstOut", "SrcATop", "DstATop", "Xor",
                "Darken", "Lighten", "Multiply", "Screen"
        };

        public SampleView(Context context) {
            super(context);

            mSrcB = makeSrc(W, H);
            mOvalB = makeDst(W, H);

            // make a ckeckerboard pattern
            Bitmap bm = Bitmap.createBitmap(new int[] { 0xFFFFFFFF, 0xFFCCCCCC,
                            0xFFCCCCCC, 0xFFFFFFFF }, 2, 2,
                    Bitmap.Config.RGB_565);
            mBG = new BitmapShader(bm,
                    Shader.TileMode.REPEAT,
                    Shader.TileMode.REPEAT);
            Matrix m = new Matrix(); //矩阵
            m.setScale(6, 6);
            mBG.setLocalMatrix(m);
        }

        @Override
        protected void onDraw(Canvas canvas) {
            canvas.drawColor(Color.WHITE);

            Paint labelP = new Paint(Paint.ANTI_ALIAS_FLAG);
            labelP.setTextSize(30);
            labelP.setTextAlign(Paint.Align.CENTER);

            Paint paint = new Paint();
            paint.setFilterBitmap(false);

            canvas.translate(15, 35);

            int x = 0;
            int y = 0;
            for (int i = 0; i < sModes.length; i++) {
                // draw the border
                paint.setStyle(Paint.Style.STROKE);
                paint.setShader(null);
                canvas.drawRect(x - 0.5f, y - 0.5f,
                        x + W + 0.5f, y + H + 0.5f, paint);  //绘制正方形的外边框,长宽就是W 、 H

                // draw the checker-board pattern
                paint.setStyle(Paint.Style.FILL);
                paint.setShader(mBG);
                canvas.drawRect(x, y, x + W, y + H, paint);  //内边框

                // draw the src/dst example into our offscreen bitmap
                int sc = canvas.saveLayer(x, y, x + W, y + H, null,
                        Canvas.MATRIX_SAVE_FLAG |
                                Canvas.CLIP_SAVE_FLAG |
                                Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
                                Canvas.FULL_COLOR_LAYER_SAVE_FLAG |
                                Canvas.CLIP_TO_LAYER_SAVE_FLAG);
                canvas.translate(x, y);     //设置矩阵的偏移量
                canvas.drawBitmap(mOvalB, 0, 0, paint);     //先绘制圆
                paint.setXfermode(sModes[i]);
                canvas.drawBitmap(mSrcB, 0, 0, paint);  //再绘制矩形
                paint.setXfermode(null);
                canvas.restoreToCount(sc);

                // draw the label
                canvas.drawText(sLabels[i],
                        x + W/2, y - labelP.getTextSize()/2, labelP);   //写字

                x += W + 10;

                // wrap around when we've drawn enough for one row
                if ((i % ROW_MAX) == ROW_MAX - 1) {
                    x = 0;
                    y += H + 30;
                }
            }
        }
    }
}

4、单个进行测试

   /**
 * Created by Administrator on 2016/8/28.
 * 对每一个属性单个进行测试,  !!!!!有一个问题   onDraw() 偶尔不执行 !!!!!
 */
public class XfermodeView extends View {
    //源图和目标图宽高
    int width = 200, height = 200;
    Bitmap rectBitmap, cirBitmap;
    int screenW, screenH;

    public XfermodeView(Context context) {
       // super(context);
        //init(context);
        //把上面的改成
        this(context,null);  //发现改成这样每次都会走了
    }


    public XfermodeView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
        setWillNotDraw(false);

    }

    private void init(Context context) {
        WindowManager windowManager = (WindowManager) context.getApplicationContext()
                .getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics metrics = new DisplayMetrics();
        windowManager.getDefaultDisplay().getMetrics(metrics);
        screenW = metrics.widthPixels;
        screenH = metrics.heightPixels;
    }


    // create a bitmap with a circle, used for the "dst" image  画一个圆
    static Bitmap makeDst(int w, int h) {
        Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bm);
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
        p.setColor(0xFFFFCC44);
        c.drawOval(new RectF(0, 0, w * 3 / 4, h * 3 / 4), p);
        return bm;
    }

    // create a bitmap with a rect, used for the "src" image     画一个矩形
    static Bitmap makeSrc(int w, int h) {
        Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bm);
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);

        p.setColor(0xFF66AAFF);
        c.drawRect(w / 3, h / 3, w * 19 / 20, h * 19 / 20, p);
        return bm;
    }

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


        //创建原图和目标图
        rectBitmap = makeSrc(width, height);
        cirBitmap = makeDst(width, height);


        Paint paint = new Paint();
        paint.setFilterBitmap(false);
        paint.setStyle(Paint.Style.FILL);
        //绘制“src”蓝色矩形原图
        canvas.drawBitmap(rectBitmap, (screenW / 8 - width / 4), (screenH / 12 - height / 4), paint);
        //绘制“dst”黄色圆形原图
        canvas.drawBitmap(cirBitmap, screenW / 2, screenH / 12, paint);

//创建一个图层,在图层上演示图形混合后的效果
        int sc = canvas.saveLayer(0, 0, screenW, screenH, null, Canvas.MATRIX_SAVE_FLAG |
                Canvas.CLIP_SAVE_FLAG |
                Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
                Canvas.FULL_COLOR_LAYER_SAVE_FLAG |
                Canvas.CLIP_TO_LAYER_SAVE_FLAG);
//        先绘制圆形
        canvas.drawBitmap(cirBitmap, screenW / 4, screenH / 3, paint);
        //设置Paint的Xfermode
        paint.setXfermode(Xfermodes.sModes[4]);  //这里修改显示模式

        //绘制矩形
        canvas.drawBitmap(rectBitmap, screenW / 4, screenH / 3, paint);
        paint.setXfermode(null);
//         还原画布
        canvas.restoreToCount(sc);
    }
}

Reference :http://blog.youkuaiyun.com/allen315410/article/details/45077165

三、接下来跟着大神来画图了
1、先绘bitmap,然后绘制形状,显示底部

      /**
 * Created by Administrator on 2016/8/29.
 */
public class RoundImageViewByXfermode extends ImageView {
    private int mBorderRadius; //圆角的大小
    private Paint mPaint;
    /**
     * 图片的类型,圆形or圆角
     */
    private int type;
    public static final int TYPE_CIRCLE = 0;
    public static final int TYPE_ROUND = 1;

    private Xfermode mXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);

    private WeakReference<Bitmap> mWeakBitmap;
    private Bitmap mMaskBitmap;

    public RoundImageViewByXfermode(Context context) {
        this(context, null);

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
    }

    public RoundImageViewByXfermode(Context context, AttributeSet attrs) {
        super(context, attrs);

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RoundImageViewByXfermode);
        //角度默认10dp
        mBorderRadius = a.getDimensionPixelSize(R.styleable.RoundImageViewByXfermode_borderRadius, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics()));
        type = a.getInt(R.styleable.RoundImageViewByXfermode_type, TYPE_CIRCLE); //默认圆角
        a.recycle();
    }


    @Override
    public void invalidate() {
        mMaskBitmap = null;
        if (mMaskBitmap != null) {
            mMaskBitmap.recycle();
            mMaskBitmap = null;
        }
        super.invalidate();
    }


    @Override
    protected void onDraw(Canvas canvas) {
//        super.onDraw(canvas);  // 这个要注释否则就会没效果
        //从缓存中取出bitmap
        Bitmap bitmap = mWeakBitmap == null ? null : mWeakBitmap.get();

        if (null == bitmap || bitmap.isRecycled()) {
            //拿到Drawable
            Drawable drawable = getDrawable();
            //获取Drawable的宽和高
            int dWidth = drawable.getIntrinsicWidth();
            int dHeight = drawable.getIntrinsicHeight();

            if (drawable != null) {
                //创建Bitmap
                bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);

                float scale = 1.0f;
                //创建画布
                Canvas drawCanvas = new Canvas(bitmap);
                //按照bitmap的宽高,以及view的宽高,计算缩放比例;因为设置的src宽高比例可能和imageview的宽高比例不同,这里我们不希望图片失真;
                if (type == TYPE_ROUND) {
                    // 如果图片的宽或者高与view的宽高不匹配,计算出需要缩放的比例;缩放后的图片的宽高,一定要大于我们view的宽高;所以我们这里取大值;
                    scale = Math.max(getWidth() * 1.0f / dWidth, getHeight() * 1.0f / dHeight);
                }

                //根据缩放比例,设置bounds,相当于缩放图片
                drawable.setBounds(0, 0, (int) (scale * dWidth), (int) (scale * dHeight));

                drawable.draw(drawCanvas);
                if (mMaskBitmap == null || mMaskBitmap.isRecycled())
                    mMaskBitmap = getBitmap();

                //Draw Bitmap
                mPaint.reset();
                mPaint.setFilterBitmap(false);


                mPaint.setXfermode(mXfermode);  //  取两层绘制交集。显示下层。
                //绘制圆角矩形
                drawCanvas.drawBitmap(mMaskBitmap, 0, 0, mPaint);
                mPaint.setXfermode(null);
                //将准备好的bitmap绘制出来
                canvas.drawBitmap(bitmap, 0, 0, null);
                mWeakBitmap = new WeakReference<Bitmap>(bitmap);  //bitmap缓存起来,避免每次调用onDraw,分配内存
            }

        }
        //如果bitmap还存在,则直接绘制即可
        if (bitmap != null) {
            mPaint.setXfermode(null);
            canvas.drawBitmap(bitmap, 0.0f, 0.0f, mPaint);
            return;
        }
    }

    public Bitmap getBitmap() {
        Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(),
                Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.BLACK);

        if (type == TYPE_ROUND) {
            canvas.drawRoundRect(new RectF(0, 0, getWidth(), getHeight()),
                    mBorderRadius, mBorderRadius, paint);
        }

        return bitmap;
    }
}

运行的时候编译器碰到一个很坑的问题
android studio Error:java.lang.OutOfMemoryError: GC overhead limit exceeded
解决方法是
这里写图片描述

REFERENCE :http://blog.youkuaiyun.com/lmj623565791/article/details/42094215

[代码]

二、BitmapShader 这个也能画出圆角矩形的效果

  Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.china, null);
        Bitmap target = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig());
        RectF rect = new RectF(0, 0, target.getWidth(), target.getHeight());
        BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        Paint paint = new Paint();
        paint.setShader(shader);

        //绘制一个圆角矩形
        Canvas canvas = new Canvas(target);
        canvas.drawRoundRect(rect, 40, 40, paint);
        imageView1.setImageBitmap(target);

参考:
http://gavinliu.cn/2016/04/12/Android-%E5%AE%9E%E7%8E%B0%E5%9B%BE%E7%89%87%E5%9C%86%E8%A7%92%E6%98%BE%E7%A4%BA%E7%9A%84%E5%87%A0%E7%A7%8D%E6%96%B9%E5%BC%8F/
http://blog.youkuaiyun.com/allen315410/article/details/45077165

http://blog.youkuaiyun.com/guolin_blog/article/details/16330267

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值