当前位置: 首页 > news >正文

【Android】双指旋转手势

一,概述

本文参考android.view.ScaleGestureDetector,对双指旋转手势做了一层封装,采用了向量计算法简单实现,笔者在此分享下。

二,实例

如下,使用RotateGestureDetector即可委托,实现旋转手势的简单封装,在对应Callback获取到旋转值设置到View即可。

public class RectView extends FrameLayout {private static final String TAG = "RectView";private View mRotateView;private final ScaleGestureDetector scaleGestureDetector = new ScaleGestureDetector(getContext(), new ScaleGestureDetector.SimpleOnScaleGestureListener() {@Overridepublic boolean onScale(@NonNull ScaleGestureDetector detector) {Log.d(TAG, "onScale() called with: detector = [" + detector.getScaleFactor() + "]");mRotateView.setScaleX(detector.getScaleFactor());mRotateView.setScaleY(detector.getScaleFactor());return true;}@Overridepublic boolean onScaleBegin(@NonNull ScaleGestureDetector detector) {return super.onScaleBegin(detector);}@Overridepublic void onScaleEnd(@NonNull ScaleGestureDetector detector) {super.onScaleEnd(detector);}});private final RotateGestureDetector rotateGestureDetector = new RotateGestureDetector(new RotateGestureDetector.Listener() {@Overridepublic void onRotateStart(RotateGestureDetector detector) {}@Overridepublic void onRotating(RotateGestureDetector detector) {mRotateView.setRotation((float) detector.eulerAngle);}@Overridepublic void onRotateEnd(RotateGestureDetector detector) {}});public RectView(Context context) {super(context);mRotateView = new View(context);mRotateView.setBackgroundColor(Color.GRAY);this.addView(mRotateView, new FrameLayout.LayoutParams(100, 100, Gravity.CENTER));}public RectView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}public RectView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}public RectView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);}//    @SuppressLint("ClickableViewAccessibility")
//    @Override
//    public boolean onTouchEvent(MotionEvent event) {
//        rotateGestureDetector.onTouchEvent(event);
//        return true;
//    }@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {rotateGestureDetector.onTouchEvent(ev);scaleGestureDetector.onTouchEvent(ev);return true;}
}

三,实现

Rotate具体实现如下,仅供参考。

import android.view.MotionEvent;import androidx.annotation.NonNull;/*** @author :zhong.jw* @date :Created in 2023/2/23 13:39* 旋转手势相关:采用向量法计算角度*/
public final class RotateGestureDetector {private static final double DEFAULT_LIMIT_START = 3f;@NonNullprivate final Listener listener;/*** 是否旋转中*/public boolean isRotating = false;/*** 旋转轴点x*/public int focusX;/*** 旋转轴点y*/public int focusY;/*** 欧拉角,范围[-180~180]*/public double eulerAngle = 0;/*** 弧度*/public double radian = 0;/*** 开始旋转的初始向量x值*/public double x1 = 0;/*** 开始旋转的初始向量y值*/public double y1 = 0;/*** 开始旋转的初始向量斜率*/public double k1 = 0;public RotateGestureDetector(@NonNull Listener listener) {this.listener = listener;}public boolean onTouchEvent(MotionEvent event) {int action = event.getActionMasked();int pointCount = event.getPointerCount();if ((action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN) && (pointCount == 2) && !isRotating) {double e1x = event.getX(0);double e2x = event.getX(1);double e1y = event.getY(0);double e2y = event.getY(1);focusX = (int) (e1x + e2x) / 2;focusY = (int) ((e1y + e2y) / 2);x1 = e2x - e1x;y1 = e2y - e1y;k1 = y1 / x1;return true;}if (action == MotionEvent.ACTION_MOVE && pointCount == 2) {double e1x = event.getX(0);double e2x = event.getX(1);double e1y = event.getY(0);double e2y = event.getY(1);double x2 = e2x - e1x;double y2 = e2y - e1y;//angle = arccos(ab/(|a||b|))radian = Math.acos((x1 * x2 + y1 * y2) / (Math.sqrt(Math.pow(x1, 2) + Math.pow(y1, 2)) * Math.sqrt(Math.pow(x2, 2) + Math.pow(y2, 2))));// y = k1*x2 > y2 来判断是否属于外角eulerAngle = (radian / Math.PI * 180) * (k1 * x2 > y2 ? -1 : 1);if (isRotating) {listener.onRotating(this);}if (Math.abs(eulerAngle) >= DEFAULT_LIMIT_START) {isRotating = true;listener.onRotateStart(this);}return true;}if ((action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) && isRotating) {isRotating = false;listener.onRotateEnd(this);}return true;}public interface Listener {/*** @param detector:旋转信息*/void onRotateStart(RotateGestureDetector detector);/*** @param detector:旋转信息*/void onRotating(RotateGestureDetector detector);/*** @param detector:旋转信息*/void onRotateEnd(RotateGestureDetector detector);}}

http://www.lqws.cn/news/110503.html

相关文章:

  • OCC笔记:TopoDS_Edge上是否一定存在Geom_Curve
  • Nginx配置Ollama 访问api服务
  • 深拷贝与浅拷贝的区别?如何手写实现一个深拷贝?
  • ​​Agentic Voice Stack 热门项目
  • 【SAP FICO】在建工程及固定资产
  • 2023年12月四级真题Reading Comprehension的分析总结
  • LeetCode第245题_最短单词距离III
  • RTC实时时钟DS1338Z-33/PT7C433833WEX国产替代FRTC1338S
  • 自动化采集脚本与隧道IP防封设计
  • EscapeX:去中心化游戏,开启极限娱乐新体验
  • HarmonyOS运动开发:精准估算室内运动的距离、速度与步幅
  • 【QT】自定义QWidget标题栏,可拖拽(拖拽时窗体变为normal大小),可最小/大化、关闭(图文详情)
  • 笔记:算法题目中需要处理 int 某个位的三种方法:for、while、to_string
  • [Java 基础]变量,装东西的盒子
  • 功能管理:基于 ABP 的 Feature Management 实现动态开关
  • 《前端面试题:CSS预处理器(Sass、Less等)》
  • Go语言学习-->第一个go程序--hello world!
  • SQLite详细解读
  • 飞牛fnNAS使用群辉DSM系统
  • 黑马Java面试笔记之 消息中间件篇(RabbitMQ)
  • WPF技术体系与现代化样式
  • 数学复习笔记 24
  • MATLAB实战:视觉伺服控制实现方案
  • 无人机自主降落论文解析
  • 无人机甲烷检测技术革新:开启环境与能源安全监测新时代
  • 无人机智能识别交通目标,AI视觉赋能城市交通治理新高度
  • GPIO的内部结构与功能解析
  • 普中STM32F103ZET6开发攻略(四)
  • .Net Framework 4/C# 初识 C#
  • C#面试问题81-100