Android Canvas drawing

An introduction to Canvas

1.1 Canvas is one of the important classes for drawing graphics, it can draw various graphics and text on View or SurfaceView.

1.2 To create a Canvas, you first need a View or SurfaceView object. In the drawing method of the View or SurfaceView, you can obtain a Canvas object through the Canvas lock method, and then draw on the Canvas object.

Two Paint brushes

2.1 Create a new brush

Paint paint=new Paint();

2.2 Set style (fill or stroke)

paint.setStyle(Paint.Style.FILL_AND_STROKE);

2.3 Set brush color

paint.setColor(Color.RED);

2.4 Set line width

paint.setStrokeWidth(4);

2.5 Turn on anti-aliasing function

paint.setAntiAlias(true);

2.6 Draw a dotted line

PathEffect effects = new DashPathEffect(new float[]{5, 10}, 0);
paint.setPathEffect(effects);

 2.7 Draw gradient

int[] mColors = {Color.parseColor("#FF9800"),Color.parseColor("#FFCE85"),Color.parseColor("#FFEBCF")};
LinearGradient linearGradient=new LinearGradient(50, 0, 50, heightSize, mColors, null, Shader.TileMode.MIRROR);
paint.setShader(linearGradient);

2.8 Set the style of line links

paint.setStrokeJoin(Paint.Join.ROUND);

 2.9 Set the thread end mode to rounded corners

paint.setStrokeCap(Paint.Cap.ROUND);

2.10 Setting the font style will make it bold

paint.setTypeface(Typeface.DEFAULT_BOLD);

 2.11 Set layer blending mode

paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));

2.11 Set special effects, such as blur effects 

MaskFilter blur = new BlurMaskFilter(10, BlurMaskFilter.Blur.NORMAL);paint.setMaskFilter(blur);
paint.setMaskFilter(blur);

Three Canvas Canvas

3.1 Create a new canvas

Canvas canvas=new Canvas();

3.2 Drawing lines

canvas.drawLine(float startX ,float  startY ,float endX ,float endY ,Paint paint);

3.3 Draw a square

canvas.drawRect(float left ,float top ,float  right ,float bottom ,Paint paint);

3.4 Draw a rounded rectangle

canvas.drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
            paint)

3.5 Draw a circle

canvas.drawCircle(float cX ,float cY ,float redius ,Paint paint);

3.6 Fan shape

canvas.drawArc(float left, float top, float right, float bottom, float startAngle,
            float sweepAngle, boolean useCenter, @NonNull Paint paint);

3.7 Draw a point

canvas.drawPoint(float x, float y , Paint paint)

3.8 Drawing colors

canvas.drawColor(@ColorInt int color)

3.9 Draw fonts

canvas.rawText(tring text, float x, float y, paint)

3.10 Drawing paths

canvas.drawPath(Path path, paint)

3.11 Draw Bitmap

canvas.drawBitmap(Bitmap bitmap, float left, float top, Paint paint)

3.12 Save canvas

canvas.save(int saveFlags)

3.12 Restore canvas

canvas.restore()

3.14 Save layer

canvas.saveLayer(RectF bounds, Paint paint, int saveFlags)

3.15 Canvas translation

canvas.translate(float dx, float dy)

3.16 Canvas scaling

canvas.scale(float sx, float sy)

3.17 Canvas rotation

canvas.rotate(float degrees)

3.18 Canvas tilt

canvas.skew(float sx, float sy)

Four Examples-Basic Graphic Drawing

4.1 Define the properties file arrts.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CavasBaseView">
        <attr name="paint_color" format="color" />
        <attr name="default_width" format="dimension" />
        <attr name="default_height" format="dimension" />
    </declare-styleable>
</resources>

4.2 Create a new View, CanvasBaseView.java

package com.juai.canvastest;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import androidx.annotation.Nullable;

public class CanvasBaseView extends View {
    private static final String TAG = "CanvasBaseView";
    //默认属性
    private int paintColor;
    private int defaultWidth;
    private int defaultHeight;

