Android 双向滑动

GestureDetector //系统
GestureDetectorCompat //AndroidX 较旧机型有更好的支持

code

package com.example.myapplication.viewimport android.animation.ObjectAnimator
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import android.view.View
import android.widget.OverScroller
import androidx.core.view.GestureDetectorCompat
import androidx.core.view.ViewCompat
import com.example.myapplication.dp
import com.example.myapplication.getAvatar
import kotlin.math.max
import kotlin.math.minprivate val IMAGE_SIZE = 300.dp.toInt()
private const val EXTRA_SCALE_FACTOR = 1.5fclass ScalableImageView(context: Context, attrs: AttributeSet) : View(context, attrs) {private val runnable = FlingRunner()private val bitmap = getAvatar(resources, IMAGE_SIZE)private val paint = Paint(Paint.ANTI_ALIAS_FLAG)private var simpleGenListener = SimpleGenListener()private val scaleListener = SimpleScaleListener()private val scaleGenstureDetector =ScaleGestureDetector(context, scaleListener) //ScaleGestureDetectorCompat是扩展private var originalOffsetX = 0fprivate var originalOffsetY = 0fprivate var offsetX = 0f //偏移private var offsetY = 0fprivate var smallScale = 0f //小图private var bigScale = 0f //大图private val scaleAnimation = ObjectAnimator.ofFloat(this, "currentScale", smallScale, bigScale)private val gestureDetector = GestureDetectorCompat(context, simpleGenListener)  //gestureDetector.setIsLongpressEnabled(false) //关闭长按private var scroller = OverScroller(context) //滑动flingprivate var big = false //双击开关private var currentScale = 0fset(value) {field = valueinvalidate()}override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {super.onSizeChanged(w, h, oldw, oldh)originalOffsetX = (width - bitmap.width) / 2foriginalOffsetY = (height - bitmap.height) / 2fif (bitmap.width / bitmap.height.toFloat() > width / height.toFloat()) {smallScale = width / bitmap.width.toFloat()bigScale = height / bitmap.height.toFloat() * EXTRA_SCALE_FACTOR} else {smallScale = height / bitmap.height.toFloat()bigScale = width / bitmap.width.toFloat() * EXTRA_SCALE_FACTOR}currentScale = smallScalescaleAnimation.setFloatValues(smallScale, bigScale)}override fun onDraw(canvas: Canvas) {super.onDraw(canvas)val scaleFraction = (currentScale - smallScale) / (bigScale - smallScale)canvas.translate(offsetX * scaleFraction, offsetY * scaleFraction)
//        val scale = smallScale + (bigScale - smallScale) * scaleFraction//scale 改变后 坐标系也会改变
//        canvas.scale(scale, scale, width / 2f, height / 2f)canvas.scale(currentScale, currentScale, width / 2f, height / 2f)canvas.drawBitmap(bitmap, originalOffsetX, originalOffsetY, paint)}private fun fixOffsets() {offsetX = min(offsetX, (bitmap.width * bigScale - width) / 2)offsetX = max(offsetX, -(bitmap.width * bigScale - width) / 2)offsetY = min(offsetY, (bitmap.height * bigScale - height) / 2)offsetY = max(offsetY, -(bitmap.height * bigScale - height) / 2)}override fun onTouchEvent(event: MotionEvent?): Boolean {scaleGenstureDetector.onTouchEvent(event)if (!scaleGenstureDetector.isInProgress){gestureDetector.onTouchEvent(event)}return true}inner class SimpleScaleListener : ScaleGestureDetector.OnScaleGestureListener {override fun onScale(detector: ScaleGestureDetector): Boolean {val tempCurrentScale =  currentScale * detector.scaleFactorif (tempCurrentScale < smallScale || tempCurrentScale > bigScale){return false}else{currentScale *= detector.scaleFactor //0 maxreturn true}//            currentScale = currentScale.coerceAtLeast(smallScale).coerceAtMost(bigScale) //最大值
//            return true //是否统计刚才的值是否拿到}override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {offsetX = (detector.focusX - width / 2f) * (1 - bigScale / smallScale)offsetY = (detector.focusY - height / 2f) * (1 - bigScale / smallScale)fixOffsets()return true}override fun onScaleEnd(detector: ScaleGestureDetector?) {}}//  GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener,inner class SimpleGenListener : GestureDetector.SimpleOnGestureListener() {//快速滑动 velocityX 速率 位移 矢量长度override fun onFling(downEvent: MotionEvent?,currentEvent: MotionEvent?,velocityX: Float,velocityY: Float): Boolean {if (big) {scroller.fling(offsetX.toInt(),offsetY.toInt(),velocityX.toInt(),velocityY.toInt(),(-(bitmap.width * bigScale - width) / 2).toInt(),((bitmap.width * bigScale - width) / 2).toInt(),(-(bitmap.height * bigScale - height) / 2).toInt(),((bitmap.height * bigScale - height) / 2).toInt())//下一帧调用 刷新ViewCompat.postOnAnimation(this@ScalableImageView, runnable)}return false}//onMove actionMove distanceX旧位置-新位置override fun onScroll(downEvent: MotionEvent?,cuuentEvent: MotionEvent?,distanceX: Float,distanceY: Float): Boolean {if (big) {offsetX -= distanceXoffsetY -= distanceYfixOffsets()invalidate()}return false}//双击 判断依据 300ms,40ms以下不处理 防手抖override fun onDoubleTap(e: MotionEvent): Boolean {big = !bigif (big) {offsetX = (e.x - width / 2) * (1 - bigScale / smallScale)offsetY = (e.y - height / 2) * (1 - bigScale / smallScale)fixOffsets()scaleAnimation.start()} else {scaleAnimation.reverse()}return true}//        //双击之后的后续操作
//        override fun onDoubleTapEvent(e: MotionEvent?): Boolean {
//            return false
//        }//长按
//        override fun onLongPress(e: MotionEvent?) {}
//        override fun onShowPress(e: MotionEvent?) {}//        //不是长按 也不是双击回调 等300ms 没按下 会回调 双击时回调使用准备 时间不够及时
//        override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
//            return false
//        }override fun onDown(e: MotionEvent?): Boolean {return true}//        //单次点击抬起 判断是否是快速抬起给系统做决定 true false 不影响结果 单击
//        override fun onSingleTapUp(e: MotionEvent?): Boolean {
//            return false
//        }}inner class FlingRunner : Runnable {override fun run() {//当前位置 速度if (scroller.computeScrollOffset()) {offsetX = scroller.currX.toFloat()offsetY = scroller.currY.toFloat()invalidate()ViewCompat.postOnAnimation(this@ScalableImageView, this)}}}
}

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

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

