【安卓】自定义View实现画板涂鸦等功能

一、实现效果

画笔测试
开始设计矩形
实现矩形的随笔画
圆形
三角形,还有bug
等腰三角形和塔型
等边三角形
等边三角形计算

二、代码

1、MainActivity.class

package com.lsl.mydrawingboarddemo;import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.ImageButton;import com.lsl.mydrawingboarddemo.view.DrawingBoardView;import java.io.IOException;/*** author lishilin* date 2023/8/9* desc 负责画板UI交互*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener {private ImageButton paintBtn,fillPaintBtn,rubberBtn,rectBtn,circleBtn,ellipticBtn,triangleBtn,triangleTowerBtn,selectedBtn,clearBtn,equilateralBtn,rightAngleBtn;private ImageButton blackBtn,whiteBtn,redBtn,blueBtn,grayBtn,cyanBtn,currentColorBtn;private DrawingBoardView drawingBoardView;private View selectColorBoxView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView();}private void initView(){paintBtn = findViewById(R.id.paint);fillPaintBtn = findViewById(R.id.fillPaint);//rubberBtn = findViewById(R.id.rubber);rectBtn = findViewById(R.id.rect);circleBtn = findViewById(R.id.circle);ellipticBtn = findViewById(R.id.elliptic);triangleBtn = findViewById(R.id.triangle);triangleTowerBtn = findViewById(R.id.triangleTower);clearBtn = findViewById(R.id.clear);equilateralBtn = findViewById(R.id.equilateralTriangle);drawingBoardView = findViewById(R.id.drawView);selectColorBoxView = findViewById(R.id.selectColorBox);rightAngleBtn = findViewById(R.id.rightAngle);blackBtn = findViewById(R.id.blackBtn);whiteBtn = findViewById(R.id.whiteBtn);redBtn = findViewById(R.id.redBtn);blueBtn = findViewById(R.id.blueBtn);grayBtn = findViewById(R.id.grayBtn);cyanBtn = findViewById(R.id.cyanBtn);paintBtn.setSelected(true);selectedBtn = paintBtn;blackBtn.setSelected(true);currentColorBtn = blackBtn;paintBtn.setOnClickListener(this);fillPaintBtn.setOnClickListener(this);//rubberBtn.setOnClickListener(this);rectBtn.setOnClickListener(this);circleBtn.setOnClickListener(this);ellipticBtn.setOnClickListener(this);triangleBtn.setOnClickListener(this);triangleTowerBtn.setOnClickListener(this);clearBtn.setOnClickListener(this);blackBtn.setOnClickListener(this);whiteBtn.setOnClickListener(this);redBtn.setOnClickListener(this);blueBtn.setOnClickListener(this);grayBtn.setOnClickListener(this);equilateralBtn.setOnClickListener(this);rightAngleBtn.setOnClickListener(this);cyanBtn.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()){case R.id.paint:setSelectBtn(paintBtn);drawingBoardView.setPaint("paint");selectColorBoxView.setVisibility(View.VISIBLE);break;case R.id.fillPaint:setSelectBtn(fillPaintBtn);drawingBoardView.setPaint("fillPaint");selectColorBoxView.setVisibility(View.VISIBLE);break;
//            case R.id.rubber:
//                setSelectBtn(rubberBtn);
//                drawingBoardView.setEraseMode();
//                selectColorBoxView.setVisibility(View.INVISIBLE);
//                break;case R.id.rect:setSelectBtn(rectBtn);selectColorBoxView.setVisibility(View.VISIBLE);drawingBoardView.setPaint("rect");break;case R.id.circle:setSelectBtn(circleBtn);selectColorBoxView.setVisibility(View.VISIBLE);drawingBoardView.setPaint("drawCircle");break;case R.id.elliptic:setSelectBtn(ellipticBtn);selectColorBoxView.setVisibility(View.VISIBLE);drawingBoardView.setPaint("drawOval");break;case R.id.triangle:setSelectBtn(triangleBtn);selectColorBoxView.setVisibility(View.VISIBLE);drawingBoardView.setPaint("drawTriangle");break;case R.id.equilateralTriangle:setSelectBtn(equilateralBtn);selectColorBoxView.setVisibility(View.VISIBLE);drawingBoardView.setPaint("drawEquilateral");break;case R.id.triangleTower:setSelectBtn(triangleTowerBtn);selectColorBoxView.setVisibility(View.VISIBLE);drawingBoardView.setPaint("drawTriangleTower");break;case R.id.rightAngle:setSelectBtn(rightAngleBtn);selectColorBoxView.setVisibility(View.VISIBLE);drawingBoardView.setPaint("drawRightAngle");break;case R.id.clear:clearStyle();//设置点击清除图标时的背景闪烁selectColorBoxView.setVisibility(View.VISIBLE);if (drawingBoardView!=null){drawingBoardView.clearBoard();}break;case R.id.blackBtn:int blackColor = ContextCompat.getColor(this,R.color.black);drawingBoardView.setPaintColor(blackColor);setCurrentColorBtn(blackBtn);break;case R.id.blueBtn:int blueColor = ContextCompat.getColor(this,R.color.blue);drawingBoardView.setPaintColor(blueColor);setCurrentColorBtn(blueBtn);break;case R.id.whiteBtn:int whiteColor = ContextCompat.getColor(this,R.color.white);drawingBoardView.setPaintColor(whiteColor);setCurrentColorBtn(whiteBtn);break;case R.id.cyanBtn:int cyanColor = ContextCompat.getColor(this,R.color.cyan);drawingBoardView.setPaintColor(cyanColor);setCurrentColorBtn(cyanBtn);break;case R.id.grayBtn:int grayColor = ContextCompat.getColor(this,R.color.gray);drawingBoardView.setPaintColor(grayColor);setCurrentColorBtn(grayBtn);break;case R.id.redBtn:int redColor = ContextCompat.getColor(this,R.color.red);drawingBoardView.setPaintColor(redColor);setCurrentColorBtn(redBtn);break;default:break;}}/*** 设置选中的图形button* @param selectBtn*/private void setSelectBtn(ImageButton selectBtn){if (this.selectedBtn!=null){this.selectedBtn.setSelected(false);}selectBtn.setSelected(true);this.selectedBtn = selectBtn;}private void setCurrentColorBtn(ImageButton currentColorBtn){if (this.currentColorBtn!=null){this.currentColorBtn.setSelected(false);}currentColorBtn.setSelected(true);this.currentColorBtn = currentColorBtn;}/*** 设置点击清除图标时的样式变化,让其背景变色500毫秒*/private void clearStyle(){clearBtn.setSelected(true);new Handler().postDelayed(new Runnable() {@Overridepublic void run() {clearBtn.setSelected(false);}}, 200);}}

