当前位置: 首页 > news >正文

Android之 Canvas绘制

一 Canvas介绍

1.1 Canvas 是绘制图形的重要类之一,它可以在 View 或 SurfaceView 上绘制各种图形和文本.

1.2 要创建 Canvas,首先需要有一个 View 或 SurfaceView 对象,在 View 或 SurfaceView 的绘制方法中,可以通过 Canvas 的锁定方法来获取一个 Canvas 对象,然后在 Canvas 对象上进行绘制。

二 Paint画笔

2.1 新建画笔

Paint paint=new Paint();

2.2  设置样式(填充或描边)

paint.setStyle(Paint.Style.FILL_AND_STROKE);

2.3 设置画笔颜色

paint.setColor(Color.RED);

2.4  设置线条宽度

paint.setStrokeWidth(4);

2.5  开启抗锯齿功能

paint.setAntiAlias(true);

2.6 绘制虚线

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

 2.7 绘制渐变

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 设置线条链接处样式

paint.setStrokeJoin(Paint.Join.ROUND);

 2.9 设置线头模式为圆角

paint.setStrokeCap(Paint.Cap.ROUND);

2.10  设置字体风格会粗体

paint.setTypeface(Typeface.DEFAULT_BOLD);

 2.11 设置图层混合模式

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

2.11 设置特殊效果,比如模糊效果 

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

三 Canvas画布

3.1 新建画布

Canvas canvas=new Canvas();

3.2 画线

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

3.3 画方形

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

3.4 画圆角矩形

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

3.5 画圆形

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

3.6 画扇形

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

3.7 绘制一个点

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

3.8 绘制颜色

canvas.drawColor(@ColorInt int color)

3.9 绘制字体

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

3.10 绘制路径

canvas.drawPath(Path path, paint)

3.11 绘制Bitmap

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

3.12 保存画布

canvas.save(int saveFlags)

3.12 恢复画布

canvas.restore()

3.14 保存图层

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

3.15 画布平移

canvas.translate(float dx, float dy)

3.16 画布缩放

canvas.scale(float sx, float sy)

3.17 画布旋转

canvas.rotate(float degrees)

3.18 画布倾斜

canvas.skew(float sx, float sy)

四 实例-基础图形绘制

4.1 定义属性文件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 新建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();}//    @Overrideprotected 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);
//    }@Overrideprotected 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 使用自定义view

<?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.CanvasBaseViewandroid: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 {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_canvas_base);}
}

4.4 效果

 

五  实例-剪切蒙版圆角

5.1 利用setXfermode混合图层裁剪蒙版

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 效果

六 实例-根据图纸坐标集合动画绘制路径Path

6.1 获取坐标集合

;: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 绘制坐标路径