相关文章

K8s:部署 CNI 网络组件+k8s 多master集群部署+负载均衡及Dashboard k8s仪表盘图像化展示管理

目录 1 部署 CNI 网络组件 1.1 部署 flannel 1.2 部署 Calico 1.3 部署 CoreDNS 2 负载均衡部署 3 部署 Dashboard 1 部署 CNI 网络组件 1.1 部署 flannel K8S 中 Pod 网络通信&#xff1a; ●Pod 内容器与容器之间的通信 在同一个 Pod 内的容器&#xff08;Pod 内的容…

《网络协议》01. 基本概念

title: 《网络协议》01. 基本概念 date: 2022-08-30 09:50:52 updated: 2023-11-05 15:28:52 categories: 学习记录&#xff1a;网络协议 excerpt: 互联网、网络互连模型&#xff08;OSI&#xff0c;TCP/IP&#xff09;、计算机通信基础、MAC 地址、ARP & ICMP、IP & 子…

以华为为例:GTM岗位主要是做什么的?如何做好GTM工作?

如何让产品上市以后卖得更好&#xff1f;这是一个系统工程。 许多公司在学习华为&#xff08;尤其是做消费者业务&#xff0c;通俗地说就是2C业务的公司或产品&#xff09;&#xff0c;设立GTM相关的岗位。我们来看一下&#xff0c;GTM岗位主要做些什么&#xff0c;如何才能胜…

后端除了增删改查还有什么?

大家好&#xff0c;我是伍六七。 刚工作 3、5 年的 Java 程序猿们&#xff0c;在日常工作中开始得心应手&#xff0c;基本上没有什么问题能难倒我们。 这个时候&#xff0c;我们很容易陷入迷茫当中&#xff0c;不知道怎么继续提升自己&#xff1f;怎么才能进阶资深、专家、总…

关键字驱动自动化测试框架搭建详解

前言 那么这篇文章我们将了解关键字驱动测试又是如何驱动自动化测试完成整个测试过程的。关键字驱动框架是一种功能自动化测试框架&#xff0c;它也被称为表格驱动测试或者基于动作字的测试。关键字驱动的框架的基本工作是将测试用例分成四个不同的部分。首先是测试步骤&#…

vite和webpack的区别

Vite&#xff0c;一个基于浏览器原生 ES imports 的开发服务器。利用浏览器去解析 imports&#xff0c;在服务器端按需编译返回&#xff0c;完全跳过了打包这个概念&#xff0c;服务器随起随用。同时不仅有 Vue 文件支持&#xff0c;还搞定了热更新&#xff0c;而且热更新的速度…

dockerfile避坑笔记(VMWare下使用Ubuntu在Ubuntu20.04基础镜像下docker打包多个go项目)

一、docker简介 docker是一种方便跨平台迁移应用的程序&#xff0c;通过docker可以实现在同一类操作系统中&#xff0c;如Ubuntu和RedHat两个linux操作系统中&#xff0c;实现程序的跨平台部署。比如我在Ubuntu中打包了一个go项目的docker镜像&#xff08;镜像为二进制文件&am…

pytorch直线拟合

目录 1、数据分析 2、pytorch直线拟合 1、数据分析 直线拟合的前提条件通常包括以下几点&#xff1a; 存在线性关系&#xff1a;这是进行直线拟合的基础&#xff0c;数据点之间应该存在一种线性关系&#xff0c;即数据的分布可以用直线来近似描述。这种线性关系可以是数据点…