2、布局代码

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/bord_color"tools:context=".MainActivity"><com.lsl.mydrawingboarddemo.view.DrawingBoardViewandroid:id="@+id/drawView"android:layout_width="match_parent"android:layout_height="650dp"android:layout_alignParentTop="true"android:background="@color/bord_color" /><LinearLayoutandroid:layout_width="600dp"android:layout_height="80dp"android:layout_alignParentBottom="true"android:layout_centerHorizontal="true"android:background="@drawable/bottom_bg_corner"android:gravity="center"android:orientation="horizontal"><LinearLayoutandroid:layout_width="100dp"android:layout_height="match_parent"android:gravity="center"><TableLayoutandroid:id="@+id/selectColorBox"android:layout_width="40dp"android:layout_height="100dp"android:gravity="center"><TableRow><ImageButtonandroid:id="@+id/blackBtn"android:background="@drawable/color_select"android:src="@drawable/black" /><ImageButtonandroid:id="@+id/whiteBtn"android:background="@drawable/color_select"android:src="@drawable/white" /></TableRow><TableRow><ImageButtonandroid:id="@+id/redBtn"android:background="@drawable/color_select"android:src="@drawable/red_circle" /><ImageButtonandroid:id="@+id/blueBtn"android:background="@drawable/color_select"android:src="@drawable/blue" /></TableRow><TableRow><ImageButtonandroid:id="@+id/grayBtn"android:background="@drawable/color_select"android:src="@drawable/gray" /><ImageButtonandroid:id="@+id/cyanBtn"android:background="@drawable/color_select"android:src="@drawable/cyan" /></TableRow></TableLayout></LinearLayout><TextViewandroid:layout_width="1dp"android:layout_height="match_parent"android:background="@color/gray" /><LinearLayoutandroid:layout_width="500dp"android:layout_height="match_parent"android:gravity="center"><ImageButtonandroid:id="@+id/paint"android:layout_width="28dp"android:layout_height="28dp"android:scaleType="centerInside"android:layout_marginLeft="8dp"android:background="@drawable/shape_selector"android:src="@drawable/paint" /><ImageButtonandroid:id="@+id/fillPaint"android:layout_width="32dp"android:layout_height="32dp"android:scaleType="centerInside"android:layout_marginLeft="8dp"android:background="@drawable/shape_selector"android:src="@drawable/brush" />
<!--            <ImageButton-->
<!--                android:id="@+id/rubber"-->
<!--                android:layout_width="32dp"-->
<!--                android:layout_height="32dp"-->
<!--                android:scaleType="centerInside"-->
<!--                android:layout_marginLeft="8dp"-->
<!--                android:background="@drawable/shape_selector"-->
<!--                android:src="@drawable/rubber" />--><ImageButtonandroid:id="@+id/rect"android:layout_width="32dp"android:layout_height="32dp"android:scaleType="centerInside"android:layout_marginLeft="8dp"android:background="@drawable/shape_selector"android:focusable="true"android:src="@drawable/rect" /><ImageButtonandroid:id="@+id/circle"android:layout_width="32dp"android:layout_height="32dp"android:scaleType="centerInside"android:layout_marginLeft="8dp"android:background="@drawable/shape_selector"android:src="@drawable/circle" /><ImageButtonandroid:id="@+id/elliptic"android:layout_width="35dp"android:layout_height="35dp"android:scaleType="centerInside"android:layout_marginLeft="8dp"android:background="@drawable/shape_selector"android:src="@drawable/elliptic" /><ImageButtonandroid:id="@+id/triangle"android:layout_width="38dp"android:layout_height="40dp"android:scaleType="centerInside"android:layout_marginLeft="8dp"android:background="@drawable/shape_selector"android:src="@drawable/triangle" /><ImageButtonandroid:id="@+id/equilateralTriangle"android:layout_width="32dp"android:layout_height="32dp"android:scaleType="centerInside"android:layout_marginLeft="8dp"android:background="@drawable/shape_selector"android:src="@drawable/equilateral_triangle" /><ImageButtonandroid:id="@+id/rightAngle"android:layout_width="37dp"android:layout_height="37dp"android:scaleType="centerInside"android:layout_marginLeft="8dp"android:background="@drawable/shape_selector"android:src="@drawable/plan" /><ImageButtonandroid:id="@+id/triangleTower"android:layout_width="28dp"android:layout_height="28dp"android:scaleType="centerInside"android:layout_marginLeft="8dp"android:background="@drawable/shape_selector"android:src="@drawable/tower" /><ImageButtonandroid:id="@+id/clear"android:layout_width="32dp"android:layout_height="32dp"android:scaleType="centerInside"android:layout_marginLeft="8dp"android:background="@drawable/shape_selector"android:src="@drawable/clear" /></LinearLayout></LinearLayout></RelativeLayout>