    public CanvasBaseView(Context context) {
        super(context);
    }

    public CanvasBaseView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public CanvasBaseView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //默认属性
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CavasBaseView);
        paintColor = array.getColor(R.styleable.CavasBaseView_paint_color, Color.RED);
        defaultWidth = array.getColor(R.styleable.CavasBaseView_default_width, 0);
        defaultHeight = array.getColor(R.styleable.CavasBaseView_default_height,0);
        array.recycle();
    }


    //    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 自己算的画 没有必要再让 view 自己测量一遍了,浪费资源
        // super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);   //获取宽的模式
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);   //获取宽的尺寸
        int heightMode = MeasureSpec.getMode(heightMeasureSpec); //获取高的模式
        int heightSize = MeasureSpec.getSize(heightMeasureSpec); //获取高的尺寸
        Log.d(TAG, "宽的模式:"+widthMode);
        Log.d(TAG, "宽的尺寸:"+widthSize);
        Log.d(TAG, "高的模式:"+heightMode);
        Log.d(TAG, "高的尺寸:"+heightSize);

        // 开始计算宽度
        int widthResult = 0;
        switch (widthMode) {
            case MeasureSpec.AT_MOST://不超过
                // 在 AT_MOST 模式下,取二者的最小值
                //widthSize测量的实际宽高,widthResult期望的最大宽高
                widthResult=Math.min(widthSize,defaultWidth);
                break;
            case MeasureSpec.EXACTLY://精准的
                // 父 View 给多少用多少
                widthResult = widthSize;
                break;
            case MeasureSpec.UNSPECIFIED://无限大,没有指定大小
                // 默认的大小
                widthResult = defaultWidth;
                break;
            default:
                widthResult = 0;
                break;
        }



        // 开始计算宽度
        int heightResult = 0;
        switch (widthMode) {
            case MeasureSpec.AT_MOST://不超过
                // 在 AT_MOST 模式下,取二者的最小值
                // heightSize测量的实际宽高,heightResult期望的最大宽高
                heightResult=Math.min(heightSize,heightResult);
                break;
            case MeasureSpec.EXACTLY://精准的
                // 父 View 给多少用多少
                heightResult = heightSize;
                break;
            case MeasureSpec.UNSPECIFIED://无限大,没有指定大小
                // 使用计算出的大小
                heightResult = heightResult;
                break;
            default:
                heightResult = 0;
                break;
        }
        // 设置最终的宽高
        setMeasuredDimension(widthResult, heightResult);
    }

