一 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 利用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;
}
4.2 效果
五 实例-根据图纸坐标集合动画绘制路径Path
5.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;@;@
5.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);
}
@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());
}
}
5.3 效果