3、自定义View代码

package com.lsl.mydrawingboarddemo.view;import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.View;import androidx.annotation.Nullable;import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;/*** author lishilin* date 2023/8/9* desc 自定义View,实现画板功能*/
public class DrawingBoardView extends SurfaceView {private Paint erasePaint, rectPaint, circlePaint;private Path erasePath, selectPath, triangleTowerPath, trianglePath, equilateralPath, anglePath;private List<Path> paintPaths, fillPaths, triangleTowerPaths, trianglePaths, equilateralPaths, anglePaths;private List<Paint> paints, fillPaints, rectPaints, circlePaints, ovalPaints, triangleTowerPaints, trianglePaints, equilateralPaints, anglePaints;private String pathType = "paintPath";private boolean eraseMode = false;private List<Rect> rects;private List<Circle> circleList;private List<RectF> ovals;private RectF rectF;private float startX, startY, currentX, currentY;private boolean isDrawing, drawPath, drawRect, drawCircle, isDrawingCircle, isDrawingOval, drawOval, drawTriangleTower, isDrawingTriangleTower, drawTriangle, isDrawingTriangle, isDrawingEquilateral, drawEquilateral, isDrawingRightAngle, drawRightAngle;private Rect rect;private Circle circle;private int pointX, pointY, radius;private float tipX, tipY, leftX, leftY, rightX, rightY, equilateralX;public DrawingBoardView(Context context) {super(context);init();}public DrawingBoardView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init();}public DrawingBoardView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}public DrawingBoardView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);}private void init() {erasePaint = new Paint();circle = new Circle();rect = new Rect();triangleTowerPaints = new ArrayList<>();triangleTowerPaths = new ArrayList<>();ovals = new ArrayList<>();trianglePaints = new ArrayList<>();trianglePaths = new ArrayList<>();rectPaints = new ArrayList<>();ovalPaints = new ArrayList<>();circlePaints = new ArrayList<>();circleList = new ArrayList<>();anglePaints = new ArrayList<>();anglePath = new Path();anglePaths = new ArrayList<>();equilateralPaths = new ArrayList<>();equilateralPaints = new ArrayList<>();circlePaint = new Paint();triangleTowerPath = new Path();circlePaint.setColor(Color.BLACK);circlePaint.setStyle(Paint.Style.STROKE);circlePaint.setStrokeWidth(5);erasePath = new Path();trianglePath = new Path();equilateralPath = new Path();triangleTowerPath = new Path();paints = new ArrayList<>();fillPaints = new ArrayList<>();fillPaths = new ArrayList<>();paintPaths = new ArrayList<>();setPaintColor(Color.BLACK);rects = new ArrayList<>();rectPaint = new Paint();rectPaint.setColor(Color.BLACK);rectPaint.setStrokeWidth(5);rectPaint.setStyle(Paint.Style.STROKE);drawPath = true;isDrawing = false;}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);Paint paint = paints.get(paints.size() - 1);if (isDrawing) {int left = (int) Math.min(startX, currentX);int right = (int) Math.max(startX, currentX);int top = (int) Math.min(startY, currentY);int bottom = (int) Math.max(startY, currentY);rect.set(left, top, right, bottom);canvas.drawRect(rect, paint);isDrawing = false;}if (!isDrawing) {for (int i = 0; i < rects.size(); i++) {canvas.drawRect(rects.get(i), rectPaints.get(i));}}for (int i = 0; i < paintPaths.size(); i++) {canvas.drawPath(paintPaths.get(i), paints.get(i));}for (int i = 0; i < trianglePaths.size(); i++) {canvas.drawPath(trianglePaths.get(i), trianglePaints.get(i));}if (isDrawingCircle) {circle.setPointX(pointX);circle.setPointY(pointY);circle.setRadius(radius);canvas.drawCircle(circle.getPointX(), circle.getPointY(), circle.getRadius(), paint);isDrawingCircle = false;}if (!isDrawingCircle) {for (int i = 0; i < circleList.size(); i++) {canvas.drawCircle(circleList.get(i).getPointX(), circleList.get(i).getPointY(), circleList.get(i).getRadius(), circlePaints.get(i));}}if (isDrawingOval) {int left = (int) Math.min(startX, currentX);int right = (int) Math.max(startX, currentX);int top = (int) Math.min(startY, currentY);int bottom = (int) Math.max(startY, currentY);rectF.set(left, top, right, bottom);canvas.drawOval(rectF, paint);isDrawingOval = false;}if (!isDrawingOval) {for (int i = 0; i < ovals.size(); i++) {canvas.drawOval(ovals.get(i), ovalPaints.get(i));}}if (isDrawingTriangleTower) {float leftX = currentX - (currentX - startX) * 2;float leftY = currentY;triangleTowerPath.lineTo(leftX, leftY);triangleTowerPath.lineTo(currentX, currentY);triangleTowerPath.close();canvas.drawPath(triangleTowerPath, paint);isDrawingTriangleTower = false;}if (!isDrawingTriangleTower){for (int i = 0; i < triangleTowerPaths.size(); i++) {canvas.drawPath(triangleTowerPaths.get(i), triangleTowerPaints.get(i));}}if (isDrawingTriangle) {trianglePath.reset();trianglePath.moveTo(tipX, tipY);trianglePath.lineTo(leftX, leftY);trianglePath.lineTo(rightX, rightY);trianglePath.close();canvas.drawPath(trianglePath, paint);isDrawingTriangle = false;}if (isDrawingEquilateral) {equilateralPath.reset();equilateralPath.moveTo(equilateralX, startY);int x = (int) ((currentY - startY) / Math.sqrt(3));int rightX = (int) (equilateralX + x);int leftX = (int) (equilateralX - x);equilateralPath.lineTo(leftX, currentY);equilateralPath.lineTo(rightX, currentY);equilateralPath.close();canvas.drawPath(equilateralPath, paint);isDrawingEquilateral = false;}if (!isDrawingEquilateral) {for (int i = 0; i < equilateralPaths.size(); i++) {canvas.drawPath(equilateralPaths.get(i), equilateralPaints.get(i));}}if (isDrawingRightAngle) {anglePath.reset();anglePath.moveTo(startX, startY);anglePath.lineTo(startX, currentY);anglePath.lineTo(currentX, currentY);anglePath.close();canvas.drawPath(anglePath, paint);isDrawingRightAngle = false;}if (!isDrawingRightAngle) {for (int i = 0; i < anglePaths.size(); i++) {canvas.drawPath(anglePaths.get(i), anglePaints.get(i));}}for (int i = 0; i < fillPaths.size(); i++) {canvas.drawPath(fillPaths.get(i), fillPaints.get(i));}}@Overridepublic boolean onTouchEvent(MotionEvent event) {float x = event.getX();float y = event.getY();Paint paint = paints.get(paints.size() - 1);switch (event.getAction()) {case MotionEvent.ACTION_DOWN:startX = event.getX();startY = event.getY();if (drawPath && selectPath != null) {selectPath.moveTo(x, y);}if (drawRect) {rect = new Rect();rectPaints.add(paint);}if (drawCircle) {circle = new Circle();circlePaints.add(paint);}if (drawOval) {rectF = new RectF();ovalPaints.add(paint);}if (drawTriangleTower) {triangleTowerPaints.add(paint);triangleTowerPath.moveTo(event.getX(), event.getY());}if (drawTriangle) {trianglePath = new Path();tipY = startY;leftX = startX;trianglePaints.add(paint);}if (drawEquilateral) {equilateralPath = new Path();equilateralPaints.add(paint);}if (drawRightAngle) {anglePath = new Path();anglePaints.add(paint);}break;case MotionEvent.ACTION_MOVE:currentX = event.getX();currentY = event.getY();if (drawPath) {selectPath.lineTo(x, y);}if (drawRect) {isDrawing = true;invalidate();}if (drawOval) {isDrawingOval = true;invalidate();}if (drawCircle) {pointX = (int) (startX + (currentX - startX) / 2);pointY = (int) (startY + (currentY - startY) / 2);radius = (int) Math.abs(currentY - startY);isDrawingCircle = true;invalidate();}if (drawTriangleTower) {isDrawingTriangleTower = true;invalidate();}if (drawTriangle) {isDrawingTriangle = true;leftY = currentY;tipX = (currentX - leftX) / 2 + leftX;rightX = currentX;rightY = currentY;invalidate();}if (drawEquilateral) {equilateralX = startX + (currentX - startX) / 2;isDrawingEquilateral = true;invalidate();}if (drawRightAngle) {isDrawingRightAngle = true;invalidate();}break;case MotionEvent.ACTION_UP:if (drawRect) {rects.add(rect);isDrawing = false;}if (drawCircle) {circleList.add(circle);isDrawingCircle = false;}if (drawOval) {ovals.add(rectF);isDrawingOval = false;}if (drawTriangleTower) {triangleTowerPaths.add(triangleTowerPath);isDrawingTriangleTower = false;}if (drawTriangle) {trianglePaths.add(trianglePath);isDrawingTriangle = false;}if (drawEquilateral) {equilateralPaths.add(equilateralPath);isDrawingEquilateral = false;}if (drawRightAngle) {anglePaths.add(anglePath);isDrawingRightAngle = false;}break;}invalidate();return true;}public void clearBoard() {for (int i = 0; i < paintPaths.size(); i++) {paintPaths.get(i).reset();}for (int i = 0; i < fillPaths.size(); i++) {fillPaths.get(i).reset();}for (int i = 0; i < triangleTowerPaths.size(); i++) {triangleTowerPaths.get(i).reset();}for (int i = 0; i < trianglePaths.size(); i++) {trianglePaths.get(i).reset();}for (int i = 0; i < equilateralPaths.size(); i++) {equilateralPaths.get(i).reset();}for (Path path : anglePaths) {path.reset();}rects.clear();ovals.clear();circleList.clear();erasePath.reset();eraseMode = false;invalidate();}public void setPaint(String type) {switch (type) {case "paint":selectPath = paintPaths.get(paintPaths.size() - 1);this.pathType = "paintPath";drawPath = true;drawRect = false;drawCircle = false;drawOval = false;drawTriangle = false;drawTriangleTower = false;drawEquilateral = false;drawRightAngle = false;break;case "fillPaint":selectPath = fillPaths.get(fillPaths.size() - 1);this.pathType = "fillPath";drawPath = true;drawRect = false;drawCircle = false;drawOval = false;drawTriangle = false;drawTriangleTower = false;drawEquilateral = false;drawRightAngle = false;break;case "rect":drawPath = false;drawRect = true;drawCircle = false;drawOval = false;drawTriangle = false;drawTriangleTower = false;drawRightAngle = false;drawEquilateral = false;break;case "drawCircle":drawRect = false;drawPath = false;drawOval = false;drawCircle = true;drawTriangle = false;drawTriangleTower = false;drawEquilateral = false;drawRightAngle = false;break;case "drawOval":drawPath = false;drawRect = false;drawCircle = false;drawOval = true;drawTriangle = false;drawTriangleTower = false;drawEquilateral = false;drawRightAngle = false;break;case "drawTriangleTower":drawPath = false;drawRect = false;drawCircle = false;drawOval = false;drawTriangle = false;drawTriangleTower = true;drawEquilateral = false;drawRightAngle = false;break;case "drawTriangle":drawPath = false;drawRect = false;drawCircle = false;drawOval = false;drawTriangleTower = false;drawTriangle = true;drawRightAngle = false;drawEquilateral = false;break;case "drawEquilateral":drawPath = false;drawRect = false;drawCircle = false;drawOval = false;drawTriangleTower = false;drawTriangle = false;drawRightAngle = false;drawEquilateral = true;break;case "drawRightAngle":drawPath = false;drawRect = false;drawCircle = false;drawOval = false;drawTriangleTower = false;drawTriangle = false;drawEquilateral = false;drawRightAngle = true;break;}}public void setPaintColor(int color) {Paint paint = new Paint();paint.setColor(color);paint.setAntiAlias(true);paint.setStyle(Paint.Style.STROKE);paint.setStrokeCap(Paint.Cap.ROUND);paint.setStrokeJoin(Paint.Join.ROUND);paint.setStrokeWidth(3);paints.add(paint);Paint fillPaint = new Paint();fillPaint.setColor(color);fillPaint.setStyle(Paint.Style.STROKE);fillPaint.setStrokeWidth(20);fillPaint.setAntiAlias(true);fillPaint.setDither(true);fillPaint.setAlpha(250);fillPaint.setStrokeJoin(Paint.Join.ROUND);fillPaint.setStrokeCap(Paint.Cap.ROUND);fillPaints.add(fillPaint);Path path = new Path();Path fillPath = new Path();Path triangleTowerPath = new Path();this.triangleTowerPath = triangleTowerPath;this.triangleTowerPaints.add(paint);triangleTowerPaths.add(triangleTowerPath);Path trianglePath = new Path();trianglePaths.add(trianglePath);trianglePaints.add(paint);fillPaths.add(fillPath);paintPaths.add(path);Path anglePath = new Path();anglePaths.add(anglePath);this.anglePath = anglePath;this.anglePaints.add(paint);Path equilateralPath = new Path();this.equilateralPath = equilateralPath;equilateralPaths.add(equilateralPath);equilateralPaints.add(paint);if (pathType.equals("paintPath")) {selectPath = path;} else {selectPath = fillPath;}}public void setEraseMode() {eraseMode = true;drawPath = true;drawRect = false;drawTriangleTower = false;drawTriangle = false;drawCircle = false;drawEquilateral = false;drawRightAngle = false;erasePath = new Path();erasePaint.setAntiAlias(true);erasePaint.setDither(true);erasePaint.setStrokeWidth(30);erasePaint.setStyle(Paint.Style.STROKE);erasePaint.setStrokeJoin(Paint.Join.ROUND);erasePaint.setStrokeCap(Paint.Cap.SQUARE);erasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));selectPath = erasePath;}private class Circle {int pointX;int pointY;int radius;public int getPointX() {return pointX;}public void setPointX(int pointX) {this.pointX = pointX;}public int getPointY() {return pointY;}public void setPointY(int pointY) {this.pointY = pointY;}public int getRadius() {return radius;}public void setRadius(int radius) {this.radius = radius;}}
}

