Android官方开发文档Training系列课程中文版:手势处理之拖拽或缩放

原文地址:https://developer.android.com/training/gestures/scale.html

这节课主要学习如何使用触摸手势来拖动、放大屏幕上的对象。

拖动对象

如果你的重点在Android 3.0以上的版本,那么你可以使用内置的拖拽事件监听器View.OnDragListener。

触摸手势最常见的操作就是使用它来拖动屏幕上的对象。下面的代码允许用户拖动屏幕上的图像。要注意以下几点:

  • 在拖动操作中,APP会一直保持手指拖动的轨迹,就算是另一只手指触到屏幕也是。举个例子,想象一根手指在拖动着一张图像,这时用户将第二根手指放置到屏幕上,如果APP只是追踪单根手指的轨迹,那么它会将第二根手指作为默认位置,并会将图像移动到这个位置。
  • 为了防止这样的事件发生,APP需要区分第一根手指与其它手指。为此,需要追踪 ACTION_POINTER_DOWN 及 ACTION_POINTER_UP 。ACTION_POINTER_DOWN 及 ACTION_POINTER_UP在第二根手指落下或抬起的时候由onTouchEvent()方法传回。
  • 在ACTION_POINTER_UP的情况下,示例提取了这个事件的索引,并确保当前活动的指针不是那个已经不在屏幕上的指针。如果是那个指针的话,那么APP会选择一个不同的指针使其活动并保存它的X及Y的位置。一旦这个值被保存下来,那么APP将会使用正确指针的数据一直计算剩余移动的距离。

下面的代码允许用户在屏幕上拖动对象。它记录了当前活动指针的初始位置,计算了它所位移的距离,并将对象移动到新的位置上。

这里要注意,代码段使用了getActionMasked()方法。你应该一直使用这个方法来接收MotionEvent对象的活动。与getAction()方法不同,getActionMasked()工作于多点触控模式下。它会返回被执行的掩饰活动,不包括指针的索引比特。

// The ‘active pointer’ is the one currently moving our object.
private int mActivePointerId = INVALID_POINTER_ID;
@Override
public boolean onTouchEvent(MotionEvent ev) {// Let the ScaleGestureDetector inspect all events.mScaleDetector.onTouchEvent(ev);final int action = MotionEventCompat.getActionMasked(ev); switch (action) { case MotionEvent.ACTION_DOWN: {final int pointerIndex = MotionEventCompat.getActionIndex(ev); final float x = MotionEventCompat.getX(ev, pointerIndex); final float y = MotionEventCompat.getY(ev, pointerIndex); // Remember where we started (for dragging)mLastTouchX = x;mLastTouchY = y;// Save the ID of this pointer (for dragging)mActivePointerId = MotionEventCompat.getPointerId(ev, 0);break;}case MotionEvent.ACTION_MOVE: {// Find the index of the active pointer and fetch its positionfinal int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);  final float x = MotionEventCompat.getX(ev, pointerIndex);final float y = MotionEventCompat.getY(ev, pointerIndex);// Calculate the distance movedfinal float dx = x - mLastTouchX;final float dy = y - mLastTouchY;mPosX += dx;mPosY += dy;invalidate();// Remember this touch position for the next move eventmLastTouchX = x;mLastTouchY = y;break;}case MotionEvent.ACTION_UP: {mActivePointerId = INVALID_POINTER_ID;break;}case MotionEvent.ACTION_CANCEL: {mActivePointerId = INVALID_POINTER_ID;break;}case MotionEvent.ACTION_POINTER_UP: {final int pointerIndex = MotionEventCompat.getActionIndex(ev); final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); if (pointerId == mActivePointerId) {// This was our active pointer going up. Choose a new// active pointer and adjust accordingly.final int newPointerIndex = pointerIndex == 0 ? 1 : 0;mLastTouchX = MotionEventCompat.getX(ev, newPointerIndex); mLastTouchY = MotionEventCompat.getY(ev, newPointerIndex); mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);}break;}}       return true;
}

平移

上面的部分展示了如何在屏幕上拖动对象。另一个通用的场景就是平移了,平移的意思是:用户的拖动动作引起的x及y轴方向上的滚动。上面的代码直接将MotionEvent拦截实现拖动。这部分的代码将会采用另一种更具有优势的方法,以便支持通用手势。它重写了GestureDetector.SimpleOnGestureListener的onScroll()方法。

只有用户在使用手指移动内容时,onScroll()才会被调用。onScroll()只有在手指按下的时候才会调用,一旦手指离开屏幕,那么平移手势也随之终止。

下面是onScroll()的使用摘要:

// The current viewport. This rectangle represents the currently visible
// chart domain and range.
private RectF mCurrentViewport =new RectF(AXIS_X_MIN, AXIS_Y_MIN, AXIS_X_MAX, AXIS_Y_MAX);// The current destination rectangle (in pixel coordinates) into which the
// chart data should be drawn.
private Rect mContentRect;private final GestureDetector.SimpleOnGestureListener mGestureListener= new GestureDetector.SimpleOnGestureListener() {
...@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY) {// Scrolling uses math based on the viewport (as opposed to math using pixels).// Pixel offset is the offset in screen pixels, while viewport offset is the// offset within the current viewport.float viewportOffsetX = distanceX * mCurrentViewport.width()/ mContentRect.width();float viewportOffsetY = -distanceY * mCurrentViewport.height()/ mContentRect.height();...// Updates the viewport, refreshes the display.setViewportBottomLeft(mCurrentViewport.left + viewportOffsetX,mCurrentViewport.bottom + viewportOffsetY);...return true;
}

下面是setViewportBottomLeft()方法的实现,它主要实现了移动内容的逻辑:

/*** Sets the current viewport (defined by mCurrentViewport) to the given* X and Y positions. Note that the Y value represents the topmost pixel position,* and thus the bottom of the mCurrentViewport rectangle.*/
private void setViewportBottomLeft(float x, float y) {/** Constrains within the scroll range. The scroll range is simply the viewport* extremes (AXIS_X_MAX, etc.) minus the viewport size. For example, if the* extremes were 0 and 10, and the viewport size was 2, the scroll range would* be 0 to 8.*/float curWidth = mCurrentViewport.width();float curHeight = mCurrentViewport.height();x = Math.max(AXIS_X_MIN, Math.min(x, AXIS_X_MAX - curWidth));y = Math.max(AXIS_Y_MIN + curHeight, Math.min(y, AXIS_Y_MAX));mCurrentViewport.set(x, y - curHeight, x + curWidth, y);// Invalidates the View to update the display.ViewCompat.postInvalidateOnAnimation(this);
}

缩放

在Detecting Common Gestures中,我们讨论到GestureDetector可以帮助我们来检测比如滑动、滚动、长按等手势。而对于缩放,Android提供了ScaleGestureDetector. GestureDetector 以及 ScaleGestureDetector。

为了可以反馈检测到的手势事件,手势探测器使用了监听器对象ScaleGestureDetector.OnScaleGestureListener。如果你只关心部分手势的话,Android还提供了ScaleGestureDetector.SimpleOnScaleGestureListener,你可以通过重写它的方法来使用。

缩放基础示例

下面的代码是缩放所需要的基础:

private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;public MyCustomView(Context mContext){...// View code goes here...mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}@Override
public boolean onTouchEvent(MotionEvent ev) {// Let the ScaleGestureDetector inspect all events.mScaleDetector.onTouchEvent(ev);return true;
}@Override
public void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.save();canvas.scale(mScaleFactor, mScaleFactor);...// onDraw() code goes here...canvas.restore();
}private class ScaleListenerextends ScaleGestureDetector.SimpleOnScaleGestureListener {@Overridepublic boolean onScale(ScaleGestureDetector detector) {mScaleFactor *= detector.getScaleFactor();// Don't let the object get too small or too large.mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));invalidate();return true;}
}

稍微复杂点的示例

下面是一个稍微复杂一点的示例,它摘自与这节课所提供的示例InteractiveChart(PS:示例工程请参见原网页)。InteractiveChart同时支持平移、缩放,它使用了ScaleGestureDetector的“平移”(getCurrentSpanX/Y)及“焦点” (getFocusX/Y)特性:

@Override
private RectF mCurrentViewport =new RectF(AXIS_X_MIN, AXIS_Y_MIN, AXIS_X_MAX, AXIS_Y_MAX);
private Rect mContentRect;
private ScaleGestureDetector mScaleGestureDetector;
...
public boolean onTouchEvent(MotionEvent event) {boolean retVal = mScaleGestureDetector.onTouchEvent(event);retVal = mGestureDetector.onTouchEvent(event) || retVal;return retVal || super.onTouchEvent(event);
}/*** The scale listener, used for handling multi-finger scale gestures.*/
private final ScaleGestureDetector.OnScaleGestureListener mScaleGestureListener= new ScaleGestureDetector.SimpleOnScaleGestureListener() {/*** This is the active focal point in terms of the viewport. Could be a local* variable but kept here to minimize per-frame allocations.*/private PointF viewportFocus = new PointF();private float lastSpanX;private float lastSpanY;// Detects that new pointers are going down.@Overridepublic boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {lastSpanX = ScaleGestureDetectorCompat.getCurrentSpanX(scaleGestureDetector);lastSpanY = ScaleGestureDetectorCompat.getCurrentSpanY(scaleGestureDetector);return true;}@Overridepublic boolean onScale(ScaleGestureDetector scaleGestureDetector) {float spanX = ScaleGestureDetectorCompat.getCurrentSpanX(scaleGestureDetector);float spanY = ScaleGestureDetectorCompat.getCurrentSpanY(scaleGestureDetector);float newWidth = lastSpanX / spanX * mCurrentViewport.width();float newHeight = lastSpanY / spanY * mCurrentViewport.height();float focusX = scaleGestureDetector.getFocusX();float focusY = scaleGestureDetector.getFocusY();// Makes sure that the chart point is within the chart region.// See the sample for the implementation of hitTest().hitTest(scaleGestureDetector.getFocusX(),scaleGestureDetector.getFocusY(),viewportFocus);mCurrentViewport.set(viewportFocus.x- newWidth * (focusX - mContentRect.left)/ mContentRect.width(),viewportFocus.y- newHeight * (mContentRect.bottom - focusY)/ mContentRect.height(),0,0);mCurrentViewport.right = mCurrentViewport.left + newWidth;mCurrentViewport.bottom = mCurrentViewport.top + newHeight;...// Invalidates the View to update the display.ViewCompat.postInvalidateOnAnimation(InteractiveLineGraphView.this);lastSpanX = spanX;lastSpanY = spanY;return true;}
};

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

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

相关文章

2021年了,对话系统凉透了吗?

文 | 兔子酱编 | 夕小瑶大家好,我是可盐可甜的兔子酱,一枚卖萌屋的资深潜水小编,今天终于有了自己的第一篇文章,希望耗时一周撰写的本文能让大家有所收获~这篇文章,算是对自己在头部大厂2年算法岗炼丹经历的一个经验浓…

前端可用性保障实践

本文基于已发表在Infoq的“美团收银台前端可用性保障实践”一文编辑而成。 一般可用性都是说后端服务的可用性,都说我们的服务可用性到了几个9,很少有人把可用性放到前端来。其实对于任何一个有UI交互流程的业务,都会有前端服务可用性&#x…

LeetCode 762. 二进制表示中质数个计算置位

1. 题目 给定两个整数 L 和 R ,找到闭区间 [L, R] 范围内,计算置位位数为质数的整数个数。 (注意,计算置位代表二进制表示中1的个数。例如 21 的二进制表示 10101 有 3 个计算置位。还有,1 不是质数。) …

报名通道开启 | 顶会 ICLR 2021:医疗对话生成与自动诊断国际竞赛,邀你来战!...

ICLR,2013 年由深度学习三巨头中的Yoshua Bengio 和 Yann LeCun 牵头创办,已受到研究者和开发者的广泛认可,是当之无愧的深度学习领域顶级会议。今年,由中山大学、加利福尼亚大学圣迭戈分校和腾讯天衍实验室等组织联合举办的医疗对…

论文浅尝 | 图神经网络的对抗攻击和防御相关文献集

本文转载自公众号:专知。作者:Wei Jin。导读:本资源整理了关于图形数据或GNN(图形神经网络)上的对抗攻击和防御的论文链接。并对其进行分类。目录Survey PapersAttack PapersDefense PapersCertified Robustness Papers地址连接:h…

Android官方开发文档Training系列课程中文版:手势处理之ViewGroup的事件管理

原文地址:https://developer.android.com/training/gestures/viewgroup.html 在ViewGroup中处理触摸事件要格外小心,因为在ViewGroup中有很多子View,而这些子View对于不同的触摸事件来说是不同的目标。要确保每个View都正确的接收了相应的触…

李宏毅《机器学习》作业班+带打比赛

人工智能来势汹汹,学习人工智能该从哪里开始呢?人工智能的学习路径又是怎样的?须知入门人工智能第一步就是机器学习。但是,在上千份同学的学习反馈中,我们发现了2个人工智能学习领域的痛难点:1、课程偏理论…

mysql查询时间段内的数据

mysql查询时间段内的数据 -- 今天 select fullName,addedTime from t_user where to_days(addedTime) < to_days(now()); -- 昨天 select fullName,addedTime from t_user where to_days(NOW()) - TO_DAYS(addedTime) < 1; -- 近7天 select fullName,addedTime…

论文浅尝 – KDD2020 | 使用图对比编码的图神经网络预训练模型

论文笔记整理&#xff1a;陈名杨&#xff0c;浙江大学在读博士生&#xff0c;研究方向为知识图谱表示学习。图表示学习是一个当前关注度较高的领域&#xff0c;并且有许多真实的应用。然而当前的很多图表示学习方法都是对一个领域或者某一个图训练一个模型&#xff0c;也就是说…

LeetCode 575. 分糖果(set集合去重)

1. 题目 给定一个偶数长度的数组&#xff0c;其中不同的数字代表着不同种类的糖果&#xff0c;每一个数字代表一个糖果。你需要把这些糖果平均分给一个弟弟和一个妹妹。返回妹妹可以获得的最大糖果的种类数。 输入: candies [1,1,2,2,3,3] 输出: 3 解析: 一共有三种种类的糖…

Android官方开发文档Training系列课程中文版:键盘输入处理之指定输入的类型

原文地址&#xff1a;http://android.xsoftlab.net/training/keyboard-input/index.html 引言 在文本框接收到焦点时&#xff0c;Android系统会在屏幕上显示一个软键盘。为了提供良好的用户体验&#xff0c;你可以指定相关输入类型的特性&#xff0c;以及输入法应当如何展现。…

美团 R 语言数据运营实战

一、引言 近年来&#xff0c;随着分布式数据处理技术的不断革新&#xff0c;Hive、Spark、Kylin、Impala、Presto 等工具不断推陈出新&#xff0c;对大数据集合的计算和存储成为现实&#xff0c;数据仓库/商业分析部门日益成为各类企业和机构的标配。在这种背景下&#xff0c;是…

Android官方开发文档Training系列课程中文版:键盘输入处理之控制输入法的显示方式

原文地址&#xff1a;http://android.xsoftlab.net/training/keyboard-input/visibility.html 当输入的焦点进入或者离开文本框时&#xff0c;Android会适时的显示或隐藏输入法。系统还会决定UI及文本框如何出现在输入法的上方。比如&#xff0c;当垂直方向上的可用空间非常紧…

python用schedule模块实现定时任务

python用schedule模块实现定时任务 import schedule import timedef test():print("Im working...") def test2(): print("Im working... in job2")# 每10分钟执行一次job函数 schedule.every(10).minutes.do(test) # 每10秒执行一次job函数 schedule.…

天天说常识推理,究竟常识是什么?

文 | 花小花Posy写这篇文章的时候&#xff0c;我去搜了搜常识的例子。“睁开眼睛打喷嚏是不可能的。&#xff08;还真没留意。&#xff09;““北极熊是左撇子。“”长颈鹿没办法咳嗽。”呃&#xff1f;好吧&#xff0c;我需要补一补自己的常识。那么这些所谓的“常识”真的是常…

论文笔记 | Counterfactual Samples Synthesizing for Robust VQA

论文笔记整理&#xff1a;窦春柳&#xff0c;天津大学硕士。来源&#xff1a;CVPR 2020链接&#xff1a;https://openaccess.thecvf.com/content_CVPR_2020/papers/Chen_Counterfactual_Samples_Synthesizing_for_Robust_Visual_Question_Answering_CVPR_2020_paper.pdf动机当今…

Android消息总线的演进之路:用LiveDataBus替代RxBus、EventBus

背景 对于Android系统来说&#xff0c;消息传递是最基本的组件&#xff0c;每一个App内的不同页面&#xff0c;不同组件都在进行消息传递。消息传递既可以用于Android四大组件之间的通信&#xff0c;也可用于异步线程和主线程之间的通信。对于Android开发者来说&#xff0c;经常…

Android官方开发文档Training系列课程中文版:键盘输入处理之处理键盘按键

原文地址&#xff1a;http://android.xsoftlab.net/training/keyboard-input/commands.html 当用户将焦点给到可编辑文本的View时&#xff0c;例如EditText这种&#xff0c;并且该设备还拥有实体键盘&#xff0c;那么所有的输入都会被系统处理。然而&#xff0c;如果你希望可以…

MYSQL返回指定时间间隔函数DATE_SUB和TO_DAYS详解

现在北京时间&#xff1a;2021年1月25日 11点 select time,content from shsqsj where time > DATE_SUB(Now(),INTERVAL 2 DAY); 返回的数据是&#xff1a;2021年1月23日23日11点到现在时刻&#xff08;2021年1月25日 11点 &#xff09;的数据。更多请参考 DATE_SUB函数 现…

论文浅尝 - ICLR2020 | 知道什么、如何以及为什么:基于方面的情感分析的近乎完整的解决方案...

论文笔记整理&#xff1a;余海阳&#xff0c;浙江大学硕士&#xff0c;研究方向为知识图谱、自然语言处理。链接&#xff1a;https://arxiv.org/abs/1911.01616动机基于目标的情感分析或基于方面的情感分析&#xff08;ABSA&#xff09;是指在细粒度的层次上解决各种情感分析任…