Android自定义View之画笔与画布(2)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_28963915/article/details/51943149

上片文章初步讲述了自定义View的一些方法 还有一些小东西梳理下,不知道怎么了 突然的感到头疼,,真是一个悲伤的故事。
先来看一下DrawText方法

/**baseLine和FontMetrics
 * 关于baseLine和FontMetrics
 * x,y并不是文字左上角的坐标点,它比较特殊,y所代表的是基线坐标y的坐标。基线
 * drawText(String text, float x, float y, Paint paint)
 * 除了基线,还有另外的四条线,它们分别是 top,ascent,descent和bottom,它们的含义分别为:
 *top:可绘制的最高高度所在线
 *bottom:可绘制的最低高度所在线
 *ascent :系统建议的,绘制单个字符时,字符应当的最高高度所在线
 *descent:系统建议的,绘制单个字符时,字符应当的最低高度所在线
 * Created by xuenan on 2016/7/18.
 */
public class MyView4 extends View{
    Paint mPaint;
    private Paint  paint;  //网格绘图
    public MyView4(Context context) {
        super(context);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.RED);
        //mPaint.setColor(Color.parseColor("#23AC3B"));
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setTextSize(120);

        paint = new Paint();
        paint.setColor(Color.parseColor("#A8A8A8"));
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setStrokeWidth(1);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.parseColor("#F5FFFA"));//画布的背景
        //网格线的绘制
        final int width = getWidth();
        final int height = getHeight();
        final int space = 100;   //长宽间隔
        int vertz = 0;
        int hortz = 0;
        for(int i=0;i<100;i++){
            canvas.drawLine(0,  vertz,  width, vertz, paint);
            canvas.drawLine(hortz, 0, hortz, height, paint);
            vertz+=space;
            hortz+=space;
        }
        //1.1 BaseLine基线
        //1、canvas.drawText()中参数y是基线y的坐标
        //2、x坐标、基线位置、文字大小确定,文字的位置就是确定的了。
        //定义在x坐标在所绘制矩形相对位置的函数是:setTextAlign(Paint.Align align)
        //Paint.Align是枚举类型,值分别为 : Paint.Align.LEFT,Paint.Align.CENTER和Paint.Align.RIGHT。
        //mPaint.setTextAlign(Paint.Align.CENTER);//主要是这里的取值不一样
        //canvas.drawText("abcdefghijk", 200, 200, mPaint);
        //canvas.drawLine(0, 200, getWidth(), 200, mPaint);
        //canvas.drawLine(200, 0, 200, getHeight(), mPaint);


        //1.2FontMetrics
        //还有另外的四条线,它们分别是 top,ascent,descent和bottom,它们的含义分别为:
        //top:可绘制的最高高度所在线
        //bottom:可绘制的最低高度所在线
        //ascent :系统建议的,绘制单个字符时,字符应当的最高高度所在线
        //descent:系统建议的,绘制单个字符时,字符应当的最低高度所在线
        //两个构造方法的区别是,得到对象的成员变量的值一个为float类型,一个为int类型。
        //Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
        //Paint.FontMetricsInt fm=  mPaint.getFontMetricsInt();
        //以float为例
        //Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
        //float ascent = fontMetrics.ascent;//当前绘制顶线
        //float descent = fontMetrics.descent;//当前绘制底线
        //float top = fontMetrics.top;//可绘制最顶线
        //float bottom = fontMetrics.bottom;//可绘制最底线
        //float leading = fontMetrics.leading;//top和ascent在y轴上的差值 为负数
        //计算方法 FontMetrics的这几个变量的值都是以baseLine为基准的
        //ascent = ascent线的y坐标 - baseline线的y坐标;//负数
        //descent = descent线的y坐标 - baseline线的y坐标;//正数
        //top = top线的y坐标 - baseline线的y坐标;//负数
        //bottom = bottom线的y坐标 - baseline线的y坐标;//正数
        //leading = top线的y坐标 - ascent线的y坐标;//负数
        //ascent线Y坐标 = baseline线的y坐标 + fontMetric.ascent;
        //descent线Y坐标 = baseline线的y坐标 + fontMetric.descent;
        //top线Y坐标 = baseline线的y坐标 + fontMetric.top;
        //bottom线Y坐标 = baseline线的y坐标 + fontMetric.bottom;


        //1.3、绘制ascent,descent,top,bottom线
        int baseLineY = 200;
        mPaint.setTextAlign(Paint.Align.LEFT);
        canvas.drawText("abcdefghijkl's", 200, baseLineY, mPaint);
        Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
        float top = fontMetrics.top + baseLineY;
        float ascent = fontMetrics.ascent + baseLineY;
        float descent = fontMetrics.descent + baseLineY;
        float bottom = fontMetrics.bottom + baseLineY;
        //绘制基线
        mPaint.setColor(Color.parseColor("#FF1493"));
        canvas.drawLine(0, baseLineY, getWidth(), baseLineY, mPaint);
        //绘制top直线
        mPaint.setColor(Color.parseColor("#FFB90F"));
        canvas.drawLine(0, top, getWidth(), top, mPaint);
        //绘制ascent直线
        mPaint.setColor(Color.parseColor("#b03060"));
        canvas.drawLine(0, ascent, getWidth(), ascent, mPaint);
        //绘制descent直线
        mPaint.setColor(Color.parseColor("#912cee"));
        canvas.drawLine(0, descent, getWidth(), descent, mPaint);
        //绘制bottom直线
        mPaint.setColor(Color.parseColor("#1E90FF"));
        canvas.drawLine(0, bottom, getWidth(), bottom, mPaint);
        //绘制最小矩形
        //String text="abcdefghijkl's";
        //canvas.drawRect(200,ascent,mPaint.measureText(text)+200,descent,mPaint);
        //文字高度
        //float text_height= bottom - top; //注意top为负数
        //文字中点y坐标
        //float text_center = (bottom - top) / 2;
        //float text_height=Math.abs(top-bottom);//同上
        //文字宽度
        //float text_width = mPaint.measureText(text);
    }
}