三、总结

绘制主要用到Paint和Path,大体思路都是监听OnTouchEvent事件,绘制路径或者其他图案,开发这个项目用时一周。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/106342.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

73 # 发布自己的 http-server 到 npm

1、添加 .npmignore 文件&#xff0c;忽略不需要的文件 public2、去官网https://www.npmjs.com/检查自己的包名是否被占用 3、切换到官方源&#xff0c;然后检查确认 nrm use npm nrm ls4、登录 npm 账号 npm login5、发布 npm publish6、查看发布情况&#xff0c;发布成功…

npm 卸载 vuecli后还是存在

运行了npm uninstall vue-cli -g&#xff0c;之后是up to date in&#xff0c;然后vue -V&#xff0c;版本号一直都在&#xff0c;说明没有卸载掉 1、执行全局卸载命令 npm uninstall vue-cli -g 2、删除vue原始文件 查看文件位置&#xff0c;找到文件删掉 where vue 3、再…

山西电力市场日前价格预测【2023-08-27】

日前价格预测 预测明日&#xff08;2023-08-27&#xff09;山西电力市场全天平均日前电价为318.11元/MWh。其中&#xff0c;最高日前电价为356.66元/MWh&#xff0c;预计出现在19: 15。最低日前电价为273.48元/MWh&#xff0c;预计出现在04: 30。 价差方向预测 1&#xff1a; 实…

