背景

我的博客:http://zhangsunyucong.top

马上就到2018年过年了,然后我又刚好有兴致,就来玩玩Android中的简单几何图形的绘制和使用Path类来绘制路径。

Path和Canvas

在Android中,和我们平时画图一样是有画笔和画布的,Path是画笔,Canvas是画布。与画的样式属性有关,如大小或者颜色等,是由Path来完成的;与画的形状,即画什么东西是由Canva完成的。关于这两个类的各个属性和方法的具体使用,可以浏览爱哥的博客几篇文章。在这里,只是用它们简单的几个函数画一些简单的图形,最后还会给出一个综合一点的demo,主要是为了加强认识绘制时坐标关系。

先贴上我的代码: 布局文件:

<?xml version="1.0" encoding="utf-8"?>"http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:id="@+id/root_draw_view"    android:gravity="center_horizontal"    android:layout_width="match_parent"    android:layout_height="match_parent">    "match_parent"        android:layout_height="wrap_content"        android:gravity="center"        android:text="有点意思"/>    "300dp"        android:layout_height="300dp" />复制代码

MyDrawView.java

public class MyDrawView extends View {    private Paint mPointPaint;    private float[] mFPts;    private RectF mRectF;    private RectF mRectOvalF;    private RectF mRightBottomRectF;    private Path mPath;    private Path mPath1;    public MyDrawView(Context context) {        super(context);        init();    }    public MyDrawView(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);        init();    }    public MyDrawView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    private int mPointStrokeWidth;    private void init() {        mPointStrokeWidth = 20;        mPointPaint = new Paint();        mPointPaint.setColor(Color.RED);        mPointPaint.setStrokeWidth(mPointStrokeWidth);        mPointPaint.setStyle(Paint.Style.FILL);        mPath = new Path();        mPath1 = new Path();    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        //super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int widthMode = MeasureSpec.getMode(widthMeasureSpec);        int widthSize = MeasureSpec.getSize(widthMeasureSpec);        int heightMode = MeasureSpec.getMode(heightMeasureSpec);        int heightSize = MeasureSpec.getSize(heightMeasureSpec);        int widthResult = 100;        int heightResult = 100;        if(widthMode != MeasureSpec.AT_MOST) {            widthResult = widthSize;        }        if(heightMode != MeasureSpec.AT_MOST) {            heightResult = heightSize;        }        int resultSize = widthResult > heightResult                ? heightResult : widthResult;        setMeasuredDimension(resultSize, resultSize);    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        mFPts = new float[] {                0, 0,                getMeasuredWidth() / 2, 0,                getMeasuredWidth(), 0,                getMeasuredWidth(), getMeasuredHeight() / 2,                getMeasuredWidth(), getMeasuredHeight(),                getMeasuredWidth() / 2, getMeasuredHeight(),                0, getMeasuredHeight(),                0, getMeasuredHeight() /2,                getMeasuredHeight() / 2, getMeasuredHeight() /2        };        mRectF = new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight());        mRectOvalF = new RectF(mPointStrokeWidth, mPointStrokeWidth,                getMeasuredWidth() - mPointStrokeWidth, getMeasuredHeight() / 2);        mRightBottomRectF = new RectF(getMeasuredWidth() / 2, getMeasuredHeight() /2,                getMeasuredWidth() - mPointStrokeWidth, getMeasuredHeight() - mPointStrokeWidth);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        canvas.drawLine(mFPts[0], mFPts[1], mFPts[2], mFPts[2], mPointPaint);        mPointPaint.setColor(Color.BLUE);        canvas.drawLines(mFPts, mPointPaint);    }}复制代码

MyDrawView中没有考虑padding的影响。

画点

几何图形中,最简单的就是点了,首先画点。

drawPoint(float x, float y, Paint paint) drawPoints(float[] pts, Paint paint) drawPoints(float[] pts, int offset, int count, Paint paint)

x是点的横坐标,y是点的纵坐标。坐标的点也可以放到数组pts中,可见数组的个数一般是偶数个,offset是开始绘制前,数组中忽略的元素个数。count是忽略了offset个点后,开始取count个元素来绘制点。

canvas.drawPoints(mFPts, mPointPaint);mPointPaint.setColor(Color.BLUE);canvas.drawPoint(getMeasuredWidth() / 2, 0, mPointPaint);复制代码

画线

由点组成线,两点确定一条直线。

drawLine(float startX, float startY, float stopX, float stopY, Paint paint) drawLines(float[] pts, int offset, int count, Paint paint) drawLines(float[] pts, Paint paint)

第一个是,直接指定直线的两个点坐标。pts是点的坐标,每两个数组元素确定一个点坐标,每四个元素确定直线的两个点的坐标。

canvas.drawLine(mFPts[0], mFPts[1], mFPts[2], mFPts[2], mPointPaint);mPointPaint.setColor(Color.BLUE);canvas.drawLines(mFPts, mPointPaint);复制代码

画矩形

由线可以组成面。矩形可以是长方形,也可以是正方形。

RectF和Rect的区别是参数的类型不同,RectF的参数类型是float,Rect的参数类型是int。

drawRect(float left, float top, float right, float bottom, Paint paint) drawRect(float left, float top, float right, float bottom, Paint paint) drawRect(Rect r, Paint paint) drawRect( RectF rect, Paint paint)

也就是,可以在RectF或者Rect中指定好顶点坐标再传给drawRect,也可以在drawRect方法中直接指定顶点坐标。

mRectF = new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight());canvas.drawRect(mRectF, mPointPaint);复制代码