再看一下图片混合

/**mPaint.setXfermode()图像混合
 * Created by xuenan on 2016/7/18.
 */
public class MyView5 extends View{
    private Paint mPaint;
    private int width = 800;
    private int height = 800;
    private Bitmap dstBmp;
    private Bitmap srcBmp;
    public MyView5(Context context) {
        super(context);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.parseColor("#23AC3B"));
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setTextSize(120);
        //目标图
        dstBmp = makeBitmap(width, height, 0);
        //源图
        srcBmp = makeBitmap(width, height, 1);
    }
    public Bitmap makeBitmap(int w, int h, int style) {
        Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bm);
        Paint p = new Paint();
        p.setAntiAlias(true);
        switch (style) {
            case 0:
                p.setColor(Color.parseColor("#ff00ff"));
                c.drawOval(new RectF(0, 0, w, h), p);
                break;
            case 1:
                p.setColor(Color.parseColor("#00ffff"));
                c.drawRect(new RectF(0, 0, w, h), p);
                break;
            default:
        }
        return bm;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.parseColor("#F5FFFA"));//画布的背景
        //1、mPaint.setXfermode(Xfermode xfermode)
        //关于Xfermode这个类 翻译如下
        //Xfermode是对于被称为实现自定义的“转移模式”,在绘图管线对象的基类。
        //静态函数创建(模式)可以调用返回的任何作为模式枚举指定的预定义子类的一个实例。
        //当Xfermode被分配到一个画笔,然后对象与该画笔绘制应用了xfermode。
        //改类有三个子类AvoidXfermode PixelXorXfermode PorterDuffXfermode
        //前两个已经过时了  不再介绍 重点介绍第三个
        //只有一个参数PorterDuff.Mode是枚举类型,值有18个
