如何用多个手指在画布上绘图

2024-07-04

我正在使用安卓Canvas创建绘图应用程序的类。这是我第一次尝试使用 Canvas 类。到目前为止,我使用的代码工作正常,绘图工作正常。但我在这段代码中意识到,它允许用户仅用一根手指进行绘画,我的意思是说,如果用户使用多于一根手指在画布上绘画,则不允许用户用多根手指进行绘画。我浏览了有关多个触摸事件的文档,但未能在我的代码中实现它。那么有人可以帮我解决这个问题吗?

我用于在画布上绘图的代码:

public class DrawView extends View implements OnTouchListener 
{
    private Canvas      m_Canvas;
    
    private Path        m_Path;
    
    private Paint       m_Paint;
    
    ArrayList<Pair<Path, Paint>> arrayListPaths = new ArrayList<Pair<Path, Paint>>();
    
    ArrayList<Pair<Path, Paint>> undonePaths = new ArrayList<Pair<Path, Paint>>(); 
    
    private float mX, mY;
    
    private Bitmap bitmapToCanvas;
    
    private static final float TOUCH_TOLERANCE = 4;
        
    public DrawView(Context context) 
    {
        super(context);
        setFocusable(true);
        setFocusableInTouchMode(true);      
        this.setOnTouchListener(this);
        
        onCanvasInitialization();
    }      
    
    public void onCanvasInitialization()
    {
        m_Paint = new Paint();
        m_Paint.setAntiAlias(true);
        m_Paint.setDither(true);
        m_Paint.setColor(Color.parseColor("#37A1D1"));
        m_Paint.setStyle(Paint.Style.STROKE);
        m_Paint.setStrokeJoin(Paint.Join.ROUND);
        m_Paint.setStrokeCap(Paint.Cap.ROUND);
        m_Paint.setStrokeWidth(2);      
                
        m_Path = new Path();    
    }
    
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) 
    {
        super.onSizeChanged(w, h, oldw, oldh);
        
        bitmapToCanvas = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        m_Canvas = new Canvas(bitmapToCanvas);
    }
    
    @Override
    protected void onDraw(Canvas canvas)
    {    
        canvas.drawBitmap(bitmapToCanvas, 0f, 0f, null);
        canvas.drawPath(m_Path, m_Paint);
    }
    
    public boolean onTouch(View arg0, MotionEvent event) 
    {
        float x = event.getX();
        float y = event.getY();

        switch (event.getAction()) 
        {
            case MotionEvent.ACTION_DOWN:
            touch_start(x, y);
            invalidate();
            break;
            case MotionEvent.ACTION_MOVE:
            {
                touch_move(x, y);
                invalidate();
                break;
            }
            case MotionEvent.ACTION_UP:
            touch_up();
            invalidate();
            break;
        }
        return true;
    }

    private void touch_start(float x, float y) 
    {
        undonePaths.clear();
        m_Path.reset();
        m_Path.moveTo(x, y);
        mX = x;
        mY = y;
    }
    
    private void touch_move(float x, float y) 
    {
        float dx = Math.abs(x - mX);
        float dy = Math.abs(y - mY);
        if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) 
        {
            m_Path.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
            mX = x;
            mY = y;
        }
    }
    private void touch_up() 
    {
        m_Path.lineTo(mX, mY);
                
        // commit the path to our offscreen
        m_Canvas.drawPath(m_Path, m_Paint);
        
        // kill this so we don't double draw                    
        Paint newPaint = new Paint(m_Paint); // Clones the mPaint object
        arrayListPaths.add(new Pair<Path, Paint>(m_Path, newPaint));
        m_Path = new Path();
    }
}

我尝试更改代码以支持多点触摸,但它无法正常工作。这是我更改的代码。 http://pastebin.com/W6qvpYGW


由于工作代码没有答案,我可以分享一个工作示例。关键是要有一个当前活动指针 ID 及其路径的数组。同样重要的是要知道,在多个移动指针的情况下,onTouchEvent 只会为所有指针调用一次,并且您需要迭代所有指针来绘制它们的新位置。

public class DrawView extends View {

    private Paint drawPaint, canvasPaint;
    private Canvas drawCanvas;
    private Bitmap canvasBitmap;

    private SparseArray<Path> paths;

    public DrawingView(Context context) {
        super(context);
        setupDrawing();
    }