代码说明,第一行代码是在onSizeChanged重写方法中的,第二行代码是在onDraw方法中的。因为onDraw方法是会不断被调用的,不适合在里面创建对象。

圆角矩形

圆角矩形是在矩形的基础上生成的。

drawRoundRect(RectF rect, float rx, float ry, Paint paint) drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint)

rx是生成圆角的椭圆的X轴半径 ry是生成圆角的椭圆的Y轴半径

canvas.drawRoundRect(mRectF, getMeasuredWidth() / 4, getMeasuredHeight() / 4, mPointPaint);复制代码

画圆

圆要指定圆心的坐标和半径的大小。

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

cx和cy分别是圆心的横坐标和纵坐标,radius为半径。

canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2,                getMeasuredHeight() / 2 - mPointStrokeWidth, mPointPaint);复制代码

画椭圆

椭圆是在矩形基础上生成的,以矩形的长为长轴,矩形的宽为短轴。特殊的,当长轴等于短轴时,椭圆就是圆。

drawOval(RectF oval, @NonNull Paint paint) drawOval(float left, float top, float right, float bottom, Paint paint)

mRectOvalF = new RectF(mPointStrokeWidth, mPointStrokeWidth,                getMeasuredWidth() - mPointStrokeWidth, getMeasuredHeight() / 2);                canvas.drawOval(mRectOvalF, mPointPaint);复制代码

画弧

弧是在椭圆上按一定角度截取的一部分。

drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint) drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint)

oval是椭圆基于的矩形顶点的矩阵,或者在方法中直接指定四个顶点,startAngle是截取的起始角度,sweepAngle是弧持续的角度,useCenter是否显示长短半径。

canvas.drawArc(mRectOvalF, 0, 90, true, mPointPaint);复制代码

canvas.drawArc(mRectOvalF, 0, 90, false, mPointPaint);复制代码

Path

在View的绘制过程中,有一个类叫做Path,Path可以帮助我们实现很多自定义形状的路径,特别是配合xfermode属性来使用的时候,可以实现很多效果。

moveTo

路径开始绘制的点叫起始点坐标,默认是(0,0)。可以使用moveTo将绘制路径的起始点移动到某个位置。moveTo不进行绘制,一般用来移动画笔。

