手势识别官方教程(4)在挑划或拖动手势后view的滚动用ScrollView和 HorizontalScrollView,自定义用Scroller或OverScroller
简单滚动用ScrollView和 HorizontalScrollView就够。自定义view时可能要自定义滚动效果,可以使用 Scroller或 OverScroller
Animating a Scroll Gesture
In Android, scrolling is typically achieved by using the ScrollView
class. Any standard layout that might extend beyond the bounds of its container should be nested in a ScrollView
to provide a scrollable view that's managed by the framework. Implementing a custom scroller should only be necessary for special scenarios. This lesson describes such a scenario: displaying a scrolling effect in response to touch gestures using scrollers.
You can use scrollers (Scroller
or OverScroller
) to collect the data you need to produce a scrolling animation in response to a touch event. They are similar, but OverScroller
includes methods for indicating to users that they've reached the content edges after a pan or fling gesture.
OverScroller包含滚动到边时的动画,而 Scroller 没有,推荐使用 OverScroller
The InteractiveChart
sample uses the EdgeEffect
class (actually the EdgeEffectCompat
class) to display a "glow" effect when users reach the content edges.
Note: We recommend that you use OverScroller
rather than Scroller
for scrolling animations. OverScroller
provides the best backward compatibility with older devices.
Also note that you generally only need to use scrollers when implementing scrolling yourself. ScrollView
and HorizontalScrollView
do all of this for you if you nest your layout within them.
通常如果只要简单滚动效果,那么 ScrollView 和 HorizontalScrollView 就够用了。
A scroller is used to animate scrolling over time, using platform-standard scrolling physics (friction, velocity, etc.). The scroller itself doesn't actually draw anything. Scrollers track scroll offsets for you over time, but they don't automatically apply those positions to your view. It's your responsibility to get and apply new coordinates at a rate that will make the scrolling animation look smooth.
滚动器只负责根据速度,摩擦等记录滚动的偏移量,并不绘制,绘制和刷新坐标是使用者的责任。
Understand Scrolling Terminology (滚动的定义)
"Scrolling" is a word that can take on different meanings in Android, depending on the context.
Scrolling is the general process of moving the viewport (that is, the 'window' of content you're looking at). When scrolling is in both the x and y axes, it's called panning. The sample application provided with this class,InteractiveChart
, illustrates two different types of scrolling, dragging and flinging:
滚动的定义在不同上下文中,含义不同,但都是在其它手势之后才产生。如下:
- Dragging is the type of scrolling that occurs when a user drags her finger across the touch screen. Simple dragging is often implemented by overriding
onScroll()
inGestureDetector.OnGestureListener
. For more discussion of dragging, see Dragging and Scaling.在拖拽手势后:分按住一点一起拖动直到停止才松手,GestureDetector.OnGestureListener的onScroll()中处理 。
- Flinging is the type of scrolling that occurs when a user drags and lifts her finger quickly. After the user lifts her finger, you generally want to keep scrolling (moving the viewport), but decelerate until the viewport stops moving. Flinging can be implemented by overriding
onFling()
inGestureDetector.OnGestureListener
, and by using a scroller object. This is the use case that is the topic of this lesson.在挑划手势后:按住一点划下就松手,GestureDetector.OnGestureListener的onFling()中处理。
It's common to use scroller objects in conjunction with a fling gesture, but they can be used in pretty much any context where you want the UI to display scrolling in response to a touch event. For example, you could override onTouchEvent()
to process touch events directly, and produce a scrolling effect or a "snapping to page" animation in response to those touch events.
Implement Touch-Based Scrolling
This section describes how to use a scroller. The snippet shown below comes from the InteractiveChart
sample provided with this class. It uses a GestureDetector
, and overrides the GestureDetector.SimpleOnGestureListener
method onFling()
. It uses OverScroller
to track the fling gesture. If the user reaches the content edges after the fling gesture, the app displays a "glow" effect.
Note: The InteractiveChart
sample app displays a chart that you can zoom, pan, scroll, and so on. In the following snippet, mContentRect
represents the rectangle coordinates within the view that the chart will be drawn into. At any given time, a subset of the total chart domain and range are drawn into this rectangular area. mCurrentViewport
represents the portion of the chart that is currently visible in the screen. Because pixel offsets are generally treated as integers, mContentRect
is of the type Rect
. Because the graph domain and range are decimal/float values, mCurrentViewport
is of the type RectF
.
The first part of the snippet shows the implementation of onFling()
:
下面是一个在挑划手势后有动画的滚动处理示例(OverScroller)
// 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() {
@Override
public 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;
}
...
@Override
public 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 animation
mScroller.fling(
// Current scroll position
startX,
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.
*/
, surfaceSize.x - mContentRect.width(),
, surfaceSize.y - mContentRect.height(),
// The edges of the content. This comes into play when using
// the EdgeEffect class to draw "glow" overlays.
mContentRect.width() / ,
mContentRect.height() / );
// Invalidates to trigger computeScroll()
ViewCompat.postInvalidateOnAnimation(this);
}
When onFling()
calls postInvalidateOnAnimation()
, it triggers computeScroll()
to update the values for x and y. This is typically be done when a view child is animating a scroll using a scroller object, as in this example.
Most views pass the scroller object's x and y position directly to scrollTo()
. The following implementation of computeScroll()
takes a different approach—it calls computeScrollOffset()
to get the current location of x and y. When the criteria for displaying an overscroll "glow" edge effect are met (the display is zoomed in, x or y is out of bounds, and the app isn't already showing an overscroll), the code sets up the overscroll glow effect and calls postInvalidateOnAnimation()
to trigger an invalidate on the 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 <
&& 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 <
&& 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;
}
...
}
Here is the section of the code that performs the actual zoom:
// 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 * ( - pointWithinViewportX),
mZoomFocalPoint.y + newHeight * ( - pointWithinViewportY));
constrainViewport();
needsInvalidate = true;
}
if (needsInvalidate) {
ViewCompat.postInvalidateOnAnimation(this);
}
This is the computeScrollSurfaceSize()
method that's called in the above snippet. It computes the current scrollable surface size, in pixels. For example, if the entire chart area is visible, this is simply the current size of mContentRect
. If the chart is zoomed in 200% in both directions, the returned size will be twice as large horizontally and vertically.
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()));
}
For another example of scroller usage, see the source code for the ViewPager
class. It scrolls in response to flings, and uses scrolling to implement the "snapping to page" animation.
手势识别官方教程(4)在挑划或拖动手势后view的滚动用ScrollView和 HorizontalScrollView,自定义用Scroller或OverScroller的更多相关文章
- 手势识别官方教程(2)识别常见手势用GestureDetector+手势回调接口/手势抽象类
简介 GestureDetector识别手势. GestureDetector.OnGestureListener是识别手势后的回调接口.GestureDetector.SimpleOnGesture ...
- 手势识别官方教程(7)识别缩放手势用ScaleGestureDetector.GestureDetector和ScaleGestureDetector.SimpleOnScaleGestureListener
Use Touch to Perform Scaling As discussed in Detecting Common Gestures, GestureDetector helps you de ...
- 手势识别官方教程(3)识别移动手势(识别速度用VelocityTracker)
moving手势在onTouchEvent()或onTouch()中就可识别,编程时主要是识别积云的速度用VelocityTracker等, Tracking Movement This lesson ...
- 手势识别官方教程(7)识别缩放手势用ScaleGestureDetector和SimpleOnScaleGestureListener
1.Use Touch to Perform Scaling As discussed in Detecting Common Gestures, GestureDetector helps you ...
- 手势识别官方教程(8)拦截触摸事件,得到触摸的属性如速度,距离等,控制view展开
onInterceptTouchEvent可在onTouchEvent()前拦截触摸事件, ViewConfiguration得到触摸的属性如速度,距离等, TouchDelegate控制view展开 ...
- 手势识别官方教程(6)识别拖拽手势用GestureDetector.SimpleOnGestureListener和onTouchEvent
三种现实drag方式 1,在3.0以后可以直接用 View.OnDragListener (在onTouchEvent中调用某个view的startDrag()) 2,onTouchEvent() ...
- Ceisum官方教程2 -- 项目实例(workshop)
原文地址:https://cesiumjs.org/tutorials/Cesium-Workshop/ 概述 我们很高兴欢迎你加入Cesium社区!为了让你能基于Cesium开发自己的3d 地图项目 ...
- Unity性能优化(3)-官方教程Optimizing garbage collection in Unity games翻译
本文是Unity官方教程,性能优化系列的第三篇<Optimizing garbage collection in Unity games>的翻译. 相关文章: Unity性能优化(1)-官 ...
- Unity性能优化(4)-官方教程Optimizing graphics rendering in Unity games翻译
本文是Unity官方教程,性能优化系列的第四篇<Optimizing graphics rendering in Unity games>的翻译. 相关文章: Unity性能优化(1)-官 ...
随机推荐
- Podfile 文件的编写
# Uncomment this line to define a global platform for your projectplatform :ios, '9.0' target 'Cocoa ...
- [Effective Java读书笔记] 第二章 创建和销毁对象(1~7)
我的技术博客经常被流氓网站恶意爬取转载.请移步原文:http://www.cnblogs.com/hamhog/p/3537576.html,享受整齐的排版.有效的链接.正确的代码缩进.更好的阅读体验 ...
- 【译】velocity
本文原文地址:http://davidwalsh.name/intro-javascript-animation 就像许多开发者确信的那样,在Web上使用CSS实现动画并不是唯一的方式,我们也可以使用 ...
- Linux负载均衡概念与实践(一)
根据网上文章整理. 负载均衡软件LVS(Linux Virtual Server)概念篇 lvs是在linux操作系统基础上建立虚拟服务器,实现服务节点之间的负载均衡.它是基于linux内核实现的.2 ...
- ECMAScript布尔操作符
在ECMAScript中提供了Boolean()转换函数以及三个布尔操作符,这三个布尔操作符分别为逻辑非.逻辑与.逻辑或,这三个操作符通常用作于某些值的求反,比较模式等.学好这一点知识也非常的重要,奠 ...
- Linux C 程序 函数,数组,指针,gdb调试器(SEVEN)
函数,数组,指针,gdb调试器 1.函数定义 如果明确指定返回类型,默认为int 参数传递:实参对形参的参数传递是单向的,实参只是把自己的值赋给形参. 形参的 ...
- Android AndroidManifest学习笔记
<application>标签 : android:allowBackup="true" 数据可以备份 <activity>标签:configChanges ...
- c#抽象工厂类
抽象工厂类的结构如下: 工厂 a=new 1工厂 抽象类A aa=a.createA() aa.create()==类A1.create() 抽象类B bb=a.createB() bb.get()= ...
- SQL 字符替换
--匹配所有字符替换 )),'被替换','替换') --匹配给定位子替换 update 表名 set 列=stuff(列名,从一开始数位数,往后数几位,替换)
- php 中文字符串首字母的获取函数
这篇文章介绍了php 中文字符串首字母的获取函数,有需要的朋友可以参考一下 function chineseFirst($str) { $str= iconv("UTF-8",&q ...