Jenkins的定时任务配置

jenkins配置定时任务位置(点击日程表的问好可查看语法配置) jenkins的定时任务的参数 # 定时任务参数(每个参数之间使用tab键或空格分隔)MINUTE HOUR DOM MONTH DOW 参数解释取值范围 MINUTE 分钟0-59HOUR小时0-23DOM一月的天数1-31MONTH月份1-12DOW 一周的天数0…

实验七 Linux 内核移植

【实验目的】 掌握 Linux 内核配置和编译的基本方法 【实验环境】 ubuntu 14.04 发行版FS4412 实验平台交叉编译工具&#xff1a;arm-none-linux-gnueabi- 【注意事项】 实验步骤中以“$”开头的命令表示在 ubuntu 环境下执行&#xff0c;以“#”开头的命令表 示在开发板下…

PHP 安装Composer,vue前端依赖包

电脑安装Composer 官网下载&#xff1a;https://getcomposer.org/Composer-Setup.exe 后端安装&#xff1a; 检查是否安装依赖&#xff1a; 安装Composer install 或 Composer i 前端安装&#xff1a; yarn install 安装依赖

vr内容编辑软件降低了虚拟现实项目开发门槛

VR虚拟场景编辑器是一种专门用于创建、修改和设计虚拟场景的工具。它利用vr虚拟现实技术&#xff0c;让用户可以在三维空间中直接对场景进行操作和编辑。这种编辑器的出现&#xff0c;使得用户可以更加直观、自由地进行场景设计和制作&#xff0c;为诸多领域带来了新的可能性。…