Go Gin中间件

Gin是一个用Go语言编写的Web框架&#xff0c;它提供了一种简单的方式来创建HTTP路由和处理HTTP请求。中间件是Gin框架中的一个重要概念&#xff0c;它可以用来处理HTTP请求和响应&#xff0c;或者在处理请求之前和之后执行一些操作。 以下是关于Gin中间件开发的一些基本信息&am…

小程序https证书

小程序通常需要与服务器进行数据交换&#xff0c;包括用户登录信息、个人资料、支付信息等敏感数据。如果不使用HTTPS&#xff0c;这些数据将以明文的方式在网络上传输&#xff0c;容易被恶意攻击者截获和窃取。HTTPS通过数据加密来解决这个问题&#xff0c;确保数据在传输过程…

策略模式在数据接收和发送场景的应用

在本篇文章中&#xff0c;我们介绍了策略模式&#xff0c;并在数据接收和发送场景中使用了策略模式。 背景 在最近项目中&#xff0c;需要与外部系统进行数据交互&#xff0c;刚开始交互的系统较为单一&#xff0c;刚开始设计方案时打算使用了if else 进行判断&#xff1a; if(…

函数调用指令, 返回机制分析(x86_64)

预备 #include <stdio.h>int addDetail(int a, int b) {return a b; }int add(int a, int b) {int c;c addDetail(a, b);return c; }int main(int argc, char *argv[]) {int sum;sum add(3, 5);printf("sum %d\n", sum);return 0; }汇编 main add addDeta…

ZZ038 物联网应用与服务赛题第J套

2023年全国职业院校技能大赛 中职组 物联网应用与服务 任 务 书 &#xff08;J卷&#xff09; 赛位号&#xff1a;______________ 竞赛须知 一、注意事项 1.检查硬件设备、电脑设备是否正常。检查竞赛所需的各项设备、软件和竞赛材料等&#xff1b; 2.竞赛任务中所使用…

FreeRTOS_任务通知

目录 1. 任务通知简介 2. 发送任务通知 2.1 函数 xTaskNotify() 2.2 函数 xTaskNotifyFromISR() 2.3 函数 xTaskNotifyGive() 2.4 函数 vTaskNotifyGiveFromISR() 2.5 函数 xTaskNotifyAndQuery() 2.6 函数 xTaskNotifyAndQueryFromISR() 3. 任务通知通用发送函数 3.…

【NI-DAQmx入门】NI-DAQmx之C、C++、VB、VB.net与C#支持

DAQmx应用程序编程接口(API) DAQmx附带数据采集编程所需的API。DAQmx API只是一组库&#xff0c;其中包含关于如何执行所有数据采集操作的函数。这些API支持LabWindows/CVI、C、C、Visual Basic 6.0、VB.NET和C#。 DAQmx API随DAQmx驱动程序一起安装&#xff0c;包含以下参考…

Lambda 架构 vs Kappa 架构

大数据处理架构详解&#xff1a;Lambda架构、Kappa架构、流批一体、Dataflow模型、实时数仓 https://www.cnblogs.com/robots2/p/17769376.html 大数据架构设计&#xff08;四十五&#xff09; - 知乎 浅析Lambda架构 - 简书 Lambda 架构 vs Kappa 架构 Lambda架构和Kappa架…

基于 golang 从零到一实现时间轮算法 (二)

Go实现单机版时间轮 上一章介绍了时间轮的相关概念&#xff0c;接下来我们会使用 golang 标准库的定时器工具 time ticker 结合环状数组的设计思路&#xff0c;实现一个单机版的单级时间轮。 首先我们先运行一下下面的源码&#xff0c;看一下如何使用。 https://github.com/x…

【Python语言速回顾】——爬虫基础知识

目录 一、爬虫概述 1、准备工作 2、爬虫类型 3、爬虫原理 二、爬虫三大库 1、Requests库 2、BeautifulSoup库 3、Lxml库 一、爬虫概述 爬虫又称网络机器人&#xff0c;可以代替人工从互联网中采集、整理数据。常见的网络爬虫主要有百度公司的Baiduspider、360公司的36…

自动驾驶算法(七):基于遗传算法的路径规划(下)

目录 1 遗传选择 2 遗传交叉 3 遗传变异 4 结语 1 遗传选择 我们书接上回&#xff0c;我们完成了种群的初始化&#xff0c;将所有的种群放入了new_pop1中&#xff0c;这个new_pop1是一个&#xff08;种群大小 * 路径&#xff09;的一个矩阵&#xff0c;我们来看如何进行遗传…

Java面向对象(进阶)-- super关键字的使用与子类对象实例化全过程

文章目录 一、super关键字的使用&#xff08;1&#xff09;为什么需要super&#xff1f;&#xff08;2&#xff09;super的理解&#xff08;3&#xff09;super可以调用的结构1、super调用方法举例1举例2举例3小结 2、super调用属性举例1举例2举例3小结 3、super调用构造器引入…