/*** ;: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);}@Overrideprotected 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();}@Overrideprotected 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() {@Overridepublic 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 效果

相关文章:

Android之 Canvas绘制

一 Canvas介绍 1.1 Canvas 是绘制图形的重要类之一&#xff0c;它可以在 View 或 SurfaceView 上绘制各种图形和文本. 1.2 要创建 Canvas&#xff0c;首先需要有一个 View 或 SurfaceView 对象&#xff0c;在 View 或 SurfaceView 的绘制方法中&#xff0c;可以通过 Canvas 的…...

Vue + Element UI 前端篇(十五):嵌套外部网页

Vue Element UI 实现权限管理系统 前端篇&#xff08;十五&#xff09;&#xff1a;嵌套外部网页 嵌套外部网页 在有些时候&#xff0c;我们需要在我们的内容栏主区域显示外部网页。如查看服务端提供的SQL监控页面&#xff0c;接口文档页面等。 这个时候就要求我们的导航菜…...

Jabbi的Rust学习日记(二)

特征&#xff1a; 就目前我学习到的rust知识来看&#xff0c;我认为rust有以下几个特征&#xff1a; 链式调用表达式强类型 use 使用use导入包&#xff0c;我觉得rust的导包和python的很像 main main函数是rust可执行程序最先执行的代码&#xff0c;可以说是程序的入口&…...

【杂】环形时钟配色笔记

配色网站笔记 coolorsflatuicolorscolordrophttps://www.webdesignrankings.com/resources/lolcolors/ 配色2...

会话跟踪技术学习笔记(Cookie+Session)+ HTTP学习笔记

一、核心知识点&#xff08;重点&#xff09;&#xff1a; 1.1 Cookie 1. Cookie&#xff1a;是一种客户端会话技术&#xff0c;数据会被保存在客户端&#xff0c;Cookie会携带数据访问服务器&#xff0c;用以完成一次会话内多次请求间的数据共享 2. 过程&#xff1a;浏览器…...

分类预测 | MATLAB实现PCA-BiLSTM(主成分双向长短期记忆神经网络)分类预测

分类预测 | MATLAB实现PCA-BiLSTM(主成分双向长短期记忆神经网络)分类预测 目录 分类预测 | MATLAB实现PCA-BiLSTM(主成分双向长短期记忆神经网络)分类预测预测效果基本介绍程序设计参考资料致谢 预测效果 基本介绍 分类预测 | MATLAB实现PCA-BiLSTM(主成分双向长短期记忆神经网…...

Yarn 和 npm 的区别

Yarn 和 npm 都是 JavaScript 的包管理工具&#xff0c;它们的主要区别在于以下几个方面&#xff1a; 性能&#xff1a;Yarn 的安装速度和包的下载速度通常比 npm 更快&#xff0c;这是因为 Yarn 使用本地缓存和并行下载等技术来提高性能。 可靠性&#xff1a;Yarn 具有更好的…...

第20章 原子操作实验(iTOP-RK3568开发板驱动开发指南 )

在上一章节的实验中&#xff0c;对并发与竞争进行了实验&#xff0c;两个app应用程序之间对共享资源的竞争访问引起了数据传输错误&#xff0c;而在Linux内核中&#xff0c;提供了四种处理并发与竞争的常见方法&#xff0c;分别是原子操作、自旋锁、信号量、互斥体&#xff0c;…...

Android 开机自启动

APP需要开机自启动&#xff0c;要通过开机广播实现。 1&#xff0c;在AndroidManifest.xml中增加权限 <!-- .接收启动完成的广播权限 --><uses-permission android:name"android.permission.RECEIVE_BOOT_COMPLETED" /> 2&#xff0c;在AndroidManifes…...

01_前端css编写的三种方式

前言 CSS的引入方式共有三种&#xff1a;行内样式、内部样式表、外部样式表 一、内联式引入 用法&#xff1a; 在元素上直接通过style属性进行设置css样式设置 示例&#xff1a; <h1 style"color:red;">style属性的应用</h1> <p style"font-si…...

07-垃圾收集算法详解

上一篇&#xff1a;06-JVM对象内存回收机制深度剖析 1.分代收集理论 当前虚拟机的垃圾收集都采用分代收集算法&#xff0c;这种算法没有什么新的思想&#xff0c;只是根据对象存活周期的不同将内存分为几块。一般将java堆分为新生代和老年代&#xff0c;这样我们就可以根据各…...

Redis高并发分布式锁实战

高并发场景秒杀抢购超卖bug实战重现 秒杀抢购场景下实战JVM级别锁与分布式锁 大厂分布式锁Resisson框架实战 Lua脚本语言快速入门与使用注意事项 Redisson分布式锁源码剖析 Redis主从架构锁失效问题解析 从CAP角度剖析Redis与Zookeeper分布式锁区别 Redlock分布式锁原理与…...

MybatisPlus分页插件使用

一. 效果展示 二. 代码编写 2.1 pom <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.2</version> </dependency>2.2 添加配置类 Configuration MapperScan(…...

Linux指令二【进程,权限,文件】

进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程&#xff0c;是操作系统进行 资源分配和调度的一个独立单位&#xff0c;是应用程序运行的载体。 一、进程基本指令 1.ps&#xff1a;当前的用户进程 ps 只显示隶属于自己的进程状态ps -aux 显示所有进程…...

uni-app运行到微信开发者工具-没有打印的情况

前言 到我们进场使用微信开发者工具时&#xff0c;就会发现它经常会有bug&#xff0c;特别是在软件更新&#xff0c;组件库更新之后 最近在更新微信开发者工具之后发现所有打印都不显示了&#xff0c;虽然是小问题-但对于强迫症很烦 以为是代码配置问题-结果是更新之后打印开…...

由前端接口入门学习后端的controller层

由前端接口入门学习后端的controller层 一、简单介绍一下controller层&#xff1a;二、前端调用后端接口时&#xff0c;一般会传递参数给后端&#xff0c;后端的控制层是如何接收的呢&#xff1f;三、更深入地介绍一下关于请求体参数DTO作为入参Map作为入参 本文是以一个前端工…...

HJ71 字符串通配符

Powered by:NEFU AB-IN Link 文章目录 HJ71 字符串通配符题意思路代码 HJ71 字符串通配符 题意 问题描述&#xff1a;在计算机中&#xff0c;通配符一种特殊语法&#xff0c;广泛应用于文件搜索、数据库、正则表达式等领域。现要求各位实现字符串通配符的算法。 要求&#xff…...

ffmpeg 开发笔记

参考&#xff1a; FFmpeg音视频处理 - 知乎 通过python实时生成音视频数据并通过ffmpeg推送和混流 - 知乎 直播常用 FFmpeg & ffplay 命令 - 知乎 音视频 FFMPEG 滤镜使用 - 知乎 官网&#xff1a; ffmpeg Documentation...

一种基于注意机制的快速、鲁棒的混合气体识别和浓度检测算法,配备了具有双损失函数的递归神经网络

A fast and robust mixture gases identification and concentration detection algorithm based on attention mechanism equipped recurrent neural network with double loss function 摘要 提出一个由注意力机制组成的电子鼻系统。首先采用端到端的编码器译码器&#xff…...

[运维|系统] go程序设置开机启动踩坑笔记

参考文献 记systemctl启动go程序 在Ubuntu上作为systemctl服务运行时Go找不到文件 go语言程序设置开机启动&#xff0c;配置不生效 需要在服务配置文件中加入工作目录配置&#xff0c;示例 WorkingDirectory/path/to/go/program/directory...

Python|GIF 解析与构建(5):手搓截屏和帧率控制

目录 Python&#xff5c;GIF 解析与构建&#xff08;5&#xff09;&#xff1a;手搓截屏和帧率控制 一、引言 二、技术实现&#xff1a;手搓截屏模块 2.1 核心原理 2.2 代码解析&#xff1a;ScreenshotData类 2.2.1 截图函数&#xff1a;capture_screen 三、技术实现&…...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

Java 加密常用的各种算法及其选择

在数字化时代&#xff0c;数据安全至关重要&#xff0c;Java 作为广泛应用的编程语言&#xff0c;提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景&#xff0c;有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

C++.OpenGL (10/64)基础光照(Basic Lighting)

基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...

STM32---外部32.768K晶振(LSE)无法起振问题

晶振是否起振主要就检查两个1、晶振与MCU是否兼容&#xff1b;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容&#xff08;CL&#xff09;与匹配电容&#xff08;CL1、CL2&#xff09;的关系 2. 如何选择 CL1 和 CL…...

Linux系统部署KES

1、安装准备 1.版本说明V008R006C009B0014 V008&#xff1a;是version产品的大版本。 R006&#xff1a;是release产品特性版本。 C009&#xff1a;是通用版 B0014&#xff1a;是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存&#xff1a;1GB 以上 硬盘&#xf…...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!

本文介绍了一种名为AnomalyAny的创新框架&#xff0c;该方法利用Stable Diffusion的强大生成能力&#xff0c;仅需单个正常样本和文本描述&#xff0c;即可生成逼真且多样化的异常样本&#xff0c;有效解决了视觉异常检测中异常样本稀缺的难题&#xff0c;为工业质检、医疗影像…...