Linux(基础篇一)

Linux基础篇 Linux基础篇一1. Linux文件系统与目录结构1.1 Linux文件系统1.2 Linux目录结构 2. VI/VIM编辑器2.1 vi/vim是什么2.2 模式间的转换2.3 一般模式2.4 插入模式2.4.1 进入编辑模式2.4.2 退出编辑模式 2.5 命令模式 3. 网络配置3.1 网络连接模式3.2 修改静态ip3.3 配置…

AI夏令营第三期用户新增挑战赛学习笔记

1、数据可视化 1.数据探索和理解&#xff1a;数据可视化可以帮助我们更好地理解数据集的特征、分布和关系。通过可视化数据&#xff0c;我们可以发现数据中的模式、异常值、缺失值等信息&#xff0c;从而更好地了解数据的特点和结构。2.特征工程&#xff1a;数据可视化可以帮助…

WPF基础入门-Class5-WPF命令

WPF基础入门 Class5-WPF命令 1、xaml编写一个button&#xff0c;Command绑定一个命令 <Grid><ButtonWidth"100"Height"40" Command"{Binding ShowCommand}"></Button> </Grid>2、编写一个model.cs namespace WPF_Le…

033 - date 和 time

date类型&#xff1a; 该DATE类型用于具有日期部分但没有时间部分的值。MySQL检索并DATE以 格式显示 值 。支持的范围是 到。 YYYY-MM-DD1000-01-019999-12-31 -- 创建表&#xff0c;字段类型是date&#xff1a; create table test_date01 (a date); -- 正确格式插入数据 in…