//   //上面模版代码其实 Android SDK 里面早就有了很好的封装 : resolveSize(int size, int measureSpec) 和 resolveSizeAndState(int size, int measureSpec, int childMeasuredState) ,两行代码直接搞定。
//    @Override
//    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//        // 没有必要再让 view 自己测量一遍了,浪费资源
//        // super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//
//        // 指定期望的 size
//        int width = resolveSize(defaultWidth, widthMeasureSpec);
//        int height = resolveSize(defaultHeight, heightMeasureSpec);
//        // 设置大小
//        setMeasuredDimension(width, height);
//    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 创建画笔
        Paint p = new Paint();
        p.setColor(Color.RED);// 设置红色
        p.setTextSize(16);

        canvas.drawText("画圆:", 10, 20, p);// 画文本
        canvas.drawCircle(60, 20, 10, p);// 小圆
        p.setAntiAlias(true);// 设置画笔的锯齿效果。 true是去除
        canvas.drawCircle(120, 20, 20, p);// 大圆

        canvas.drawText("画线及弧线:", 10, 60, p);
        p.setColor(Color.GREEN);// 设置绿色
        canvas.drawLine(60, 40, 100, 40, p);// 画线
        canvas.drawLine(110, 40, 190, 80, p);// 斜线
        //画笑脸弧线
        p.setStyle(Paint.Style.STROKE);//设置空心
        RectF oval1=new RectF(150,20,180,40);
        canvas.drawArc(oval1, 180, 180, false, p);//小弧形
        oval1.set(190, 20, 220, 40);
        canvas.drawArc(oval1, 180, 180, false, p);//小弧形
        oval1.set(160, 30, 210, 60);
        canvas.drawArc(oval1, 0, 180, false, p);//小弧形

        canvas.drawText("画矩形:", 10, 80, p);
        p.setColor(Color.GRAY);// 设置灰色
        p.setStyle(Paint.Style.FILL);//设置填满
        canvas.drawRect(60, 60, 80, 80, p);// 正方形
        canvas.drawRect(60, 90, 160, 100, p);// 长方形

        canvas.drawText("画扇形和椭圆:", 10, 120, p);

        Shader mShader = new LinearGradient(0, 0, 100, 100,
                new int[] { Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW,
                        Color.LTGRAY }, null, Shader.TileMode.REPEAT); // 一个材质,打造出一个线性梯度沿著一条线。
        p.setShader(mShader);
        // p.setColor(Color.BLUE);
        RectF oval2 = new RectF(60, 100, 200, 240);// 设置个新的长方形,扫描测量
        canvas.drawArc(oval2, 200, 130, true, p);
        // 画弧,第一个参数是RectF:该类是第二个参数是角度的开始,第三个参数是多少度,第四个参数是真的时候画扇形,是假的时候画弧线
        //画椭圆,把oval改一下
        oval2.set(210,100,250,130);
        canvas.drawOval(oval2, p);

        canvas.drawText("画三角形:", 10, 200, p);
        // 绘制这个三角形,你可以绘制任意多边形
        Path path = new Path();
        path.moveTo(80, 200);// 此点为多边形的起点
        path.lineTo(120, 250);
        path.lineTo(80, 250);
        path.close(); // 使这些点构成封闭的多边形
        canvas.drawPath(path, p);

        // 你可以绘制很多任意多边形,比如下面画六连形
        p.reset();//重置
        p.setColor(Color.LTGRAY);
        p.setStyle(Paint.Style.STROKE);//设置空心
        Path path1=new Path();
        path1.moveTo(180, 200);
        path1.lineTo(200, 200);
        path1.lineTo(210, 210);
        path1.lineTo(200, 220);
        path1.lineTo(180, 220);
        path1.lineTo(170, 210);
        path1.close();//封闭
        canvas.drawPath(path1, p);


        //画圆角矩形
        p.setStyle(Paint.Style.FILL);//充满
        p.setColor(Color.LTGRAY);
        p.setAntiAlias(true);// 设置画笔的锯齿效果
        canvas.drawText("画圆角矩形:", 10, 260, p);
        RectF oval3 = new RectF(80, 260, 200, 300);// 设置个新的长方形
        canvas.drawRoundRect(oval3, 20, 15, p);//第二个参数是x半径,第三个参数是y半径

        //画贝塞尔曲线
        canvas.drawText("画贝塞尔曲线:", 10, 310, p);
        p.reset();
        p.setStyle(Paint.Style.STROKE);
        p.setColor(Color.GREEN);
        Path path2=new Path();
        path2.moveTo(100, 320);//设置Path的起点
        path2.quadTo(150, 310, 170, 400); //设置贝塞尔曲线的控制点坐标和终点坐标
        canvas.drawPath(path2, p);//画出贝塞尔曲线

        //画点
        p.setStyle(Paint.Style.FILL);
        canvas.drawText("画点:", 10, 390, p);
        canvas.drawPoint(60, 390, p);//画一个点
        canvas.drawPoints(new float[]{60,400,65,400,70,400}, p);//画多个点

        //画图片,就是贴图
//        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
//        canvas.drawBitmap(bitmap, 250,360, p);
    }
}

4.3 Use custom views

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.juai.canvastest.CanvasBaseView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:default_height="300dp"
        app:default_width="300dp"
        app:paint_color="#000000" />
</LinearLayout>
public class CanvasBaseActivity extends Activity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_canvas_base);

    }
}