    public DrawingView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setupDrawing();
    }

    public DrawingView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setupDrawing();
    }

    private void setupDrawing() {
        paths = new SparseArray<>();

        drawPaint = new Paint();
        drawPaint.setColor(Color.RED);
        drawPaint.setAntiAlias(true);
        drawPaint.setStrokeWidth(20);
        drawPaint.setStyle(Paint.Style.STROKE);
        drawPaint.setStrokeJoin(Paint.Join.ROUND);
        drawPaint.setStrokeCap(Paint.Cap.ROUND);

        canvasPaint = new Paint(Paint.DITHER_FLAG);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        drawCanvas = new Canvas(canvasBitmap);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
        for (int i=0; i<paths.size(); i++) {
            canvas.drawPath(paths.valueAt(i), drawPaint);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int index = event.getActionIndex();
        int id = event.getPointerId(index);

        Path path;
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_POINTER_DOWN:
                path = new Path();
                path.moveTo(event.getX(index), event.getY(index));
                paths.put(id, path);
                break;

            case MotionEvent.ACTION_MOVE:
                for (int i=0; i<event.getPointerCount(); i++) {
                    id = event.getPointerId(i);
                    path = paths.get(id);
                    if (path != null) path.lineTo(event.getX(i), event.getY(i));
                }
                break;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
                path = paths.get(id);
                if (path != null) {
                    drawCanvas.drawPath(path, drawPaint);
                    paths.remove(id);
                }
                break;
            default:
                return false;
        }
        invalidate();
        return true;
    }

}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何用多个手指在画布上绘图 的相关文章

  • LinearLayout 和 LinearLayoutCompat 有什么区别

    I know LinearLayoutCompat被意识到为我们提供了一些更新的方法 这些方法被添加到较高级别的 Android 到较低级别的 Android 中 我的问题是这个方法 linearLayout setPaddingRelat
  • 如何完全杀死 Android 线程?

    我有一个服务 它有自己的线程在后台运行 我想终止该服务 包括线程 我像这样创建了线程并运行它 public class DaemonService extends Service private DaemonThread thread cl
  • SurfaceView 将视频播放为拉伸视图

    我正在使用 mediaplayer xml 来播放视频文件
  • 在对话框前面显示小吃栏

    我使用以下方法来展示SnackBar在我的应用程序中 public static void showSnackBar String msg View view Snackbar snackbar Snackbar make view msg
  • SwipeListView 使用 NullPointerException

    我正在尝试使用SwipeListView 但是当我触摸应用程序上的某个项目时 它会崩溃 日志信息就在那里 05 26 21 52 26 545 E AndroidRuntime 19862 java lang NullPointerExce
  • 没有身份验证的 Android SyncAdapter 与 Android 服务

    我不希望对我的同步适配器使用身份验证 因为我计划使用它来同步搜索查询的 Twitter 公共时间线 我应该使用 SyncAdapter 还是任何普通服务 在此用例中 两者都可以正常工作 如果您的应用程序可能包含多个用户帐户 那么采用 Syn
  • 不适当的阻塞方法调用,但挂起函数“withContext”只能从协程或另一个挂起函数调用

    在我的服务中 我需要致电onStartCommand一些需要的方法withContext Dispatchers IO 反而CoroutineScope Dispatchers IO like url URL pokemon linkIma
  • Google Play 游戏服务 - 自定义通知/欢迎回来

    我一直在互联网上查找 但找不到我的答案 有没有办法向用户显示他们已登录 GPGS 第一次登录时 您会收到一条通知 内容为 欢迎 但是当您下次打开应用程序时 什么也没有显示 有没有办法显示欢迎回来通知 对于 iOS 这是可能的 在第 9 步上
  • 隐藏另一个布局的浮动操作按钮

    我有一个FloatingActionButton五月之内activity main xml名为的布局fabBtn 我的应用程序是用ViewPager和三个Fragments 我想隐藏FloatingActionButton当我的第一次Fra
  • 从用户操作中禁用整个活动

    有没有一种简单的方法来禁用用户与活动交互 当有一个操作正在运行时执行 并且标题栏中有一个旋转的进度条 编辑 看来我还不够清楚 我的意思是 虽然我已经有一个旋转的进度条 但用户仍然可以按活动上的任何按钮 我想禁止用户在任务正在运行 然而 我不
  • 清单合并 - Android studio 0.8.1 升级构建错误:属性“manifestFile”不存在

    我刚刚升级到 Android Studio 0 8 1 并升级了构建工具等 来自 Android Studio 0 6 但后来我得到了这个构建错误 发现任务配置有问题 processDevelopmentDebugResources 文件
  • 禁用 Android Monkey 上的系统级事件

    当猴子测试我的应用程序时 adb shell monkey p com foo bar throttle 1000 v 14400 我注意到它可以访问我设备上的各种系统设置 例如音频控制和屏幕截图 根据http developer andr
  • Proguard 损坏可绘制文件

    我对 proguard 有一个奇怪的问题 不知何故它破坏了我的有效可绘制文件 没有proguard的drawable显示可以 proguard 应该缩小 xml 可绘制对象吗 可绘制 wide btn round white xml
  • 如何在AsyncTask中举杯,提示我使用Looper

    我有 AsyncTask 在后台完成的任务 在某些时候 我需要发出一个 Toast 来表示某件事已完成 我尝试过但失败了因为Caused by java lang RuntimeException Can t create handler
  • React-native 应用程序在真实的 Android 设备上崩溃

    我正在使用react native 目前开始使用redux并创建了一个应用程序 但是当我在模拟器上运行该应用程序时 它工作正常 然后我通过 USB 将我的手机与 android 6 连接 它也可以工作 但在 Android 8 的其他设备中
  • Android 嵌套片段问题“java.lang.IllegalStateException:活动已被销毁”

    您可能知道 Android 支持嵌套片段 也是通过 API 级别 17 的支持库实现的 所以基本上我正在尝试将嵌套片段添加到 ViewPager 的片段之一中 并熟悉这一新的好功能 在第一次应用程序启动时 一切都按预期工作 即我可以添加子片
  • 给出文档名称的 UID

    我想知道在我的 firestore 数据库中保存用户首选项的最佳实践是什么 我会尝试用一个例子来解释 Case 1 我的 用户 集合中有这种文档 名称是由 Firebase 随机生成的 有 3 个字段 user uid 细绳 nicknam
  • appcompat 在操作栏中显示进度导致 NPE

    将我的 SDK 更新到所有最新的 Android 5 0 后 我无法使用 appcompat 中 ActionBar 中内置的进度条 我已经完成了所有通常的修复 将 supportRequestWindowFeature 调用移至 setC
  • Android RecyclerView:拖放多个ViewType

    I implement drag and drop for a RecyclerView it works well when have one View type but reset the RecyclerView when have
  • 在模拟器中实施应用内结算

    我一直在阅读有关 Android 应用程序的 实施应用内计费 的内容 并且文档说不可能在模拟器中测试该应用程序 真的吗 我正在开发的手机没有 移动数据计划 因此我尝试通过 USB 通过 PC 连接手机 但由于我的 PC 位于代理后面 因此我

