使用 Android Studio 举行画布等功能的操纵,Java语言,对小白友爱 ...

宁睿  论坛元老 | 2025-4-12 19:46:00 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 1947|帖子 1947|积分 5851

##实现功能

用Android studio 举行编译一个步伐。步伐要求:
1、点击空白区域能产生随机图形(简朴图形有:三角形,圆形,矩形:复杂图形:凹字形,凸字形,拱门形,心形)
2、点击图形可以弹出是否删除的会话框
3、长按图形可以对图形举行推拽
4、附加功能:使点击图形的时候可以或许被识别:添加流动的虚线框
##重要的功能弄块

1、对Paint类、Path类、Canvas类的使用,图形绘画和虚线边框绘画部分
2、对点击变乱识别onTouchEvent触摸变乱(ACTION_DOWN、ACTION_MOVE、ACTION_UP)
##功能进程分析

        首先是对鼠标点击变乱举行过程分析,统共分三个进程,分别为鼠标点击时(ACTION_DOWN),鼠标移动时(ACTION_MOVE),鼠标抬起时(ACTION_UP)。
1、当为鼠标点击时(ACTION_DOWN):首先要识别空白画面是否有图形,如果没有则天生图形,如果存在图形就举行手势的点击检测,以便举行下一步的判断。
2、对有图形的手势的点击检测:如果点击时间小于500毫秒则为点击变乱,对接鼠标抬起时(ACTION_UP)变乱,弹出是否删除的会话框。
3、对有图形的手势的点击检测:如果点击时间长于500毫秒则为长按变乱,对接鼠标移动时(ACTION_MOVE)变乱,可以举行移动图形
注意:流动边框的添加应陪同于鼠标点击的过程。

#这是我写的类的分类,以下会徐徐解释每个部分
   
     这是我的类文件分类     
1、CustomCanvasView类的构建意义和相关代码

