Android 多点触控

三种类型 :接力型 /配合型 /单独型

单点触控

package com.example.myapplication.view

import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import com.example.myapplication.dp

class MultiTouchView1(context: Context, attrs: AttributeSet) : View(context, attrs) {

    private val bitmap = getBitmap()
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)

    private var offsetX = 0f //初始位置
    private var offsetY = 0f

    private var downX = 0f //按下的位置
    private var downY = 0f

    private var originalOffsetX = 0f //偏移位置
    private var originalOffsetY = 0f

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        canvas.drawBitmap(bitmap, offsetX, offsetY, paint)
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        when (event.actionMasked) {
            MotionEvent.ACTION_DOWN -> {
                downX = event.x
                downY = event.y
                originalOffsetX = offsetX
                originalOffsetY = offsetY

            }
            MotionEvent.ACTION_MOVE -> {
                offsetX = event.x - downX + originalOffsetX
                offsetY = event.y - downY + originalOffsetY
                invalidate()
            }
        }
        return true
    }
}

触摸事件序列是针对View而不是手指

x,y ,index ,id 属于一个point ,一个序列,getX 获取index为0的手指位置

  public final float getX() {
        return nativeGetAxisValue(mNativePtr, AXIS_X, 0, HISTORY_CURRENT);
    }

point_move 两个point,当第二根手指up,index会设置为0而不是1,有时候第0根手指down 为0index的会给按下的,然后0变为1

index作用是在发生MotionEvnent时,通过index遍历每个Point的操作

getX(),getX(index),index通过evnent.pointCount获取

for遍历event.point,通过getX(index) 如果某一个point抬起会报错 找不到Index,这时可以通过id查找

 MotionEvent.ACTION_MOVE -> {

                event.getX(event.actionIndex) //正在按下手指的ID
               event.getX(event.findPointerIndex(downId)) //通过ID获取
}

action_move 不适合这种方法,因为在实时移动 更新,没有所谓的Point 也就是index ,id,只有在down / up 才有意义

接力型

package com.example.myapplication.view

import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import com.example.myapplication.dp


class MultiTouchView1(context: Context, attrs: AttributeSet) : View(context, attrs) {

    private val bitmap =getBitmap()
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)

    private var offsetX = 0f //初始位置
    private var offsetY = 0f

    private var downX = 0f //按下的位置
    private var downY = 0f

    private var originalOffsetX = 0f //偏移位置
    private var originalOffsetY = 0f

    private var trackingPointerId = 0 //当前按下的手指ID

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        canvas.drawBitmap(bitmap, offsetX, offsetY, paint)
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        when (event.actionMasked) {
            MotionEvent.ACTION_DOWN -> {
                trackingPointerId = event.getPointerId(0)
                downX = event.x
                downY = event.y
                originalOffsetX = offsetX
                originalOffsetY = offsetY

            }

            //多个
            MotionEvent.ACTION_POINTER_DOWN -> {
                val actionIndex = event.actionIndex
                trackingPointerId = event.getPointerId(actionIndex) //按下的手指序号获取ID
                //更新 接管
                downX = event.getX(actionIndex)
                downY = event.getY(actionIndex)
                originalOffsetX = offsetX
                originalOffsetY = offsetY
            }

            MotionEvent.ACTION_POINTER_UP -> {
                val actionIndex = event.actionIndex
                val pointerId = event.getPointerId(actionIndex)
                if (pointerId == trackingPointerId) {
                    //如果是正在跟踪的手指 进行替换
                    val newIndex = if (actionIndex == event.pointerCount - 1) {
                            event.pointerCount - 2
                        } else {
                            event.pointerCount - 1
                        }

                    trackingPointerId = event.getPointerId(newIndex) //按下的手指序号获取ID
                    //更新 接管
                    downX = event.getX(newIndex)
                    downY = event.getY(newIndex)
                    originalOffsetX = offsetX
                    originalOffsetY = offsetY
                }
            }
            MotionEvent.ACTION_MOVE -> {

                val index = event.findPointerIndex(trackingPointerId)
                offsetX = event.getX(index) - downX + originalOffsetX
                offsetY = event.getY(index) - downY + originalOffsetY
                invalidate()
            }
        }
        return true
    }
}