lineTo

lineTo用来绘制一条直线路径。

mPath.moveTo(getMeasuredWidth()/ 2, getMeasuredHeight() / 2);mPath.lineTo(getMeasuredWidth(), getMeasuredHeight());canvas.drawPath(mPath, mPointPaint);复制代码

直线路径的起始点是(getMeasuredWidth()/ 2, getMeasuredHeight() / 2),终点是(getMeasuredWidth(), getMeasuredHeight())

quadTo

quadTo用来画由一个控制点控制的贝塞尔曲线。

mPath.moveTo(mPointStrokeWidth, getMeasuredHeight() / 2);mPath.quadTo(0, 0, getMeasuredWidth() / 2, mPointStrokeWidth);canvas.drawPath(mPath, mPointPaint);复制代码

起始点是(mPointStrokeWidth, getMeasuredHeight() / 2),控制点是(0, 0),终点是(getMeasuredWidth() / 2, mPointStrokeWidth)

cubicTo

cubicTo用来画由两个控制点控制的贝塞尔曲线。

mPath.moveTo(mPointStrokeWidth, getMeasuredHeight() / 2);mPath.cubicTo(0, 0, getMeasuredWidth() / 2, mPointStrokeWidth,         getMeasuredWidth(), getMeasuredHeight() / 2);canvas.drawPath(mPath, mPointPaint);复制代码

起始点是(mPointStrokeWidth, getMeasuredHeight() / 2),两个控制点是(0, 0)和(getMeasuredWidth() / 2, mPointStrokeWidth),终点是(getMeasuredWidth(), getMeasuredHeight() / 2)。

arcTo

arcTo用来画一条圆弧路径。与前面画圆弧一样的,圆弧是截取椭圆的一部分,而椭圆是基于矩形的。

mRectOvalF = new RectF(mPointStrokeWidth, mPointStrokeWidth,    getMeasuredWidth() - mPointStrokeWidth, getMeasuredHeight() / 2);mPath.arcTo(mRectOvalF, 0, 90, false);canvas.drawPath(mPath, mPointPaint);复制代码

和刚开始的圆弧参数定义一样,指定基于的矩形的四个顶点,startAngle截取的起始角度,sweepAngle弧持续的角度,useCenter是否显示长短半径。

Path的addArc、addRoundRect、addOval、addRect、addCircle

它们实现的几何路径,可以自己尝试一下。

Path.Op

在开头,mPointPaint首先设置画笔的样式为描边STROKE,后面为了更好看出Path.Op的效果会改为FILL填充。

mRightBottomRectF = new RectF(getMeasuredWidth() / 2, getMeasuredHeight() /2, getMeasuredWidth() - mPointStrokeWidth, getMeasuredHeight() - mPointStrokeWidth);mPath.addCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2,    getMeasuredHeight() / 2 - mPointStrokeWidth, Path.Direction.CCW);canvas.drawPath(mPath, mPointPaint);mPath1.addRect(mRightBottomRectF, Path.Direction.CCW);canvas.drawPath(mPath1, mPointPaint);复制代码

Path.Op.DIFFERENCE

mPath.addCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2,                getMeasuredHeight() / 2 - mPointStrokeWidth,                Path.Direction.CCW);mPath1.addRect(mRightBottomRectF, Path.Direction.CCW);mPath.op(mPath1, Path.Op.DIFFERENCE);canvas.drawPath(mPath, mPointPaint);复制代码

Path.Op.INTERSECT

mPath.addCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2,                getMeasuredHeight() / 2 - mPointStrokeWidth,                Path.Direction.CCW);mPath1.addRect(mRightBottomRectF, Path.Direction.CCW);mPath.op(mPath1, Path.Op.INTERSECT);canvas.drawPath(mPath, mPointPaint);复制代码

Path.Op.REVERSE_DIFFERENCE

