热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【Touchinput】为滚动手势添加动画(6)

原在Android中,滚动通常通过使用ScrollView该类来实现。任何可能超出其容器边界的标准布局应该嵌套在a中ScrollView以提供由框架管理的可滚动视图。

在Android中,滚动通常通过使用ScrollView 该类来实现 。任何可能超出其容器边界的标准布局应该嵌套在a中ScrollView以提供由框架管理的可滚动视图。只有在特殊情况下才需要实现自定义滚动条。本课描述了这样一种场景:显示滚动效果以响应使用滚动条的触摸手势。

您可以使用滚动条(Scroller或OverScroller)收集产生滚动动画以响应触摸事件所需的数据。它们是相似的,但 OverScroller 包括用于向用户指示他们已经在泛或手动手势之后到达内容边缘的方法。当用户到达内容边缘时,InteractiveChart示例使用EdgeEffect类(实际上是EdgeEffectCompat类)来显示“发光”效果。

注意:我们建议您使用OverScroller而不是Scroller滚动动画。 OverScroller提供与旧设备的最佳后向兼容性。
另外请注意,在实现自动滚动时,您通常只需要使用滚动条。ScrollView而 HorizontalScrollView如果你窝在其中布局做这一切×××。

使用平台标准的滚动物理(摩擦,速度等),滚动器用于随着时间的推移进行动画滚动。滚动器本身并不实际绘制任何东西。随着时间的推移,滚动器会为您追踪滚动偏移量,但它们不会自动将这些位置应用于您的视图。您有责任按照使滚动动画看起来平滑的速率获取和应用新的坐标。

请参阅以下相关资源:

  • 输入事件 API指南
  • 传感器概述
  • 使视图互动

了解滚动术语


“滚动”是一个可以在Android中采用不同含义的词,具体取决于上下文。

滚动是移动视口的一般过程(也就是您正在查看的内容的“窗口”)。当滚动在x轴和y轴上时,称为 平移。随该类提供的示例应用程序InteractiveChart演示了两种不同类型的滚动,拖动和拖动:

  • 拖动是用户在触摸屏上拖动手指时发生的滚动类型。简单的拖动往往是通过覆盖来实现 onScroll()在 GestureDetector.OnGestureListener。有关拖动的更多讨论,请参阅 拖动和缩放。
  • 甩甩是用户快速拖动手指时发生的滚动类型。用户抬起手指后,通常需要继续滚动(移动视口),但是会减速直到视口停止移动。投掷可以通过重写来实现 onFling() 在GestureDetector.OnGestureListener,并且通过使用滚动条对象。这是本课的主题。

通常将滚动条对象与滑动手势结合使用,但它们可以用于任何需要UI显示滚动以响应触摸事件的上下文中。例如,您可以重写 onTouchEvent()以直接处理触摸事件,并产生滚动效果或响应这些触摸事件的“贴紧页面”动画。

实现基于触摸的滚动


本节介绍如何使用滚动条。下面显示的代码片段来自InteractiveChart本课程提供的示例。它使用a GestureDetector,并覆盖该 GestureDetector.SimpleOnGestureListener方法 onFling()。它OverScroller用来跟踪扔手势。如果用户在投掷手势后到达内容边缘,则应用程序会显示“发光”效果。

:该InteractiveChart示例应用程序显示一个图表,你可以缩放,平移,滚动,等等。在以下代码片段中, mContentRect表示图表将被绘制到的视图内的矩形坐标。在任何给定时间,整个图表域和范围的一个子集被绘制到这个矩形区域中。 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的值。这通常是在视图孩子使用滚动器对象来动画滚动时完成的,如本例所示。

大多数视图直接传递滚动条对象的x和y位置 scrollTo()。下面的实现computeScroll() 采用不同的方法 - 它调用 computeScrollOffset()获取x和y的当前位置。当满足显示过度滚动“发光”边缘效果的条件(显示放大,x或y超出范围,并且应用程序尚未显示过度滚动)时,代码将设置超滚动发光效果并调用 postInvalidateOnAnimation() 来触发视图上的无效:

// 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 &#61; false;// The scroller isn&#39;t finished, meaning a fling or programmatic pan// operation is currently active.if (mScroller.computeScrollOffset()) {Point surfaceSize &#61; computeScrollSurfaceSize();int currX &#61; mScroller.getCurrX();int currY &#61; mScroller.getCurrY();boolean canScrollX &#61; (mCurrentViewport.left > AXIS_X_MIN|| mCurrentViewport.right AXIS_Y_MIN|| mCurrentViewport.bottom (surfaceSize.x - mContentRect.width())&& mEdgeEffectRight.isFinished()&& !mEdgeEffectRightActive) {mEdgeEffectRight.onAbsorb((int)OverScrollerCompat.getCurrVelocity(mScroller));mEdgeEffectRightActive &#61; true;needsInvalidate &#61; true;}if (canScrollY&& currY <0&& mEdgeEffectTop.isFinished()&& !mEdgeEffectTopActive) {mEdgeEffectTop.onAbsorb((int)OverScrollerCompat.getCurrVelocity(mScroller));mEdgeEffectTopActive &#61; true;needsInvalidate &#61; true;} else if (canScrollY&& currY > (surfaceSize.y - mContentRect.height())&& mEdgeEffectBottom.isFinished()&& !mEdgeEffectBottomActive) {mEdgeEffectBottom.onAbsorb((int)OverScrollerCompat.getCurrVelocity(mScroller));mEdgeEffectBottomActive &#61; true;needsInvalidate &#61; true;}...}

以下是执行实际缩放的代码部分&#xff1a;

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

这是computeScrollSurfaceSize()在上面的代码段中调用的方法。它以像素为单位计算当前可滚动表面大小。例如&#xff0c;如果整个图表区域都可见&#xff0c;则这只是当前的大小mContentRect。如果图表在两个方向都放大了200&#xff05;&#xff0c;则返回的尺寸将是水平和垂直两倍。

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()));
}

对于卷轴使用方法的另一示例&#xff0c;请参见 源代码的 ViewPager类。它响应甩尾而滚动&#xff0c;并使用滚动来实现“贴合到页面”动画。

Lastest Update&#xff1a;2018.04.25

联系我

QQ:94297366
微信打赏&#xff1a;https://pan.baidu.com/s/1dSBXk3eFZu3mAMkw3xu9KQ

公众号推荐&#xff1a;

【Touch&input 】为滚动手势添加动画&#xff08;6&#xff09;

转:https://blog.51cto.com/4789781/2124894



推荐阅读
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • Android实战——jsoup实现网络爬虫,糗事百科项目的起步
    本文介绍了Android实战中使用jsoup实现网络爬虫的方法,以糗事百科项目为例。对于初学者来说,数据源的缺乏是做项目的最大烦恼之一。本文讲述了如何使用网络爬虫获取数据,并以糗事百科作为练手项目。同时,提到了使用jsoup需要结合前端基础知识,以及如果学过JS的话可以更轻松地使用该框架。 ... [详细]
  • Android日历提醒软件开源项目分享及使用教程
    本文介绍了一款名为Android日历提醒软件的开源项目,作者分享了该项目的代码和使用教程,并提供了GitHub项目地址。文章详细介绍了该软件的主界面风格、日程信息的分类查看功能,以及添加日程提醒和查看详情的界面。同时,作者还提醒了读者在使用过程中可能遇到的Android6.0权限问题,并提供了解决方法。 ... [详细]
  • 本文介绍了H5游戏性能优化和调试技巧,包括从问题表象出发进行优化、排除外部问题导致的卡顿、帧率设定、减少drawcall的方法、UI优化和图集渲染等八个理念。对于游戏程序员来说,解决游戏性能问题是一个关键的任务,本文提供了一些有用的参考价值。摘要长度为183字。 ... [详细]
  • 本文介绍了Python函数的定义与调用的方法,以及函数的作用,包括增强代码的可读性和重用性。文章详细解释了函数的定义与调用的语法和规则,以及函数的参数和返回值的用法。同时,还介绍了函数返回值的多种情况和多个值的返回方式。通过学习本文,读者可以更好地理解和使用Python函数,提高代码的可读性和重用性。 ... [详细]
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
  • 本文介绍了贝叶斯垃圾邮件分类的机器学习代码,代码来源于https://www.cnblogs.com/huangyc/p/10327209.html,并对代码进行了简介。朴素贝叶斯分类器训练函数包括求p(Ci)和基于词汇表的p(w|Ci)。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文介绍了在Vue项目中如何结合Element UI解决连续上传多张图片及图片编辑的问题。作者强调了在编码前要明确需求和所需要的结果,并详细描述了自己的代码实现过程。 ... [详细]
  • 在开发app时,使用了butterknife后,在androidStudio打包apk时可能会遇到报错。为了解决这个问题,可以通过打开proguard-rules.pro文件进行代码混淆来解决。本文介绍了具体的混淆代码和方法。 ... [详细]
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • Python的参数解析argparse模块的学习
    本文介绍了Python中参数解析的重要模块argparse的学习内容。包括位置参数和可选参数的定义和使用方式,以及add_argument()函数的详细参数关键字解释。同时还介绍了命令行参数的操作和可接受数量的设置,其中包括整数类型的参数。通过学习本文内容,可以更好地理解和使用argparse模块进行参数解析。 ... [详细]
author-avatar
瞄瞄摩卡李流
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有