配合型:

package com.example.myapplication.view

import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import com.example.myapplication.dp
import com.example.myapplication.getAvatar


//双指滑动
class MultiTouchView2(context: Context, attrs: AttributeSet) : View(context, attrs) {

    private val bitmap = getAvatar(resources, 200.dp.toInt())
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)

    private var offsetX = 0f //初始位置
    private var offsetY = 0f

    private var downX = 0f //按下的位置
    private var downY = 0f

    private var originalOffsetX = 0f //偏移位置
    private var originalOffsetY = 0f


    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        canvas.drawBitmap(bitmap, offsetX, offsetY, paint)
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        val focusX : Float//焦点 两值相加 /2
        val focusY : Float
        var pointerCount = event.pointerCount
        var sumX = 0f
        var sumY = 0f
        val inPointerUp = event.actionMasked == MotionEvent.ACTION_POINTER_UP //如果是抬起
        for (i in 0 until  pointerCount){
            if (!(inPointerUp && i == event.actionIndex)){ //当前位置并且不是抬起 则计算
                sumX += event.getX(i) //得到每个点的坐标
                sumY += event.getY(i)
            }
        }

        if (inPointerUp){
            pointerCount -- //处理额外偏移
        }
        focusX = sumX / pointerCount //得到焦点值 当抬起后count会变
        focusY = sumY /pointerCount

        when (event.actionMasked) {
            MotionEvent.ACTION_DOWN,MotionEvent.ACTION_POINTER_DOWN, MotionEvent.ACTION_POINTER_UP -> {
                downX = focusX
                downY = focusY
                originalOffsetX = offsetX
                originalOffsetY = offsetY
            }

            MotionEvent.ACTION_MOVE -> {
                offsetX = focusX - downX + originalOffsetX
                offsetY = focusY - downY + originalOffsetY
                invalidate()
            }
        }
        return true
    }
}

多指

package com.example.myapplication.view

import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Path
import android.util.AttributeSet
import android.util.SparseArray
import android.view.MotionEvent
import android.view.View
import androidx.core.util.isEmpty
import com.example.myapplication.dp

class MultiTouchView3(context: Context, attrs: AttributeSet) : View(context, attrs) {
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    private var paths = SparseArray<Path>()

    init {
        paint.style = Paint.Style.STROKE
        paint.strokeWidth = 4.dp
        paint.strokeCap = Paint.Cap.ROUND
        paint.strokeJoin = Paint.Join.ROUND

    }
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        for (i in 0 until paths.size()){
            val path = paths.valueAt(i)
            canvas.drawPath(path,paint)
        }

    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        when(event.actionMasked){

            MotionEvent.ACTION_DOWN,MotionEvent.ACTION_POINTER_DOWN ->{
                val actionIndex = event.actionIndex
                val path = Path()
                path.moveTo(event.getX(actionIndex),event.getY(actionIndex))
                paths.append(event.getPointerId(actionIndex),path)
                invalidate()
            }

            MotionEvent.ACTION_MOVE ->{
                if (!paths.isEmpty()){
                    for (i in 0 until paths.size()){
                        val pointerId = event.getPointerId(i)
                        val path = paths.get(pointerId)
                        path.lineTo(event.getX(i),event.getY(i))
                    }
                    invalidate()
                }

            }
            MotionEvent.ACTION_UP,MotionEvent.ACTION_POINTER_UP -> {
                val actionIndex = event.actionIndex
                val pointerId = event.getPointerId(actionIndex)
                paths.remove(pointerId)
                invalidate()
            }
        }