//        Mode.CLEAR
//        Mode.SRC
//        Mode.DST
//        Mode.SRC_OVER
//        Mode.DST_OVER
//        Mode.SRC_IN
//        Mode.DST_IN
//        Mode.SRC_OUT
//        Mode.DST_OUT
//        Mode.SRC_ATOP
//        Mode.DST_ATOP
//        Mode.XOR
//        Mode.DARKEN
//        Mode.LIGHTEN
//        Mode.MULTIPLY
//        Mode.SCREEN
//        Mode.OVERLAY
//        Mode.ADD
//        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
        //xml文件中设置math_parent
        //新建图层
        int layerID = canvas.saveLayer(0,0,getWidth(),getHeight(),mPaint,Canvas.ALL_SAVE_FLAG);
        canvas.drawBitmap(dstBmp, 0, 0, mPaint);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(srcBmp, width/2, height/2, mPaint);
        mPaint.setXfermode(null);
        canvas.restoreToCount(layerID);
    }
    /**图像混合实现圆角图片
     * @param bitmap 原图
     * @param pixels 圆角大小
     * @return
     */
    public Bitmap getRoundCornerBitmap(Bitmap bitmap, float pixels) {
        //获取bitmap的宽高
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        Bitmap cornerBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Paint paint = new Paint();
        Canvas canvas = new Canvas(cornerBitmap);
        paint.setAntiAlias(true);
        canvas.drawRoundRect(new RectF(0, 0, width, height), pixels, pixels, paint);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(bitmap, null, new RectF(0, 0, width, height), paint);
        //绘制边框
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(6);
        paint.setColor(Color.GREEN);
        canvas.drawRoundRect(new RectF(0, 0, width, height), pixels, pixels, paint);
        return cornerBitmap;
    }
}

一个利用图片混合实现波浪纹的效果

/**
 * mPath.rQuadTo()贝塞尔曲线的绘制原理
 * 二阶贝赛尔
 * public void quadTo(float x1, float y1, float x2, float y2)
 * public void rQuadTo(float dx1, float dy1, float dx2, float dy2)
 * 三阶贝赛尔
 * public void cubicTo(float x1, float y1, float x2, float y2,float x3, float y3)
 * public void rCubicTo(float x1, float y1, float x2, float y2,float x3, float y3)
 * 一阶贝塞尔公式
 * B(t) = (1-t)*P0+t*P1; t=>[0,1]
 * 二阶贝塞尔公式
 * B(t) = (1-t)^2*P0+2*t*(1-t)*P1+t^2*P2;  t=>[0,1]
 * 三阶贝塞尔曲线
 * <p/>
 * Created by xuenan on 2016/7/18.
 */
public class MyView6 extends View {
    private Context context;
    private Bitmap mBitmap;
    private Canvas mCanvas;

    private Path mWavePath;
    private Paint mCirclePaint;
    private Paint mWavePaint;

    private int mCanvasSize;
    private int mCircleRadius;
    private int mCircleCenterX;
    private int mCircleCenterY;

    private int mWaveOriginX;
    private int mWaveOriginY;
    private int mWaveMoveX;
    private int mWaveAmplitude = DEFAULT_WAVE_AMPLITUDE;

    private int mWaveLength = DEFAULT_WAVE_LENGTH;

    private static final int DEFAULT_WAVE_AMPLITUDE = 100;
    private static final int DEFAULT_WAVE_LENGTH = 800;

    public MyView6(Context context) {
        super(context);
        this.context = context;
        init();
    }

    private void init() {
        setLayerType(LAYER_TYPE_SOFTWARE, null);//关闭硬件加速
        mCirclePaint = new Paint();
        mCirclePaint.setColor(Color.parseColor("#abc123"));
        mCirclePaint.setAntiAlias(true);
        mWavePaint = new Paint();
        mWavePaint.setStyle(Paint.Style.FILL);
        mWavePaint.setColor(Color.parseColor("#D22D2E"));
        mWavePaint.setAntiAlias(true);
        mWavePath = new Path();

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        mCircleCenterX = w / 2;
        mCircleCenterY = h / 2;
        mCircleRadius = (int) (Math.min(mCircleCenterX, mCircleCenterY) * 0.615f);

        mWaveOriginX = -mWaveLength;
        mWaveOriginY = mCircleCenterX;

        mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(mBitmap);

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = measureSize(widthMeasureSpec);
        int height = measureSize(heightMeasureSpec);
        int imageSize = (width < height) ? width : height;
        setMeasuredDimension(imageSize, imageSize);
    }