4.4 Effect

 

Five examples-clipping mask rounded corners

5.1 Use setXfermode mixed layer clipping mask

public Bitmap getPltMaskingBitmap(float widthMM, float heightMM, int averageRadius) {
        int dipValue = 300;
        Bitmap resourceBitmap = BitmapFactory.decodeFile(uvCaseFilePath);
        Log.e("HHH", "图纸的宽高: " + resourceBitmap.getWidth() + "--" + resourceBitmap.getHeight());

        int widthPX = (int) ScreenUtils.getApplyDimension(dipValue, widthMM);
        int heightPX = (int) (resourceBitmap.getHeight()* 1f/ resourceBitmap.getWidth()*widthPX);

        Bitmap bitmap = Bitmap.createBitmap(widthPX, heightPX, Bitmap.Config.ARGB_8888);
        Canvas rootCanvas = new Canvas(bitmap);
        //rootCanvas.drawColor(Color.RED);


        Paint paint = new Paint();
        paint.setColor(Color.BLACK);
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.FILL_AND_STROKE);

        //绘制蒙版
        RectF rectF = new RectF(0, 0, widthPX, heightPX);
        int radius = (int) ScreenUtils.getApplyDimension(dipValue, averageRadius);
        rootCanvas.drawRoundRect(rectF, radius, radius, paint);

        //设置混合模式
        rootCanvas.save();
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));

        //绘制原图
        paint.setColor(Color.RED);
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.FILL);
        Rect dstRect = new Rect(0, 0, widthPX, heightPX);
        rootCanvas.drawBitmap(resourceBitmap, null, dstRect, paint);
//        rootCanvas.drawRect(rectF,paint);

        return bitmap;
    }

5.2 Effect

Six examples - draw the path Path according to the drawing coordinate set animation

6.1 Get coordinate collection