构建意义:举行相关属性的定义,如Paint、Path、Canva、Random、ValueAnimator(动画相关类)、坐标等。
举行对全局属性的定义于设置,如对图形的填充方式(FILL),对图形边框的填充方式(STORK
)以及绘画边框的颜色和流动情况
CustomCanvasView:
  1. import android.animation.ValueAnimator;
  2. import android.content.Context;
  3. import android.graphics.*;
  4. import android.util.AttributeSet;
  5. import android.view.MotionEvent;
  6. import android.view.View;
  7. import androidx.appcompat.app.AlertDialog;
  8. import java.util.ArrayList;
  9. import java.util.Random;
  10. // 自定义绘图视图,继承自Android的View类
  11. public class CustomCanvasView extends View {
  12.     private ArrayList<Shape> shapes = new ArrayList<>();    // 存储所有图形的列表
  13.     private Paint paint, borderPaint;    // 主画笔(填充图形)和边框画笔(绘制选中效果)
  14.     private Random random = new Random();  // 随机数生成器,用于创建随机颜色和图形类型
  15.     private Shape selectedShape;// 当前选中的图形对象
  16.     private float lastX, lastY;    // 记录上次触摸点的坐标
  17.     private float phase;   // 虚线动画的相位(控制虚线流动)
  18.     private ValueAnimator phaseAnimator;   // 相位动画控制器
  19.     // 构造函数,初始化视图
  20.     public CustomCanvasView(Context context, AttributeSet attrs) {
  21.         super(context, attrs);
  22.         init(); // 调用初始化方法
  23.     }
  24.     // 初始化方法:设置画笔和动画
  25.     private void init() {
  26.         // 初始化主画笔(填充图形用)
  27.         paint = new Paint();
  28.         paint.setAntiAlias(true);    // 开启抗锯齿
  29.         paint.setStyle(Paint.Style.FILL); // 填充模式
  30.         // 初始化边框画笔(选中效果用)
  31.         borderPaint = new Paint();
  32.         borderPaint.setAntiAlias(true);
  33.         borderPaint.setStyle(Paint.Style.STROKE); // 描边模式
  34.         borderPaint.setStrokeWidth(8);           // 边框粗细
  35.         borderPaint.setColor(Color.BLUE);        // 边框颜色
  36.         // 创建虚线动画(让边框虚线流动起来)
  37.         phaseAnimator = ValueAnimator.ofFloat(0, 100); // 动画数值范围 // 创建浮点数值动画器,数值范围从0到100(用于驱动相位变化)
  38.         phaseAnimator.setDuration(1500);       // 设置动画持续时间为1500毫秒(1.5秒)
  39.         phaseAnimator.setRepeatCount(ValueAnimator.INFINITE); // 设置无限循环模式(动画会持续重复播放)
  40.         phaseAnimator.addUpdateListener(animation -> {  // 添加动画更新监听器(每帧更新时触发)
  41.             phase = (float) animation.getAnimatedValue(); // 获取当前动画值并更新相位变量(强制转换为float类型)
  42.             invalidate(); // 请求重绘视图(自动调用View的onDraw方法)
  43.         });
  44.         phaseAnimator.start(); // 启动动画(开始执行数值变化)
  45.     }
  46.     // 绘制方法(Android系统自动调用)
  47.     @Override
  48.     protected void onDraw(Canvas canvas) {               // 绘制方法(Android系统自动调用)
  49.         super.onDraw(canvas);
  50.         // 增强型for循环:遍历shapes集合中的所有元素
  51.         // Shape shape : shapes → 每次循环将集合元素赋值给shape变量
  52.         for (Shape shape : shapes) {
  53.             // 调用当前图形的draw方法,完成基础绘制
  54.             // paint: 图形填充画笔,canvas: 绘制画布
  55.             shape.draw(paint, canvas);
  56.             // 检查当前图形是否被选中
  57.             // shape.isSelected → 访问图形的选中状态属性(假设为布尔类型)
  58.             if (shape.isSelected) {
  59.                 // 如果被选中,调用drawBorder方法绘制特殊边框
  60.                 // borderPaint: 边框专用画笔(通常设置虚线样式)
  61.                 // phase: 虚线动画相位值(控制虚线流动效果)
  62.                 shape.drawBorder(borderPaint, canvas, phase);
  63.             }
  64.         }
  65.     }
  66.     // 处理触摸事件的方法
  67.     @Override
  68.     public boolean onTouchEvent(MotionEvent event) {
  69.         float x = event.getX();// 获取触摸点坐标
  70.         float y = event.getY();
  71.         switch (event.getAction()) {
  72.             case MotionEvent.ACTION_DOWN:// 手指按下
  73.                 handleTouchDown(x, y);
  74.                 return true;
  75.             case MotionEvent.ACTION_MOVE:// 手指移动
  76.                 handleTouchMove(x, y);
  77.                 return true;
  78.             case MotionEvent.ACTION_UP:// 手指抬起
  79.                 handleTouchUp();
  80.                 return true;
  81.         }
  82.         return super.onTouchEvent(event);
  83.     }
  84.     // 处理按下事件
  85.     private void handleTouchDown(float x, float y) {
  86.         selectedShape = findShape(x, y); // 1. 查找点击位置是否有图形 findShape(x, y)
  87.         lastX = x;
  88.         lastY = y;    // 2. 记录当前触摸点坐标
  89. // 遍历图形集合中的所有图形对象
  90.         for (Shape shape : shapes) {
  91.             // 设置当前图形的选中状态:只有当该图形是当前选中对象时才标记为选中
  92.             // (通过对象地址比较实现精准匹配)
  93.             shape.setSelected(shape == selectedShape);
  94.         }
  95. // 判断当前是否存在已选中的图形
  96.         if (selectedShape == null) {
  97.             // 当没有选中任何图形时:
  98.             // 根据点击坐标(x,y)和随机数生成器,通过工厂模式创建随机类型的新图形
  99.             // 并将新图形添加到图形集合中(例如圆形/矩形等)
  100.             shapes.add(ShapeFactory.createRandomShape(x, y, random));
  101.         } else {
  102.             // 当存在选中图形时:
  103.             // 启动长按手势检测(通常通过Handler.postDelayed实现计时逻辑)
  104.             // 0.5秒后若仍未松开手指,则触发拖拽操作(防止误触)
  105.             startDragDetection();
  106.         }
  107.         invalidate();// 刷新界面
  108.     }
  109.     // 处理移动事件
  110. // 处理触摸移动事件的方法,参数x/y表示当前触摸点坐标
  111.     private void handleTouchMove(float x, float y) {
  112.         // 检查当前是否存在被选中的图形且处于拖动状态
  113.         if (selectedShape != null && selectedShape.isDragging()) {
  114.             // 计算x轴方向移动距离$dx = x - \text{lastX}$
  115.             float dx = x - lastX;
  116.             // 计算y轴方向移动距离$dy = y - \text{lastY}$
  117.             float dy = y - lastY;
  118.             // 调用图形对象的移动方法,传入$x$/$y$轴偏移量
  119.             selectedShape.move(dx, dy);
  120.             // 更新记录的上次触摸点x坐标(用于下次计算偏移量)
  121.             lastX = x;
  122.             // 更新记录的上次触摸点y坐标(用于下次计算偏移量)
  123.             lastY = y;
  124.             // 请求重绘视图(触发onDraw()更新图形位置)
  125.             invalidate();
  126.         }
  127.     }
  128.     // 处理抬起事件
  129.     private void handleTouchUp() {
  130.         // 如果不是拖动状态,显示删除对话框
  131.         if (selectedShape != null && !selectedShape.isDragging()) {
  132.             showDeleteDialog();
  133.         }
  134.         if (selectedShape != null) {// 结束拖动状态
  135.             selectedShape.setDragging(false);
  136.         }
  137.     }
  138.     // 启动长按检测(延迟500毫秒)
  139.     private void startDragDetection() {
  140.         postDelayed(() -> {
  141.             if (selectedShape != null) {
  142.                 selectedShape.setDragging(true);
  143.             }
  144.         }, 500);
  145.     }
  146.     // 显示删除确认对话框
  147.     private void showDeleteDialog() {
  148.         new AlertDialog.Builder(getContext())
  149.                 .setTitle("删除图形")
  150.                 .setMessage("确定要删除此图形吗?")
  151.                 .setPositiveButton("是", (dialog, which) -> {
  152.                     shapes.remove(selectedShape);
  153.                     invalidate();
  154.                 })
  155.                 .setNegativeButton("否", null)
  156.                 .show();
  157.     }
  158.     // 查找包含指定坐标的图形(从后往前找,实现上层优先)
  159.     // 定义私有方法,用于根据坐标查找符合条件的图形
  160.     private Shape findShape(float x, float y) {
  161.         // 逆向遍历图形列表(从最后一个元素开始)
  162.         // 目的:实现"最后添加的图形优先检测"的层叠逻辑
  163.         for (int i = shapes.size()-1; i >= 0; i--) {
  164.             // 获取当前索引对应的图形对象
  165.             Shape shape = shapes.get(i);
  166.             // 检测坐标(x,y)是否在当前图形范围内
  167.             if (shape.contains(x, y)) {
  168.                 // 找到符合条件的图形,立即返回
  169.                 return shape;
  170.             }
  171.         }
  172.         // 遍历完所有图形仍未找到,返回null表示未命中
  173.         return null;
  174.     }
复制代码
2、Shape基类的构建意义和代码

构建意义:举行所有图形产生的共有功能的抽象如:主题绘画方法、边框绘画方法、界限检测方法、移动方法、被选中和拖拽状态
Shape:
  1. abstract  class Shape {
  2.     protected float x, y;// 图形中心坐标
  3.     protected int color; // 图形颜色
  4.     private boolean isDragging = false;// 是否正在拖动
  5.     public boolean isSelected = false;// 是否被选中
  6.     // 构造函数
  7.     public Shape(float x, float y, int color) {
  8.         this.x = x;
  9.         this.y = y;
  10.         this.color = color;
  11.     }
  12.     // 抽象方法:由子类实现具体绘制逻辑
  13.     public abstract void draw(Paint paint, Canvas canvas);
  14.     // 抽象方法:绘制选中边框
  15.     public abstract void drawBorder(Paint borderPaint, Canvas canvas, float phase);
  16.     // 抽象方法:检测坐标是否在图形内
  17.     public abstract boolean contains(float touchX, float touchY);
  18.     // 公共方法 // 移动图形的方法
  19.     public void move(float dx, float dy) {
  20.         x += dx;
  21.         y += dy;
  22.     }
  23.     // Getter和Setter方法
  24.     // 设置当前对象是否被选中的状态
  25.     public void setSelected(boolean selected) {
  26.         isSelected = selected;  // 将参数值直接赋值给成员变量isSelected
  27.     }
  28.     // 设置当前对象是否处于拖拽状态
  29.     public void setDragging(boolean dragging) {
  30.         isDragging = dragging;  // 将参数值直接赋值给成员变量isDragging
  31.     }
  32. // (被注释的获取选中状态方法)
  33. // public boolean isSelected() {
  34. //     return isSelected;  // 原设计用于返回当前选中状态
  35. // }
  36.     // 获取当前拖拽状态
  37.     public boolean isDragging() {
  38.         return isDragging;  // 直接返回成员变量isDragging的值
  39.     }
  40.     // 通用点击检测方法(使用Android的Region类)
  41.     // 检查指定坐标(x,y)是否在Path路径构成的区域内
  42.     protected boolean checkInRegion(Path path, float x, float y) {
  43.         // 阶段1:计算路径的包围矩形
  44.         RectF bounds = new RectF();          // 创建存储边界信息的矩形对象
  45.         path.computeBounds(bounds, true);    // 计算路径的最小包围矩形,true表示精确计算
  46.         // 阶段2:创建路径对应的区域对象
  47.         Region region = new Region();        // 创建空区域对象
  48.         region.setPath(path, new Region(     // 将Path转换为Region,需要指定剪裁范围
  49.                 (int) bounds.left,           // 左边界取整
  50.                 (int) bounds.top,            // 上边界取整
  51.                 (int) bounds.right,          // 右边界取整
  52.                 (int) bounds.bottom          // 下边界取整
  53.         ));                                  // 这里创建了一个与路径边界匹配的剪裁区域
  54.         // 阶段3:坐标检测
  55.         return region.contains((int)x, (int)y); // 将坐标转为整型后检测是否在区域内
  56.     }
  57. }
复制代码
3.ShapeFactory类的构建意义和代码

构建意义:由于是点击空白处举行随机图形的天生,所以使用随机数举行实现
ShapeFactory:
  1.     class ShapeFactory {
  2.     static Shape createRandomShape(float x, float y, Random random) {
  3.         // 生成随机类型(1-7)
  4.         int type = random.nextInt(7) + 1;
  5.         // 生成随机颜色(RGB各分量0-255)
  6.         int color = Color.rgb(
  7.                 random.nextInt(256),
  8.                 random.nextInt(256),
  9.                 random.nextInt(256)
  10.         );
  11.         // 根据类型创建具体图形
  12.         switch (type) {
  13.             case 1: return new Shape1_Triangle(x, y, color);   // 三角形
  14.             case 2: return new Shape2_Circle(x, y, color);    // 圆形
  15.             case 3: return new Shape3_Rectangle(x, y, color);  // 矩形
  16.             case 4: return new Shape4_ConcaveShape(x, y, color);  // 凹形
  17.             case 5: return new Shape5_ConvexShape(x, y, color);   // 凸形
  18.             case 6: return new s6_ArchShape(x, y, color);     // 拱门形
  19.             case 7: return new Shape7_HeartShape(x, y, color);    // 心形
  20.             default: return new Shape2_Circle(x, y, color);      // 默认返回圆形
  21.         }
  22.     }
  23. }
复制代码
4、各个图形类应包罗图形绘制、流动边框绘制和界限检测

Shape1_Triangle:
  1. class Shape1_Triangle extends Shape {
  2.     public Shape1_Triangle(float x, float y, int color) {
  3.         super(x, y, color);
  4.     }
  5.     @Override
  6.     public void draw(Paint paint, Canvas canvas) {
  7.         paint.setColor(color); // 设置颜色
  8.         Path path = createPath(); // 创建路径
  9.         canvas.drawPath(path, paint); // 绘制路径
  10.     }
  11.     @Override
  12.     public void drawBorder(Paint borderPaint, Canvas canvas, float phase) {
  13.         //设置虚线路径效果,实线20像素+虚线20像素,phase控制起始偏移量(用于动画效果)
  14.         borderPaint.setPathEffect(new DashPathEffect(new float[]{20, 20}, phase));
  15.         canvas.drawPath(createPath(), borderPaint); // 绘制相同路径作为边框
  16.     }
  17.     @Override
  18.     public boolean contains(float x, float y) {
  19.         return checkInRegion(createPath(), x, y);// 使用基类检测方法
  20.     }
  21.     private Path createPath() {
  22.         Path path = new Path();
  23.         path.moveTo(this.x, this.y);
  24.         path.lineTo(x + 100, y + 173);
  25.         path.lineTo(x - 100, y + 173);
  26.         path.close();
  27.         return path;
  28.     }
  29. }
复制代码
Shape2_Circle
  1. class Shape2_Circle extends Shape {
  2.     public Shape2_Circle(float x, float y, int color) {
  3.         super(x, y, color);
  4.     }
  5.     @Override
  6.     public void draw(Paint paint, Canvas canvas) {
  7.         paint.setColor(color);
  8.         canvas.drawCircle(x, y, 50, paint);
  9.     }
  10.     @Override
  11.     public void drawBorder(Paint borderPaint, Canvas canvas, float phase) {
  12.         borderPaint.setPathEffect(new DashPathEffect(new float[]{20, 20}, phase));
  13.         canvas.drawCircle(x, y, 55, borderPaint);
  14.     }
  15.     @Override
  16.     public boolean contains(float x, float y) {
  17.         return Math.hypot(x - this.x, y - this.y) <= 50;
  18.     }
  19. }
复制代码
3、Shape3_Rectangle
  1. class Shape3_Rectangle extends Shape {
  2.     public Shape3_Rectangle(float x, float y, int color) {
  3.         super(x, y, color);
  4.     }
  5.     @Override
  6.     public void draw(Paint paint, Canvas canvas) {
  7.         paint.setColor(color);
  8.         canvas.drawRect(x-50, y-50, x+50, y+50, paint);
  9.     }
  10.     @Override
  11.     public void drawBorder(Paint borderPaint, Canvas canvas, float phase) {
  12.         borderPaint.setPathEffect(new DashPathEffect(new float[]{20, 20}, phase));
  13.         canvas.drawRect(x-55, y-55, x+55, y+55, borderPaint);
  14.     }
  15.     @Override
  16.     public boolean contains(float x, float y) {
  17.         return x >= this.x-50 && x <= this.x+50 &&
  18.                 y >= this.y-50 && y <= this.y+50;
  19.     }
  20. }
复制代码
4、Shape4_ConcaveShape(凹形)
  1. class Shape4_ConcaveShape extends Shape {
  2.     public Shape4_ConcaveShape(float x, float y, int color) {
  3.         super(x, y, color);
  4.     }
  5.     @Override
  6.     public void draw(Paint paint, Canvas canvas) {
  7.         paint.setColor(color);
  8.         canvas.drawPath(createMainPath(), paint);
  9.     }
  10.     @Override
  11.     public void drawBorder(Paint borderPaint, Canvas canvas, float phase) {
  12.         borderPaint.setPathEffect(new DashPathEffect(new float[]{20, 20}, phase));
  13.         canvas.drawPath(createBorderPath(), borderPaint);
  14.     }
  15.     @Override
  16.     public boolean contains(float x, float y) {
  17.         return checkInRegion(createMainPath(), x, y);
  18.     }
  19.     private Path createMainPath() {
  20.         Path path = new Path();
  21.         path.moveTo(this.x - 50, this.y - 50);
  22.         path.lineTo(x + 50, y - 50);
  23.         path.lineTo(x + 50, y + 30);
  24.         path.arcTo(new RectF(x - 40, y + 30, x + 40, y + 70), 0, 180, false);
  25.         path.lineTo(x - 50, y + 30);
  26.         path.close();
  27.         return path;
  28.     }
  29.     private Path createBorderPath() {
  30.         Path path = new Path();
  31.         path.moveTo(x - 55, y - 55);
  32.         path.lineTo(x + 55, y - 55);
  33.         path.lineTo(x + 55, y + 35);
  34.         path.arcTo(new RectF(x - 45, y + 35, x + 45, y + 75), 0, 180, false);
  35.         path.lineTo(x - 55, y + 35);
  36.         path.close();
  37.         return path;
  38.     }
  39. }
复制代码
剩下图形以及添加其他图形,各人可以按需添加。
注意:对于图形的绘制如 (x+100,y+100),这里的盘算方向于坐标轴相反的,(x,y)都加100像素其位置在(x,y)的左下角!!
5、完备步伐演示

1、点击产生的图形


2、点击图形弹出是否删除的会话框,可以看到所点击的图形有虚线框举行标识


3、移动图形,所选图形向上移动,(静态截图显示不了鼠标)

           

6、如有必要完备代码请私信,谢谢各人的浏览,如有发起接待私信^_^

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

宁睿

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表