随机推荐

  • 2 个站点共享一个 Web 应用程序池?什么时候应该使用这个?

    我只是做了一个简单的实验 我在本地 IIS 中创建了一个 Web 应用程序池 然后让 2 个网站指向同一个应用程序池 网站不是虚拟的 当然 它们在本地运行正常 我们什么时候应该开始使用共享应用程序池 在什么情况下您应该在两个网站之间共享应用
  • 微软Azure DDOS防护

    我正在 Microsoft Azure 中运行企业级应用程序 我想知道 Microsoft Azure 中 DDOS 投影的建议是什么 该文档明确指出该平台受到 DDOS 保护 但没有提供更多详细信息 我对Azure DDOS的理解是 如果
  • Android onBackPressed() 没有被调用?

    在我的 MainActivity 从 AppCompatActivity 扩展 中 我想重写 onBackPressed 方法 如下所示 Override public void onBackPressed Log d MainActivi
  • 构建并运行在 xcode 中禁用

    有一些应用程序显示构建并运行图标已禁用 这使我无法运行该应用程序 并非所有应用程序都会发生这种情况 但只有少数应用程序会发生这种情况 它主要发生在 facebook 应用程序和其他一些应用程序中 有人能知道可能是什么问题吗 我已经尝试了更改
  • PHP 从 MySQL 中选择日期字段为未来 7 天

    我有一个自动检查器 可以检查将在未来 7 天内过期的域名 并向客户发送电子邮件 我使用这个 SQL 查询 SELECT from domain names where status or status valid and expiry da
  • EditorFor/CheckBoxFor boolean 将 data-val-required 属性添加到 HTML,而不将 required 属性添加到模型

    我的模型类有一个没有必需属性的 bool 属性 public class Test public bool TestBool1 get set 然后在我的剃刀视图中 我使用 EditorFor CheckBoxFor 也会发生同样的情况 d
  • Swift - 可选 Void

    我正忙着使用NSURLProtocolClient的 URLProtocol 函数 welf client URLProtocol welf didReceiveResponse operation response cacheStorag
  • Jenkins 中的算法协商失败 SSH

    我正在尝试从 Jenkins ssh 到本地服务器 但抛出以下错误 SSH Exception Algorithm negotiation fail com jcraft jsch JSchException Algorithm negot
  • 解压jar到指定目录

    我想使用以下命令将我的罐子之一提取到指定目录jar命令行实用程序 如果我理解正确的话 C选项应该能解决问题 但是当我尝试时 jar xvf myJar jar C directoryToExtractTo 我从 jar 实用程序获取使用信息
  • 如何在 IE 受信任的根证书颁发机构存储中自动安装自签名证书

    我创建了一个自签名证书 但浏览器告诉我 此 CA 根证书不受信任 要启用信任 请在受信任的根证书颁发机构存储中安装此证书 我通过进入 IE gt Internet 选项 gt 内容 gt 证书 gt 等 我实际上必须导出自签名证书 然后将其
  • 为什么 UIPickerView 是半透明的

    Why UIPickerView 100不是不透明的吗 当我将文本颜色设置为白色并将 UIPickerView 放在红色背景上时 UIPickerView 项目有一个红白色文字颜色 我尝试使用自定义视图viewForRow 但还是没有效果
  • 如何加快 PostgreSQL 表中的行计数?

    我们需要计算 PostgreSQL 表中的行数 在我们的例子中 不需要满足任何条件 如果可以显着提高查询速度 那么获得行估计是完全可以接受的 基本上 我们想要select count id from table 尽可能快地运行 即使这意味着
  • 从散点评估/拟合椭圆

    这是交易 我有多个点 X Y 形成 椭圆形 形状 我想评估 拟合可能的 最佳 椭圆并获取其属性 a b F1 F2 或者只是椭圆的中心 任何想法 线索将不胜感激 Gilad Matlab有一个函数拟合椭圆 http www mathwork
  • 我如何知道我的电子邮件是否已使用 Asp.net 被接收和阅读

    我需要知道如何查询已发送的电子邮件状态 Asp net中有没有特殊的类来实现这个功能 如果没有 你知道这样做的方法吗 如果您正在使用System Net Mail命名空间 您可以向电子邮件添加标头以请求已读回执 message Header
  • c malloc 问题(内存损坏)

    使用 malloc 时 如果它生成带有错误的核心转储 malloc memory corruption 这是否意味着 malloc 尝试分配无法自由分配的内存 如果是的话 造成这种情况的原因是什么 这完全取决于您的 malloc 实现 但通
  • iPad 3 Retina 显示屏、@2x 图像和应用程序已在商店中提供

    因此 未来的某些 iPad 可能会配备 Retina 显示屏 苹果很可能会坚持 2x范式 http 9to5mac com 2012 01 19 more ipad retina images found in ibooks 2 files
  • Vim 作为笔记平台:在垂直分割的窗口中跳转到标签

    过去 我使用 Vim 作为笔记平台 使用 Vim 帮助文件格式创建一个包含标签列表的 index txt 文件 然后创建一堆具有正常格式的文本文件 Help Tag 链接语法以便使用CTRL 在index txt文件中的标签上将跳转到任意注
  • 在 Julia 中向矩阵添加一行?

    如何在 Julia 的矩阵中添加一行 例如 mat 1 2 3 3 4 2 我想添加行x 4 2 1 在最后 我试过 push mat x 但它给出了一个错误 对于这样的矩阵串联 您可以这样做 mat mat x 或者使用函数垂直串联 vc
  • 获取每个人每天的最短日期时间的记录

    CREATE TABLE IF NOT EXISTS accesscards id int 11 NOT NULL AUTO INCREMENT department varchar 255 NOT NULL name varchar 25
  • 如何用多个手指在画布上绘图

    我正在使用安卓Canvas创建绘图应用程序的类 这是我第一次尝试使用 Canvas 类 到目前为止 我使用的代码工作正常 绘图工作正常 但我在这段代码中意识到 它允许用户仅用一根手指进行绘画 我的意思是说 如果用户使用多于一根手指在画布上绘