;:H A L0 ECN U0,0;P0;U81,95;D81,175;U331,5191;D331,5208;D333,5244;D340,5278;D349,5311;D362,5342;D377,5371;D396,5400;D418,5426;D443,5452;D470,5475;D499,5495;D528,5511;D559,5524;D591,5534;D624,5541;D659,5544;D695,5544;D730,5542;D764,5535;D797,5526;D828,5513;D857,5498;D886,5479;D912,5457;D938,5432;D961,5405;D981,5376;D997,5347;D1010,5316;D1020,5284;D1027,5251;D1030,5216;D1031,5180;D1028,5145;D1021,5111;D1012,5078;D1000,5047;D984,5017;D965,4989;D943,4962;D918,4937;D891,4914;D863,4894;D833,4878;D802,4865;D770,4855;D737,4848;D702,4845;D667,4844;D631,4847;D597,4853;D564,4863;D533,4875;D504,4891;D475,4910;D449,4932;D423,4957;D400,4984;D380,5012;D364,5042;D351,5073;D341,5105;D334,5138;D331,5173;D331,5191;D331,5208;D332,5230;U446,5468;D452,5467;D456,5463;D468,5472;D497,5493;D527,5510;D558,5524;D590,5534;D623,5541;D656,5544;D691,5545;D726,5542;D762,5536;D795,5526;D827,5514;D856,5499;D884,5480;D911,5459;D936,5435;D959,5408;D979,5378;D996,5348;D1010,5317;D1020,5285;D1027,5252;D1030,5219;D1031,5184;D1028,5149;D1022,5113;D1012,5080;D1000,5048;D985,5019;D966,4991;D945,4964;D921,4939;D894,4916;U321,5937;D325,5933;D332,5932;D334,5915;D340,5880;D349,5848;D362,5817;D377,5787;D396,5759;D418,5732;D443,5707;D470,5684;D499,5664;D528,5647;D559,5634;D591,5624;D624,5618;D659,5614;D695,5614;D730,5617;D764,5623;D797,5632;D828,5645;D857,5661;D886,5680;D912,5701;D938,5726;D961,5754;D981,5782;D997,5812;D1010,5843;D1020,5875;D1027,5908;D1030,5942;D1031,5978;D1028,6014;D1021,6048;D1012,6080;D1000,6111;D984,6141;D965,6169;D943,6196;D918,6221;D891,6245;D863,6264;D833,6281;D802,6294;D770,6304;D737,6310;D702,6314;D667,6314;D631,6311;D597,6305;D564,6296;D533,6283;D504,6267;D475,6249;D449,6227;D423,6202;D400,6174;D380,6146;D364,6116;D351,6085;D341,6053;D334,6020;D331,5986;D330,5950;D332,5932;D334,5915;D337,5893;U1237,6092;D1240,6077;D1251,6048;D1268,6022;D1291,5999;D1317,5981;D1346,5969;D1376,5963;D1408,5962;D1440,5968;D1468,5979;D1494,5997;D1517,6019;D1536,6045;D1548,6074;D1554,6104;D1554,6136;D1549,6168;D1537,6197;D1520,6222;D1497,6245;D1471,6264;D1443,6276;D1412,6282;D1380,6282;D1348,6277;D1320,6265;D1294,6248;D1271,6226;D1253,6199;D1240,6171;D1234,6140;D1234,6108;D1237,6092;D1240,6077;D1249,6055;U1050,5548;D1046,5547;D1047,5530;D1053,5496;D1063,5463;D1075,5432;D1091,5402;D1110,5374;D1132,5347;D1157,5322;D1184,5299;D1212,5279;D1242,5263;D1273,5250;D1305,5240;D1338,5233;D1372,5230;D1408,5229;D1444,5232;D1478,5238;D1510,5248;D1541,5260;D1571,5276;D1599,5295;D1626,5317;D1652,5342;D1675,5369;D1694,5397;D1711,5427;D1724,5458;D1734,5490;D1740,5523;D1744,5558;D1744,5593;D1741,5629;D1735,5663;D1726,5695;D1713,5727;D1697,5756;D1679,5784;D1657,5811;D1632,5837;D1605,5860;D1576,5879;D1546,5896;D1516,5909;D1483,5919;D1450,5925;D1416,5929;D1380,5929;D1345,5926;D1311,5920;D1278,5911;D1247,5898;D1217,5883;D1189,5864;D1162,5842;D1137,5817;D1114,5790;D1094,5761;D1078,5732;D1064,5701;D1055,5669;D1048,5635;D1044,5601;D1044,5565;D1046,5547;D1047,5530;D1051,5509;U1686,5151;D1681,5151;D1675,5140;D1658,5124;D1636,5115;D1612,5116;D1590,5126;D1573,5143;D1565,5166;D1566,5190;D1575,5212;D1593,5228;D1616,5237;D1640,5236;D1662,5226;D1678,5209;D1687,5186;D1687,5162;D1681,5151;D1675,5140;D1658,5124;D1654,5122;U1225,5011;D1230,5007;D1237,5006;D1240,4991;D1251,4962;D1268,4936;D1291,4913;D1317,4895;D1346,4883;D1376,4877;D1408,4876;D1440,4882;D1469,4893;D1494,4911;D1517,4933;D1536,4959;D1548,4988;D1554,5018;D1554,5050;D1549,5082;D1537,5111;D1520,5136;D1497,5159;D1471,5178;D1443,5190;D1412,5196;D1380,5196;D1349,5191;D1320,5179;D1294,5162;D1271,5140;D1253,5113;D1241,5085;D1234,5054;D1234,5022;D1237,5006;D1240,4991;D1249,4969;U205,5926;D200,5925;D200,5185;D204,5137;D212,5090;D224,5045;D241,5002;D262,4961;D287,4922;D317,4885;D351,4850;D388,4818;D426,4790;D467,4767;D509,4749;D553,4735;D598,4725;D645,4720;D694,4719;D1404,4719;D1453,4723;D1500,4731;D1545,4743;D1587,4760;D1628,4781;D1667,4806;D1704,4836;D1739,4870;D1771,4907;D1799,4945;D1822,4986;D1840,5028;D1854,5072;D1864,5117;D1869,5164;D1870,5213;D1870,5973;D1867,6022;D1859,6069;D1846,6114;D1830,6156;D1809,6197;D1783,6236;D1754,6273;D1720,6309;D1683,6341;D1644,6368;D1604,6391;D1562,6409;D1518,6423;D1472,6433;D1425,6438;D1376,6439;D666,6439;D617,6436;D571,6428;D526,6415;D483,6399;D442,6378;D403,6352;D366,6323;D331,6289;D299,6252;D271,6213;D248,6173;D230,6131;D216,6087;D206,6041;D201,5994;D200,5945;D200,5925;D200,5886;U120,5925;D120,5185;D124,5128;D133,5074;D147,5021;D167,4971;D191,4924;D220,4878;D255,4835;D294,4793;D337,4756;D382,4724;D429,4697;D478,4675;D529,4658;D582,4647;D637,4641;D694,4639;D1404,4639;D1461,4643;D1516,4652;D1568,4666;D1618,4686;D1666,4710;D1711,4739;D1755,4774;D1796,4813;D1834,4856;D1866,4901;D1893,4948;D1915,4997;D1931,5048;D1943,5101;D1949,5156;D1950,5213;D1950,5973;D1946,6030;D1937,6085;D1923,6137;D1904,6187;D1879,6235;D1850,6281;D1816,6324;D1776,6365;D1733,6403;D1688,6435;D1641,6462;D1592,6484;D1541,6500;D1488,6512;D1433,6518;D1376,6519;D666,6519;D609,6515;D555,6506;D502,6492;D452,6473;D404,6449;D359,6419;D315,6385;D274,6345;D237,6302;D205,6257;D178,6210;D156,6161;D139,6110;D128,6057;D121,6002;D120,5945;D120,5925;D120,5886;U81,6027;D81,567;D85,516;D93,467;D106,421;D123,376;D145,333;D171,293;D202,254;D237,218;D276,184;D316,156;D358,132;D402,112;D448,98;D495,88;D544,82;D595,81;D2755,81;D2806,85;D2855,93;D2901,106;D2946,123;D2989,145;D3029,171;D3068,202;D3104,237;D3138,276;D3167,316;D3190,358;D3210,402;D3224,448;D3234,495;D3240,544;D3241,595;D3241,6075;D3237,6126;D3229,6175;D3216,6221;D3199,6266;D3177,6309;D3151,6349;D3120,6388;D3085,6424;D3046,6458;D3006,6487;D2964,6510;D2920,6530;D2874,6544;D2827,6554;D2778,6560;D2727,6561;D567,6561;D516,6557;D467,6549;D421,6536;D376,6519;D333,6497;D293,6471;D254,6440;D218,6405;D184,6366;D156,6326;D132,6284;D112,6240;D98,6194;D88,6147;D82,6098;D81,6047;D81,6027;D81,5987;U3240,0;@;@