    private int measureSize(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else if (specMode == MeasureSpec.AT_MOST) {
            result = specSize;
        } else {
            result = mCanvasSize;
        }

        return result;
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mCanvasSize = canvas.getWidth();
        if (canvas.getHeight() < mCanvasSize) {
            mCanvasSize = canvas.getHeight();
        }

        int layerID = canvas.saveLayer(0, 0, getWidth(), getHeight(), mWavePaint, Canvas.ALL_SAVE_FLAG);
        mCanvas.drawCircle(mCircleCenterX, mCircleCenterY, mCircleRadius, mCirclePaint);
        mWavePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));


        mWavePath.reset();
        mWavePaint.setColor(Color.parseColor("#ff00ff"));
        mWavePath.moveTo(mWaveOriginX + mWaveMoveX, mWaveOriginY);
        for (int i = mWaveOriginX; i <= getWidth() + mWaveLength; i += mWaveLength) {
            mWavePath.rQuadTo(mWaveLength / 4f, -mWaveAmplitude, mWaveLength / 2f, 0);
            mWavePath.rQuadTo(mWaveLength / 4f, mWaveAmplitude, mWaveLength / 2f, 0);
        }
        mWavePath.lineTo(getWidth(), getHeight());
        mWavePath.lineTo(0, getHeight());
        mWavePath.close();

        mCanvas.drawPath(mWavePath, mWavePaint);

        canvas.drawBitmap(mBitmap, 0, 0, null);

        mWavePaint.setXfermode(null);
        canvas.restoreToCount(layerID);

    }

    public void startAnim() {
        ValueAnimator animator = ValueAnimator.ofInt(0, Math.abs(mWaveLength));
        animator.setDuration(1000);
        animator.setInterpolator(new LinearInterpolator());
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.setInterpolator(new LinearInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mWaveMoveX = (int) animation.getAnimatedValue();
                postInvalidate();
            }
        });
        animator.start();
    }
}

最后看一下Canvas画布的一些属性

/**Canvas 的变换与操作
 有时候我们还需要对 Canvas 做一些操作,比如旋转,裁剪,平移等等。
 canvas.translate 平移
 canvas.rotate 旋转
 canvas.scale 缩放
 canvas.skew 错切
 canvas.clipRect 裁剪
 canvas.save和canvas.restore 保存和恢复
 PorterDuffXfermode 图像混合 (paint相关方法)
 * Created by xuenan on 2016/7/18.
 */
public class MyView7 extends View{
    Paint mPaint;
    private Paint  paint;  //网格绘图
    public MyView7(Context context) {
        super(context);
        mPaint = new Paint();
        mPaint.setStrokeWidth(3);
        mPaint.setStyle(Paint.Style.STROKE);

        paint = new Paint();
        paint.setColor(Color.parseColor("#A8A8A8"));
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setStrokeWidth(1);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.parseColor("#F5FFFA"));//画布的背景
        //网格线的绘制
        final int width = getWidth();
        final int height = getHeight();
        final int space = 100;   //长宽间隔
        int vertz = 0;
        int hortz = 0;
        for(int i=0;i<100;i++){
            canvas.drawLine(0,  vertz,  width, vertz, paint);
            canvas.drawLine(hortz, 0, hortz, height, paint);
            vertz+=space;
            hortz+=space;
        }