mPath.addCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2,                getMeasuredHeight() / 2 - mPointStrokeWidth,                Path.Direction.CCW);mPath1.addRect(mRightBottomRectF, Path.Direction.CCW);mPath.op(mPath1, Path.Op.REVERSE_DIFFERENCE);canvas.drawPath(mPath, mPointPaint);复制代码

Path.Op.XOR

mPath.addCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2,                getMeasuredHeight() / 2 - mPointStrokeWidth,                Path.Direction.CCW);mPath1.addRect(mRightBottomRectF, Path.Direction.CCW);mPath.op(mPath1, Path.Op.XOR);canvas.drawPath(mPath, mPointPaint);复制代码

最后,例子###

(一)例子一

public class MyDrawView extends View {    private Paint mGraphPaint;    private Paint mPointPaint;    private Paint mRectPaint;    private RectF mRectF;    private Paint mLinesPaint;    private float mFPts[];    private float mFLinePts[];    private Path mPath;    public MyDrawView(Context context) {        super(context);        init();    }    public MyDrawView(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);        init();    }    public MyDrawView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    private int mPointStrokeWidth;    private void init() {        mGraphPaint = new Paint();        mGraphPaint.setColor(Color.GREEN);        mGraphPaint.setStrokeWidth(5);        mGraphPaint.setStyle(Paint.Style.STROKE);        mGraphPaint.setShadowLayer(50, 30,30, Color.BLUE);        mPointStrokeWidth = 20;        mPointPaint = new Paint();        mPointPaint.setColor(Color.RED);        mPointPaint.setStrokeWidth(mPointStrokeWidth);        mPointPaint.setStyle(Paint.Style.FILL);        mRectPaint = new Paint();        mRectPaint.setColor(Color.BLACK);        mRectPaint.setStrokeWidth(5);        mRectPaint.setStyle(Paint.Style.STROKE);        mLinesPaint = new Paint();        mLinesPaint.setColor(Color.GRAY);        mLinesPaint.setStrokeWidth(5);        mLinesPaint.setStyle(Paint.Style.STROKE);        mPath = new Path();    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        //super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int widthMode = MeasureSpec.getMode(widthMeasureSpec);        int widthSize = MeasureSpec.getSize(widthMeasureSpec);        int heightMode = MeasureSpec.getMode(heightMeasureSpec);        int heightSize = MeasureSpec.getSize(heightMeasureSpec);        int widthResult = 100;        int heightResult = 100;        if(widthMode != MeasureSpec.AT_MOST) {            widthResult = widthSize;        }        if(heightMode != MeasureSpec.AT_MOST) {            heightResult = heightSize;        }        int resultSize = widthResult > heightResult                ? heightResult : widthResult;        setMeasuredDimension(resultSize, resultSize);    }    private int num = 30;    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        mRectF = new RectF(                num - mPointStrokeWidth / 2,                num - mPointStrokeWidth / 2,                getMeasuredWidth() - num +  mPointStrokeWidth / 2,                getMeasuredHeight() - num + mPointStrokeWidth / 2);        mFPts = new float[] {                getMeasuredWidth() / 2, num,                getMeasuredWidth() - num, getMeasuredHeight()/ 2,                getMeasuredWidth() / 2 ,getMeasuredHeight() - num,                num, getMeasuredHeight()/ 2        };    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        canvas.drawCircle( getMeasuredHeight() / 2,                getMeasuredHeight() / 2,                getMeasuredHeight() / 2 - num,                mGraphPaint);        canvas.drawRect(mRectF, mRectPaint);        canvas.drawPoints(mFPts, mPointPaint);        canvas.drawLines(mFPts, mLinesPaint);        mPath.moveTo(mFPts[0], mFPts[1]);        mPath.lineTo(mFPts[2], mFPts[3]);        mPath.lineTo(mFPts[4], mFPts[5]);        mPath.lineTo(mFPts[6], mFPts[7]);        mPath.close();        canvas.drawPath(mPath, mLinesPaint);        canvas.drawLine(mFPts[0], mFPts[1], mFPts[4], mFPts[5], mLinesPaint);        canvas.drawLine(mFPts[2], mFPts[3], mFPts[6], mFPts[7], mLinesPaint);        mPath.moveTo(mFPts[6], mFPts[7]);        mPath.quadTo(mFPts[0], mFPts[1], mFPts[2], mFPts[3]);        canvas.drawPath(mPath, mLinesPaint);        mPath.moveTo(num - mPointStrokeWidth / 2,                getMeasuredHeight() - num + mPointStrokeWidth / 2);        mPath.cubicTo(mFPts[4], mFPts[5],                getMeasuredWidth() - num +  mPointStrokeWidth / 2, getMeasuredHeight() - num +                        mPointStrokeWidth / 2,                mFPts[2], mFPts[3]);        canvas.drawPath(mPath, mLinesPaint);    }}复制代码

效果图:

(二)例子二

例子引用自这里

public class MyDrawView extends View {    private Paint mPaint;    private int mOffsetX;    private int mOffsetY;    public MyDrawView(Context context) {        super(context);        init();    }    public MyDrawView(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);        init();    }    public MyDrawView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    private void init() {        mPaint = new Paint();        mPaint.setColor(Color.RED);        mPaint.setStrokeWidth(5);        mPaint.setStyle(Paint.Style.STROKE);    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        //super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int widthMode = MeasureSpec.getMode(widthMeasureSpec);        int widthSize = MeasureSpec.getSize(widthMeasureSpec);        int heightMode = MeasureSpec.getMode(heightMeasureSpec);        int heightSize = MeasureSpec.getSize(heightMeasureSpec);        int widthResult = 100;        int heightResult = 100;        if(widthMode != MeasureSpec.AT_MOST) {            widthResult = widthSize;        }        if(heightMode != MeasureSpec.AT_MOST) {            heightResult = heightSize;        }        int resultSize = widthResult > heightResult                ? heightResult : widthResult;        setMeasuredDimension(resultSize, resultSize);    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        mOffsetX = w / 2;        mOffsetY = h / 2 - 55;    }    private Point getHeartPoint(float angle) {        float t = (float) (angle / Math.PI);        float x = (float) (19.5 * (16 * Math.pow(Math.sin(t), 3)));        float y = (float) (-20 * (13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t)));        return new Point(mOffsetX + (int) x, mOffsetY + (int) y);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        float angle = 10;        while (angle < 180) {            Point p = getHeartPoint(angle);            canvas.drawPoint(p.x, p.y, mPaint);            angle = angle + 0.02f;        }    }}复制代码

关于画笔和画布的使用,到这里是未完的,其他的效果,以后有时间再补充。谢谢大家的观看。

更多相关文章

  1. 没有一行代码,「2020 新冠肺炎记忆」这个项目却登上了 GitHub 中
  2. Android(安卓)ActivityManagerService(AMS)的进程管理
  3. android 示例源码
  4. 『ANDROID』App工程结构搭建:几种常见Android代码架构分析
  5. android中的jar包反编译修改心得
  6. Android(安卓)插件框架 xCombine
  7. Android(安卓)StrictMode详解
  8. Android\OPhone自定义视图(View)
  9. 我的Android(安卓)NDK之旅(一),不使用ndk-build命令来创建jni

随机推荐

  1. Android分类列表之ListView-ViewType实现
  2. cocos 3.2 工程移植到android
  3. MVC模式
  4. Android(安卓)Studio代码自动检测错误提
  5. Android(安卓)studio 升级到4.0-4.0.1版
  6. Android初级教程理论知识(第六章广播接受
  7. android实现蓝牙文件发送的实例代码,支持
  8. android5.0以后 framework 添加资源 编译
  9. 【Android注释技巧】Android函数上面的注
  10. Android(安卓)MMS源码结构