6.2 Draw coordinate path

/**
 * ;:H A L0 ECN U U0,0;U4000,4000;D0,4000;D0,0;D4000,0;D4000,4000;D4000,4000;U4000,0;@;
 * 上面数据是一个100*100mm的方框,数据分解如下
 * ;:H A L0 ECN U U0,0;  是整个数据的开头命令
 * U4000,4000;   ‘U’ 代表抬刀,后面数字是点的绝对坐标,‘;’是命令结束符
 * D0,4000;   ‘D’ 代表落刀,后面数字是点的绝对坐标,‘;’是命令结束符
 *
 * @; '@'是整个数据的结束命令,‘;’是命令结束符
 * <p>
 * 分辨率是1016/inch,   值4000代表100mm  ,1 inch=25.4 mm
 */
public class DrawPLTEditView extends View {
    private static final String TAG = "DrawPLTView";
    private Context context;
    public List<PLTPointGroup> pltPointGroupList = new ArrayList<>();


    private float offerX, offerY;
    private int minX, minY;
    int maxXLength, maxYLength;
    private float ratioHeight = 1;

    private Paint paint = new Paint();

    public DrawPLTEditView(Context context) {
        this(context, null);
        this.context = context;
    }

    public DrawPLTEditView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        paint.setColor(Color.RED);
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.FILL);
    }

    public void setPltPointGroupList(List<PLTPointGroup> pltPointGroupList) {
        this.pltPointGroupList = pltPointGroupList;
        requestLayout();
        handler.sendEmptyMessageDelayed(1, 1000);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int viewWidth = 0, viewHeight = 0;
        float pltWidth = 0, pltHeight = 0;

        //图纸坐标宽高
        if (pltPointGroupList.size() > 0) {
            maxXLength = pltPointGroupList.get(0).maxXLength;
            maxYLength = pltPointGroupList.get(0).maxYLength;
            minX = pltPointGroupList.get(0).minX;
            minY = pltPointGroupList.get(0).minY;

            //图纸像素最大宽高 (maxXLength/40f 毫米)
            pltWidth = getApplyDimension((int) (maxXLength / 40f));
            pltHeight = getApplyDimension((int) (maxYLength / 40f));

            if (maxXLength > maxYLength) {//宽大于高
                viewWidth = ScreenUtils.getScreenWidth(context) - ScreenUtils.dip2px(context, 26) * 2 - ScreenUtils.dip2px(context, 32) * 2;
                //比例
                ratioHeight = decimalFloatDouble(viewWidth * 1f / pltWidth);
                viewHeight = (int) (pltHeight * ratioHeight);
                ratioHeight = ratioHeight - 0.01f;

            } else {
                viewHeight = ScreenUtils.getScreenHeight(context) -ScreenUtils.getStatusBarHeight(context)- ScreenUtils.dip2px(context, 50) * 2 - ScreenUtils.dip2px(context, 32) * 2;
                //比例
                ratioHeight = decimalFloatDouble(viewHeight * 1f / pltHeight);

                viewWidth = (int) (pltWidth * ratioHeight);
                ratioHeight = ratioHeight - 0.02f;


            }

            //解决移动超边缘不可见问题
            viewWidth = ScreenUtils.getScreenWidth(context);
            viewHeight = ScreenUtils.getScreenHeight(context)-ScreenUtils.getStatusBarHeight(context);


            //偏移
            offerX = (viewWidth - pltWidth * ratioHeight) / 2f;
            offerY = (viewHeight - pltHeight * ratioHeight) / 2f;

            Log.e(TAG, "onMeasure: " + ScreenUtils.px2dip(context, viewWidth) + "--" + ScreenUtils.px2dip(context, viewHeight));
            Log.e(TAG, "onMeasure2: " + ScreenUtils.px2dip(context, pltWidth) + "--" + ScreenUtils.px2dip(context, pltHeight));

            setMeasuredDimension((int) viewWidth, (int) viewHeight);

        }
    }

    public float decimalFloatDouble(double number) {
        BigDecimal bigDecimal = BigDecimal.valueOf(number).setScale(2, RoundingMode.DOWN);
        return bigDecimal.floatValue();
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (pltPointGroupList.size() > 0) {
            if(groupIndex>0) {
                List<PLTPointGroup> pltPointHaveGroupList = pltPointGroupList.subList(0, groupIndex);
                for (int position = 0; position < pltPointHaveGroupList.size(); position++) {
                    PLTPointGroup pltPointGroup = pltPointHaveGroupList.get(position);

                    for (int i = 0; i < pltPointGroup.pltPointList.size() - 1; i++) {
                        PLTPoint startPltPoint = pltPointGroup.pltPointList.get(i);
                        int startX = startPltPoint.x;
                        int startY = startPltPoint.y;

                        PLTPoint stopPltPoint = pltPointGroup.pltPointList.get(i + 1);
                        int stopX = stopPltPoint.x;
                        int stopY = stopPltPoint.y;

                        float startXX = getApplyDimension((startX - minX) / 40f * ratioHeight);
                        float startYY = getApplyDimension((startY - minY) / 40f * ratioHeight);
                        float stopXX = getApplyDimension((stopX - minX) / 40f * ratioHeight);
                        float stopYY = getApplyDimension((stopY - minY) / 40f * ratioHeight);

                        canvas.drawLine(startXX + offerX,
                                startYY + offerY,
                                stopXX + offerX,
                                stopYY + offerY,
                                paint);
                    }
                }
            }

            PLTPointGroup pltPointGroup = pltPointGroupList.get(groupIndex);
            for (int i = 0; i < pltPointGroup.pltPointList.size() - 1; i++) {
                if (i <= childIndex) {
                    PLTPoint startPltPoint = pltPointGroup.pltPointList.get(i);
                    int startX = startPltPoint.x;
                    int startY = startPltPoint.y;

                    PLTPoint stopPltPoint = pltPointGroup.pltPointList.get(i + 1);
                    int stopX = stopPltPoint.x;
                    int stopY = stopPltPoint.y;

                    float startXX = getApplyDimension((startX - minX) / 40f * ratioHeight);
                    float startYY = getApplyDimension((startY - minY) / 40f * ratioHeight);
                    float stopXX = getApplyDimension((stopX - minX) / 40f * ratioHeight);
                    float stopYY = getApplyDimension((stopY - minY) / 40f * ratioHeight);

                    canvas.drawLine(startXX + offerX,
                            startYY + offerY,
                            stopXX + offerX,
                            stopYY + offerY,
                            paint);
                }
            }


        }
    }

    int groupIndex = 0;
    int childIndex = 0;
    Handler handler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            if (msg.what == 1) {
                childIndex++;
                if (childIndex >= pltPointGroupList.get(groupIndex).pltPointList.size()) {
                    groupIndex++;
                    childIndex = 0;
                }

                if (groupIndex < pltPointGroupList.size()) {
                    invalidate();
                    handler.sendEmptyMessageDelayed(1, 50);
                }
            }
        }
    };

    /**
     * 一次性画完
     *
     * @param canvas
     */
    private void drawAllPoint(Canvas canvas) {
        for (int position = 0; position < pltPointGroupList.size(); position++) {
            PLTPointGroup pltPointGroup = pltPointGroupList.get(position);

            for (int i = 0; i < pltPointGroup.pltPointList.size() - 1; i++) {
                PLTPoint startPltPoint = pltPointGroup.pltPointList.get(i);
                int startX = startPltPoint.x;
                int startY = startPltPoint.y;

                PLTPoint stopPltPoint = pltPointGroup.pltPointList.get(i + 1);
                int stopX = stopPltPoint.x;
                int stopY = stopPltPoint.y;

                float startXX = getApplyDimension((startX - minX) / 40f * ratioHeight);
                float startYY = getApplyDimension((startY - minY) / 40f * ratioHeight);
                float stopXX = getApplyDimension((stopX - minX) / 40f * ratioHeight);
                float stopYY = getApplyDimension((stopY - minY) / 40f * ratioHeight);

                canvas.drawLine(startXX + offerX,
                        startYY + offerY,
                        stopXX + offerX,
                        stopYY + offerY,
                        paint);
            }
        }
    }

    /**
     * 像素=毫米x分辨率
     * dip,像素/英寸单位,1英寸=2.54厘米=25.4毫米
     * metrics.xdpi * (1.0f/25.4f)  代表分辨率x1.0fx1英寸  就是所需的dip(25.4f毫米级表示1英寸)
     * (300f / 25.4f) 一英寸上有300像素,一毫米上有 (300f / 25.4f)像素
     * value 毫米值
     */
    private float getApplyDimension(float value) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, value, context.getResources().getDisplayMetrics());
    }
}

6.3 Effect

Guess you like

Origin blog.csdn.net/qq_29848853/article/details/132696451