OceanMind海睿思签约常州市建筑科学研究院,打造检验检测行业数字化转型标杆

近日&#xff0c;中新赛克海睿思 与 中国知名综合性建筑研究和科技创新型高科技企业——常州市建筑科学研究院集团股份有限公司&#xff08;以下简称“建科股份”&#xff09;达成深度战略合作&#xff0c;为建科股份提供行业领先的数据工程建设服务&#xff0c;携手推进检验检…

java八股文面试[JVM]——双亲委派模型

1.当AppClassLoader去加载一个class时&#xff0c;它首先不会自己去尝试加载这个类&#xff0c;而是把类加载请求委托给父加载器ExtClassLoader去完成。 2.当ExtClassLoader去加载一个class时&#xff0c;它首先也不会去尝试加载这个类&#xff0c;而是把类加载请求委托给父加载…

python面试题--1

目录 python面试题 1&#xff09;什么是Python&#xff1f;使用Python有什么好处&#xff1f; 2&#xff09;什么是PEP 8&#xff1f; 3&#xff09;什么是序列化和非序列化&#xff1f; 4&#xff09;如何解释Python&#xff1f; 5&#xff09;如何在Python中内存管理&a…

2023年国赛 高教社杯数学建模思路 - 案例:退火算法

文章目录 1 退火算法原理1.1 物理背景1.2 背后的数学模型 2 退火算法实现2.1 算法流程2.2算法实现 建模资料 ## 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 退火算法原理 1.1 物理背景 在热力学上&a…