        return true
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

剑指offer(C++)-JZ21:调整数组顺序使奇数位于偶数前面(一)(算法-其他)

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 题目描述&#xff1a; 输入一个长度为 n 整数数组&#xff0c;实现一个函数来调整该数组中数字的顺序&#xff0c;使得所有的奇数…

d3.js

D3&#xff1a;Data-Driven Documents • 通过D3提供的接口来基于数据操控文档的各个图元。 标题对于D3(本讲解)最为重要的标签&#xff0c;主要操作的对象(画布) HTML - 导入D3.js D3.js作为JavaScript的外库&#xff0c;必须先将其导入&#xff0c;如&#xff1a; Python的…

Android 图层列表 、 LayerDrawable 、 layer-list \ 改变 seekbar thumb 滑块 的颜色

android 官网 &#xff1a; 图层列表 LayerDrawable / layer-list LayerDrawable 是管理其他可绘制对象数组的可绘制对象。列表中的每个可绘制对象均按照列表顺序绘制。列表中的最后一个可绘制对象绘于顶部。 每个可绘制对象均由单个 <layer-list> 元素内的 <item>…

计算机毕业设计:基于python机器学习的全国气象数据采集预测可视化系统 预测模型+爬虫(包含文档+源码+部署教程)

[毕业设计]2023-2024年最新最全计算机专业毕设选题推荐汇总 感兴趣的可以先收藏起来&#xff0c;还有大家在毕设选题&#xff0c;项目以及论文编写等相关问题都可以给我留言咨询&#xff0c;希望帮助更多的人 。 1、摘 要 随着气候变化的不断加剧&#xff0c;气象数据的准确性…

方太集团合同档案管理平台,让数字化成果深度利用、可查可验

数字经济大背景下&#xff0c;方太集团积极拥抱企业数字化转型&#xff0c;推动合同档案业务管理数字化&#xff0c;助力业务档案高效融合&#xff0c;助力企业创新科技发展。 方太集团&#xff08;以下简称“方太”&#xff09;创建于1996年。作为一家以智能厨电为核心业务的…

美国材料与试验协会ASTM发布新版玩具安全标准 ASTM F963-23

美国材料与试验协会ASTM发布新版玩具安全标准 ASTM F963-23 2023年10月13日&#xff0c;美国材料与试验协会&#xff08;ASTM&#xff09;发布了新版玩具安全标准ASTM F963-23 ​根据CPSIA的规定&#xff0c;当ASTM将ASTM F963的拟定修订意见通知CPSC时&#xff0c;若CPSC认为…

SpringDataJpa(三)

七、Specifications动态查询 有时我们在查询某个实体的时候&#xff0c;给定的条件是不固定的&#xff0c;这时就需要动态构建相应的查询语句&#xff0c;在Spring Data JPA中可以通过JpaSpecificationExecutor接口查询。相比JPQL,其优势是类型安全,更加的面向对象。 import …

电动汽车多时段动态充电价格及网损策略研究

摘要&#xff1a;电动汽车以无序充电方式接入配电网时与网内基础用电负荷叠加&#xff0c;会形成峰上加峰的现象&#xff0c;不利于配电网的稳定运行。针对上述问题&#xff0c;首先对私家车充电负荷进行建模&#xff0c;采用蒙特卡罗抽样模拟电动汽车无序行为下的充电负荷曲线…

【PostgreSql高阶语法 】1、CASE WHEN THEN END用法

目录 1. 基础描述2. 用法举例2.1 基础使用2.1.1 方式12.1.2 方式 2 2.2 进行分组2.3 分组练习举例 1. 基础描述 目的&#xff1a;在SQL语句中添加判断条件&#xff0c;就要用到CASE WHEN THEN END用法&#xff1a;类似于java里面的switch语句&#xff0c;一组CASE WHEN THEN E…

Harmony 应用开发的知识储备

Harmony 应用开发的知识储备 前言正文一、DevEco Studio版本二、手机版本① 环境变量 三、API版本四、开发语言五、运行调试 前言 这里先说明一点&#xff0c;如果你对Android应用开发很熟悉&#xff0c;那么做Harmony应用开发也可以驾轻就熟&#xff0c;只不过在此之前你需要知…

Gated Context Aggregation Network for Image Dehazing and Deraining(GCANet)

1 总体概述 GCANet是端到端去雾的一篇代表性的文章&#xff0c;它摒弃以往使用手工设计的先验以及大气散射模型的使用&#xff0c;直接通过原始有雾图像估计出无雾图像J与有雾图像I之间的残差&#xff0c;图像恢复阶段直接使用网络输出的残差与输入有雾图像I之间的加和完成去雾…

MyBatis-plus最详细的入门使用教程来了

1.概述 MyBatis-Plus &#xff08;简称 MP&#xff0c;下文就使用简称啦&#xff09;是一个 MyBatis的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。官网地址&#xff1a;https://baomidou.com/ 有以下特性&#xff1a; 无…

modbus-TCP协议详解

modbus-TCP协议详解 1996年施耐德公司推出基于以太网TCP/IP的modbus协议&#xff1a;modbus-TCP。 MODBUS-TCP使MODBUS-RTU协议运行于以太网&#xff0c;MODBUS-TCP使用TCP/IP以太网在站点间传送MODBUS报文&#xff0c;MODBUS-TCP结合了以太网物理网络和网络标准TCP/IP以及以…

春江天玺89㎡三室两厅,现代轻奢丨不止风格更是一种生活态度。福州中宅装饰,福州装修

空间在格局上并没做太多改动&#xff0c;在满足基本的室内功能基础上&#xff0c;用相对简洁的手法来做立面整体的统筹与演绎&#xff0c;在提亮整个空间感的情况下&#xff0c;进行合理的动线分区&#xff0c;提高空间利用率&#xff0c;成为本案的设计要点。 Part 1 在客厅的…

JVM中jhat虚拟机堆转储快照分析工具

jhat虚拟机堆转储快照分析工具 1、jhat jhat也是jdk内置的工具之一。主要是用来分析java堆的命令&#xff0c;可以将堆中的对象以html的形式显示出来&#xff0c;包括对 象的数量&#xff0c;大小等等&#xff0c;并支持对象查询语言。 使用jmap等方法生成java的堆文件后&a…

HHDESK端口转发监控服务

端口转发是一种网络技术&#xff0c;用于将外部网络请求转发到内部网络中的特定设备或服务。它允许通过公共网络访问内部网络中的资源&#xff0c;提供了灵活性和便利性。 传统的端口转发方式是通过配置路由器的端口映射&#xff0c;但这需要具备网络知识和一定的技术操作&…

lesson4-C++内存管理

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 目录 C/C内存分布 C语言中动态内存管理方式 C中动态内存管理 operator new与operator delete函数 new和delete的实现原理 定位new表达式(placement-new) 常见面试题 C/C内存分布 我们先来看一段代码&#xff1a; int…

SpringBoot上传与下载文件

使用SpringBoot的虚拟路径映射。 Config中的类 import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import o…

收单外包机构评级等级为何获D、E级及其影响分析

孟凡富 中国支付清算协会发布2022年度收单外包服务机构评级等级。本次评级工作&#xff0c;共有包括银行和非银行支付机构在内的134家收单机构对13000家外包机构进行了评价&#xff0c;参评外包机构数量较上一年度增长35.59%&#xff0c;评级工作覆盖面继续扩大。评级等级在C级…

中波发射机概述

一、简介 1.1 中波发射机简介 中波发射机是一种用于广播电台传输中波频率信号的设备。它们是广播电台系统的重要组成部分&#xff0c;用于发送声音、音乐和其他信息到广泛的地理区域。中波频率范围一般介于530kHz至1700kHz之间&#xff0c;具有较好的传播性能&#xff0c;可以…