Android官方开发文档Training系列课程中文版:手势处理之滚动动画及Scroller

原文地址:http://android.xsoftlab.net/training/gestures/scroll.html

在Android中,滑动经常由ScrollView类来实现。任何超出容器边界的布局都应该将自己内嵌在ScrollView中,以便提供可滚动的视图效果。自定义滚动只有在特定的场景下才会被用到。这节课将会描述这样一种场景:使用scroller显示一种可滚动的效果。

你可以使用Scroller或者OverScroller来收集一些滑动动画所需要的数据。这两个类很相似,但是OverScroller包含了一些用于指示已经到达布局边界的方法。示例InteractiveChart在这里使用了类EdgeEffect(实际上是类EdgeEffectCompat),在用户到达布局边界时会显示一种”glow“的效果。

Note: 我们推荐使用OverScroller。这个类提供了良好的向后兼容性。还应该注意,通常只有在实现滑动自己内部的时候,才需要使用到scroller类。如果你将布局嵌入到ScrollView或HorizontalScrollView中的话,那么它们会将滚动的相关事宜做好。

Scroller用于在时间轴上做动画滚动效果,它使用了标准的滚动物理学(摩擦力、速度等等)。Scroller本身不会绘制任何东西。Scroller会随着时间的变化追踪移动的偏移量,但是它们不会自动的将这些值应用在你的View上。你需要自己获取这些值并使用,这样才会使滑动的效果更流畅。

了解滑动术语

“Scrolling”这个词在Android中可被翻译为各种不同的意思,这取决于具体的上下文。

Scrolling是一种viewport(viewport的意思是,你所看到的内容的窗口)移动的通用处理过程。当scrolling处于x轴及y轴上时,这被称为“平移(panning)”。示例程序提供了相关的类:InteractiveChart,演示了滑动的两种不同类型,dragging(拖动)及flinging(滑动):

  • Dragging 该滚动类型在这种情况下发生:当用户的手指在屏幕上来回滑动时。简单的Dragging由GestureDetector.OnGestureListener接口的onScroll()方法实现。
  • Flinging 该滚动类型在这种情况下发生:当用户快速在屏幕上滑动并离开屏幕时。在用户离开了屏幕之后,常理上应该保持滚动状态,并随之慢慢减速,直到viewport停止移动。Flinging由GestureDetector.OnGestureListener接口的onFling()方法及scroller对象所实现。

将scroller对象与滑动手势结合使用是一种共通的方法。你可以重写onTouchEvent()方法直接处理触摸事件,并降低滑动的速度来响应相应的触摸事件。

实现基础滚动

这部分章节将会描述如何使用scroller。下面的代码段摘自示例应用InteractiveChart。它在这里使用了一个GestureDetector对象,重写了GestureDetector.SimpleOnGestureListener的onFling()方法。它使用了OverScroller来追踪滑动中的手势。如果用户在滑动之后到达了内容的边缘,那么APP会显示一个”glow”的效果。

Note: 示例APP InteractiveChart 展示了一个图表,这个图标可以缩放、平移、滚动等等。在下面的代码段中,mContentRect代表了矩形的坐标点,而绘制图表的View则居于其中。在给定的时间内,一个总表的子集将会被绘制到这块区域内。mCurrentViewport代表了屏幕中当前可视的部分图表。因为像素的偏移量通常被当做整型,所以mContentRect的类型是Rect。因为图形的数据范围是小数类型,所以mCurrentViewport的类型是RectF。

代码段的第一部分展示了onFling()方法的实现:

// The current viewport. This rectangle represents the currently visible 
// chart domain and range. The viewport is the part of the app that the
// user manipulates via touch gestures.
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 OverScroller mScroller;
private RectF mScrollerStartViewport;
...
private final GestureDetector.SimpleOnGestureListener mGestureListener= new GestureDetector.SimpleOnGestureListener() {@Overridepublic boolean onDown(MotionEvent e) {// Initiates the decay phase of any active edge effects.releaseEdgeEffects();mScrollerStartViewport.set(mCurrentViewport);// Aborts any active scroll animations and invalidates.mScroller.forceFinished(true);ViewCompat.postInvalidateOnAnimation(InteractiveLineGraphView.this);return true;}...@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {fling((int) -velocityX, (int) -velocityY);return true;}
};
private void fling(int velocityX, int velocityY) {// Initiates the decay phase of any active edge effects.releaseEdgeEffects();// Flings use math in pixels (as opposed to math based on the viewport).Point surfaceSize = computeScrollSurfaceSize();mScrollerStartViewport.set(mCurrentViewport);int startX = (int) (surfaceSize.x * (mScrollerStartViewport.left - AXIS_X_MIN) / (AXIS_X_MAX - AXIS_X_MIN));int startY = (int) (surfaceSize.y * (AXIS_Y_MAX - mScrollerStartViewport.bottom) / (AXIS_Y_MAX - AXIS_Y_MIN));// Before flinging, aborts the current animation.mScroller.forceFinished(true);// Begins the animationmScroller.fling(// Current scroll positionstartX,startY,velocityX,velocityY,/** Minimum and maximum scroll positions. The minimum scroll * position is generally zero and the maximum scroll position * is generally the content size less the screen size. So if the * content width is 1000 pixels and the screen width is 200  * pixels, the maximum scroll offset should be 800 pixels.*/0, surfaceSize.x - mContentRect.width(),0, surfaceSize.y - mContentRect.height(),// The edges of the content. This comes into play when using// the EdgeEffect class to draw "glow" overlays.mContentRect.width() / 2,mContentRect.height() / 2);// Invalidates to trigger computeScroll()ViewCompat.postInvalidateOnAnimation(this);
}

当onFling()方法调用postInvalidateOnAnimation()方法时,它会调用computeScroll()来更新x及y的值。

大多数View会将scroller对象的x及y的属性值直接设置给方法scrollTo()。而下面的computeScroll()则采用了不同的方法:它调用computeScrollOffset()来获取x及y的当前坐标。当显示的区域到达边界时,这里就会展示一个”glow”的效果,代码会设置一个越过边缘的效果,并会调用postInvalidateOnAnimation()来使View重新绘制。

// Edge effect / overscroll tracking objects.
private EdgeEffectCompat mEdgeEffectTop;
private EdgeEffectCompat mEdgeEffectBottom;
private EdgeEffectCompat mEdgeEffectLeft;
private EdgeEffectCompat mEdgeEffectRight;
private boolean mEdgeEffectTopActive;
private boolean mEdgeEffectBottomActive;
private boolean mEdgeEffectLeftActive;
private boolean mEdgeEffectRightActive;
@Override
public void computeScroll() {super.computeScroll();boolean needsInvalidate = false;// The scroller isn't finished, meaning a fling or programmatic pan // operation is currently active.if (mScroller.computeScrollOffset()) {Point surfaceSize = computeScrollSurfaceSize();int currX = mScroller.getCurrX();int currY = mScroller.getCurrY();boolean canScrollX = (mCurrentViewport.left > AXIS_X_MIN|| mCurrentViewport.right < AXIS_X_MAX);boolean canScrollY = (mCurrentViewport.top > AXIS_Y_MIN|| mCurrentViewport.bottom < AXIS_Y_MAX);/*          * If you are zoomed in and currX or currY is* outside of bounds and you're not already* showing overscroll, then render the overscroll* glow edge effect.*/if (canScrollX&& currX < 0&& mEdgeEffectLeft.isFinished()&& !mEdgeEffectLeftActive) {mEdgeEffectLeft.onAbsorb((int) OverScrollerCompat.getCurrVelocity(mScroller));mEdgeEffectLeftActive = true;needsInvalidate = true;} else if (canScrollX&& currX > (surfaceSize.x - mContentRect.width())&& mEdgeEffectRight.isFinished()&& !mEdgeEffectRightActive) {mEdgeEffectRight.onAbsorb((int) OverScrollerCompat.getCurrVelocity(mScroller));mEdgeEffectRightActive = true;needsInvalidate = true;}if (canScrollY&& currY < 0&& mEdgeEffectTop.isFinished()&& !mEdgeEffectTopActive) {mEdgeEffectTop.onAbsorb((int) OverScrollerCompat.getCurrVelocity(mScroller));mEdgeEffectTopActive = true;needsInvalidate = true;} else if (canScrollY&& currY > (surfaceSize.y - mContentRect.height())&& mEdgeEffectBottom.isFinished()&& !mEdgeEffectBottomActive) {mEdgeEffectBottom.onAbsorb((int) OverScrollerCompat.getCurrVelocity(mScroller));mEdgeEffectBottomActive = true;needsInvalidate = true;}...}

下面是执行实际放大的代码:

// Custom object that is functionally similar to Scroller
Zoomer mZoomer;
private PointF mZoomFocalPoint = new PointF();
...
// If a zoom is in progress (either programmatically or via double
// touch), performs the zoom.
if (mZoomer.computeZoom()) {float newWidth = (1f - mZoomer.getCurrZoom()) * mScrollerStartViewport.width();float newHeight = (1f - mZoomer.getCurrZoom()) * mScrollerStartViewport.height();float pointWithinViewportX = (mZoomFocalPoint.x - mScrollerStartViewport.left)/ mScrollerStartViewport.width();float pointWithinViewportY = (mZoomFocalPoint.y - mScrollerStartViewport.top)/ mScrollerStartViewport.height();mCurrentViewport.set(mZoomFocalPoint.x - newWidth * pointWithinViewportX,mZoomFocalPoint.y - newHeight * pointWithinViewportY,mZoomFocalPoint.x + newWidth * (1 - pointWithinViewportX),mZoomFocalPoint.y + newHeight * (1 - pointWithinViewportY));constrainViewport();needsInvalidate = true;
}
if (needsInvalidate) {ViewCompat.postInvalidateOnAnimation(this);
}

下面是computeScrollSurfaceSize()方法的内容。它计算了当前可滑动的界面的尺寸,以像素为单位。举个例子,如果整个图表区域是可见的,那么它的值就等于mContentRect。如果图标被放大了200%,那么返回的值就是水平及垂直方向值的两倍。

private Point computeScrollSurfaceSize() {return new Point((int) (mContentRect.width() * (AXIS_X_MAX - AXIS_X_MIN)/ mCurrentViewport.width()),(int) (mContentRect.height() * (AXIS_Y_MAX - AXIS_Y_MIN)/ mCurrentViewport.height()));
}

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

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

相关文章

美团在O2O场景下的广告营销

美团作为中国最大的在线本地生活服务平台&#xff0c;覆盖了餐饮、酒店、旅行、休闲娱乐、外卖配送等方方面面生活场景&#xff0c;连接了数亿用户和数百万商户。如何帮助本地商户开展在线营销&#xff0c;使得他们能快速有效地触达目标用户群体提升经营效率&#xff0c;是美团…

LeetCode 202. 快乐数(快慢指针)

1. 题目 2. 解题 一个数经过若干次各位数平方和后&#xff0c;会等于它自己使用类似环形链表的快慢指针法&#xff0c;最终快慢指针相遇&#xff0c;若不为1则是不快乐数 class Solution { public:int bitSquareSum(int n) {int sum 0, bit;while(n > 0){bit n % 10;su…

全栈深度学习第4期: 机器学习岗位区别与团队管理

一起追剧鸭简介Berkeley全栈深度学习追剧计划是由夕小瑶的卖萌屋发起的优质公开课打卡项目&#xff0c;通过微信群为同期追剧的小伙伴提供交流平台。关于该计划的详请见这里。Berkeley深度学习追剧群目前已有1000小伙伴加入&#xff0c;公众号后台回复口令 深度学习追剧 入群。…

会议交流 | DataFunCon 线上大会 - 知识图谱专题论坛

OpenKG开放知识图谱&#xff08;简称 OpenKG&#xff09;旨在促进中文知识图谱数据的开放与互联&#xff0c;促进知识图谱和语义技术的普及和广泛应用。点击阅读原文&#xff0c;进入 OpenKG 博客。

Android官方开发文档Training系列课程中文版:手势处理之多点触控处理

原文地址&#xff1a;http://android.xsoftlab.net/training/gestures/multi.html 多点触控是指多个手指同时触摸屏幕的情况。这节课主要学习如何检测多点触控手势。 记录多个触控点 当多根手指同时触碰到屏幕时&#xff0c;系统会产生以下触摸事件&#xff1a; ACTION_DOW…

我国政务大数据政策的文本分析:推进逻辑与未来进路

原文地址&#xff1a;https://www.sohu.com/a/238844423_99983415 摘要&#xff1a;[目的/意义]从已颁布政策文件中找出我国推进政务大数据发展和应用的内在逻辑, 为优化未来的政策路径提供对策建议。[方法/过程]通过政府门户网站收集189条有效政策文本, 综合运用词频分析软件…

Flutter原理与实践

Flutter是Google开发的一套全新的跨平台、开源UI框架&#xff0c;支持iOS、Android系统开发&#xff0c;并且是未来新操作系统Fuchsia的默认开发套件。自从2017年5月发布第一个版本以来&#xff0c;目前Flutter已经发布了近60个版本&#xff0c;并且在2018年5月发布了第一个“R…

LeetCode 522. 最长特殊序列 II

1. 题目 给定字符串列表&#xff0c;你需要从它们中找出最长的特殊序列。 最长特殊序列定义如下&#xff1a;该序列为某字符串独有的最长子序列&#xff08;即不能是其他字符串的子序列&#xff09;。 子序列可以通过删去字符串中的某些字符实现&#xff0c;但不能改变剩余字…

论文浅尝 - 计算机工程 | 大规模企业级知识图谱实践综述

本文转载自公众号&#xff1a;计算机工程。大规模企业级知识图谱实践综述王昊奋, 丁军, 胡芳槐, 王鑫中文摘要&#xff1a;近年来&#xff0c;知识图谱及其相关技术得到快速发展&#xff0c;并被广泛应用于工业界各种认知智能场景中。在简述知识图谱相关研究的基础上&#xff0…

python实现拆分、合并、删除pdf

PDF&#xff08;Portable Document Format&#xff09;&#xff0c;中文名称便携文档格式是我们经常会接触到的一种文件格式&#xff0c;文献、文档…很多都是PDF格式。它以格式稳定的优势&#xff0c;使得我们在打印、分享、传输过程中能够最优的保持原有色彩和格式。PDF是以P…

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

原文地址&#xff1a;https://developer.android.com/training/gestures/scale.html 这节课主要学习如何使用触摸手势来拖动、放大屏幕上的对象。 拖动对象 如果你的重点在Android 3.0以上的版本&#xff0c;那么你可以使用内置的拖拽事件监听器View.OnDragListener。 触摸手…

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

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

前端可用性保障实践

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

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

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

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

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

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

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

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

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

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

人工智能来势汹汹&#xff0c;学习人工智能该从哪里开始呢&#xff1f;人工智能的学习路径又是怎样的&#xff1f;须知入门人工智能第一步就是机器学习。但是&#xff0c;在上千份同学的学习反馈中&#xff0c;我们发现了2个人工智能学习领域的痛难点&#xff1a;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;也就是说…