微软宣布在 Excel 中使用 Python:结合了 Python 的强大功能和 Excel 的灵活性。

文章目录 Excel 中的 Python 有何独特之处&#xff1f;1. Excel 中的 Python 是为分析师构建的。高级可视化机器学习、预测分析和预测数据清理 2. Excel 中的 Python 通过 Anaconda 展示了最好的 Python 分析功能。3. Excel 中的 Python 在 Microsoft 云上安全运行&#xff0c;…

C++ 异常

一、异常概念 异常是一种处理错误的方式&#xff0c;当一个函数发现自己无法处理的错误时就可以抛出异常&#xff0c;让函数的直接或间接 的调用者处理这个错误。 throw: 当问题出现时&#xff0c;程序会抛出一个异常。这是通过使用 throw 关键字来完成的。 catch: 在您想要…

XSS盲打练习(简单认识反射型、存储型XSS和cookie欺骗)

文章目录 挖掘cms网站XSS漏洞利用XSS平台盲打CMS&#xff0c;获取后台管理cookiecookie欺骗登录管理员账户 挖掘cms网站XSS漏洞 来到cms网站主页&#xff0c;发现有一个搜索框&#xff0c;输入任意内容后搜索&#xff0c;发现内容会回显&#xff0c;这里可能存在反射型XSS漏洞…

华为星闪,一项将 “ 更稳 WiFi ” 和 “ 更好蓝牙 ” 融合起来的通信标准

兼顾多用途和专业化的 AI 大模型、移除安卓代码的 HarmonyOS NEXT 、给折叠屏应用提供适配方向的《 折叠屏/平板应用体验评估标准 》。。。 不过除了这些比较贴近我们普通用户&#xff0c;容易讲清楚的东西&#xff0c;华为还官宣了一个大家可能没注意的黑科技&#xff1a; 星…

ModaHub魔搭社区:WinPlan经营大脑数据采集

目录 WinPlan经营大脑数据采集介绍 WinPlan经营大脑数据采集模版 WinPlan经营大脑数据采集介绍 基于指标、维度来创建业务表单,通过业务表单的形式来采集实际数据,最终生成企业统一的经营数据库。由于需要客户创建数据采集模版(业务流程),然后可以基于各个业务模版作为…