        //1.1
        //mPaint.setColor(Color.parseColor("#008B00"));
        //canvas.drawCircle(200,200,200,mPaint);
        //translate平移 translate(float dx, float dy)
        //float dx:水平方向平移的距离,正数指向正方向(向右)平移的量,负数指向负方向(向左)平移的量
        //flaot dy:垂直方向平移的距离,正数指向正方向(向下)平移的量,负数指向负方向(向上)平移的量
        //mPaint.setColor(Color.parseColor("#68228B"));
        //canvas.translate(200,200);
        //canvas.drawCircle(200,200,200,mPaint);
        //屏幕显示与Canvas不是一个概念!Canvas是一个很虚幻的概念,相当于一个透明图层
        //(用过PS的同学应该都知道),每次Canvas画图时(即调用Draw系列函数),
        // 都会产生一个透明图层,然后在这个图层上画图,画完之后覆盖在屏幕上显示。
        //translate 函数其实实际相当于平移坐标系,即平移坐标系的原点的位置。


        //1.2Rotate
        //旋转画布,看起来和图片旋转效果有点类似。默认是围绕坐标系原点旋转,同理也可以设定中心点旋转。
        //rotate(float degrees)
        //rotate(float degrees, float px, float py)
        //第一个构造函数 degrees 参数表示旋转的度数。正数表示顺时针旋转,负数表示逆时针旋转,
        // 0 度表示水平X轴方向。默认以坐标原点作为中心点。
        //第二个构造函数 px , py 表示中心点坐标。
        //mPaint.setColor(Color.parseColor("#008B00"));
        //未旋转的直线
        //canvas.drawLine(200, 200, 600, 200,mPaint);
        //顺时针旋转30度
        //canvas.rotate(30,400,200);
        //mPaint.setColor(Color.parseColor("#68228B"));
        //canvas.drawLine(200, 200, 600, 200,mPaint);


        //1.3缩3、放(scale )有两个方法
        //scale(float sx, float sy)//缩放
        //scale(float sx, float sy, float px, float py)//先平移后缩放
        //canvas.save();
        //mPaint.setColor(Color.RED);
        //未缩放的圆形
        //canvas.drawCircle(200, 200, 100, mPaint);
        //画布缩放
        //canvas.scale(1.5f, 1.5f);
        //mPaint.setColor(Color.GREEN);
        //canvas.drawCircle(200, 200, 100, mPaint);
        //canvas.restore();
        //先将画布平移px,py,然后scale,scale结束之后再将画布平移回原基准点。
        //canvas.scale(1.5f, 1.5f, 200, 200);
        //mPaint.setColor(Color.YELLOW);
        //canvas.drawCircle(200, 200, 100, mPaint);

        //1.4、错切(skew)skew(float sx, float sy)
        //float sx:将画布在x方向上倾斜相应的角度,sx倾斜角度的tan值
        //float sy:将画布在y轴方向上倾斜相应的角度,sy为倾斜角度的tan值
        //mPaint.setColor(Color.RED);
        //未旋转的圆形
        //canvas.drawRect(100, 100, 500, 400, mPaint);
        //canvas.skew(0.56f, 0f);
        //mPaint.setColor(Color.GREEN);
        //canvas.drawRect(100, 100, 500, 400, mPaint);

        //1.5、保存和恢复save()、restore()
        //对画布的平移,旋转,缩放,错切,裁剪都是不可逆的操作,如果我们还需要返回到原始状态
        //对画布进行操作怎么办呢?细心的同学肯定知道 save(),restore() 方法在上文已经出现过了,
        //对的,它就是用来对画布状态的保存与恢复,这样我们就可以愉快的进行可逆操作了。方法预览:
        //save():每次调用 save() 函数,都会把当前的画布的状态进行保存,然后放入特定的栈中;
        //restore():每当调用 restore() 函数,就会把栈中最顶层的画布状态取出来,并按照这个状态
        //恢复当前的画布,并在这个画布上做画。
        canvas.drawColor(Color.GREEN);
        //保存当前画布
        canvas.save();
        canvas.clipRect(new Rect(200, 200, 500, 400));
        canvas.drawColor(Color.YELLOW);
        //恢复画布
        canvas.restore();
        canvas.drawColor(Color.RED);
        //在使用canvas.save和canvas.restore时最好配对使用,
        //若restore( )的调用次数比save( )多可能会造成异常。
    }

好困啊我 就写到这

猜你喜欢

转载自blog.csdn.net/qq_28963915/article/details/51943149