am e23f97ba: Doc change: updates to Gestures training class.
* commit 'e23f97ba6ee5214679fff61e33deda075cfd741d': Doc change: updates to Gestures training class.
This commit is contained in:
BIN
docs/downloads/training/InteractiveChart.zip
Normal file
BIN
docs/downloads/training/InteractiveChart.zip
Normal file
Binary file not shown.
@@ -25,12 +25,18 @@ next.link=movement.html
|
|||||||
<li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
|
<li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
|
||||||
</li>
|
</li>
|
||||||
<li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
|
<li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
|
||||||
<li><a href="http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html">Making Sense of Multitouch</a> blog post</li>
|
|
||||||
<li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
|
<li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
|
||||||
<li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
|
<li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
|
||||||
<li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
|
<li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<h2>Try it out</h2>
|
||||||
|
|
||||||
|
<div class="download-box">
|
||||||
|
<a href="{@docRoot}shareables/training/InteractiveChart.zip"
|
||||||
|
class="button">Download the sample</a>
|
||||||
|
<p class="filename">InteractiveChart.zip</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,12 +20,18 @@ next.link=detector.html
|
|||||||
<li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
|
<li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
|
||||||
</li>
|
</li>
|
||||||
<li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
|
<li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
|
||||||
<li><a href="http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html">Making Sense of Multitouch</a> blog post</li>
|
|
||||||
<li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
|
<li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
|
||||||
<li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
|
<li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
|
||||||
<li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
|
<li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<h2>Try it out</h2>
|
||||||
|
|
||||||
|
<div class="download-box">
|
||||||
|
<a href="{@docRoot}shareables/training/InteractiveChart.zip"
|
||||||
|
class="button">Download the sample</a>
|
||||||
|
<p class="filename">InteractiveChart.zip</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -24,12 +24,18 @@ next.link=scroll.html
|
|||||||
<li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
|
<li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
|
||||||
</li>
|
</li>
|
||||||
<li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
|
<li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
|
||||||
<li><a href="http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html">Making Sense of Multitouch</a> blog post</li>
|
|
||||||
<li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
|
<li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
|
||||||
<li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
|
<li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
|
||||||
<li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
|
<li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<h2>Try it out</h2>
|
||||||
|
|
||||||
|
<div class="download-box">
|
||||||
|
<a href="{@docRoot}shareables/training/InteractiveChart.zip"
|
||||||
|
class="button">Download the sample</a>
|
||||||
|
<p class="filename">InteractiveChart.zip</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -25,12 +25,18 @@ next.link=scale.html
|
|||||||
<li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
|
<li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
|
||||||
</li>
|
</li>
|
||||||
<li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
|
<li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
|
||||||
<li><a href="http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html">Making Sense of Multitouch</a> blog post</li>
|
|
||||||
<li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
|
<li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
|
||||||
<li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
|
<li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
|
||||||
<li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
|
<li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<h2>Try it out</h2>
|
||||||
|
|
||||||
|
<div class="download-box">
|
||||||
|
<a href="{@docRoot}shareables/training/InteractiveChart.zip"
|
||||||
|
class="button">Download the sample</a>
|
||||||
|
<p class="filename">InteractiveChart.zip</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ next.link=viewgroup.html
|
|||||||
<h2>This lesson teaches you to</h2>
|
<h2>This lesson teaches you to</h2>
|
||||||
<ol>
|
<ol>
|
||||||
<li><a href="#drag">Drag an Object</a></li>
|
<li><a href="#drag">Drag an Object</a></li>
|
||||||
|
<li><a href="#pan">Drag to Pan</a></li>
|
||||||
<li><a href="#scale">Use Touch to Perform Scaling</a></li>
|
<li><a href="#scale">Use Touch to Perform Scaling</a></li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
@@ -25,20 +26,25 @@ next.link=viewgroup.html
|
|||||||
<li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
|
<li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
|
||||||
</li>
|
</li>
|
||||||
<li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
|
<li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
|
||||||
<li><a href="http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html">Making Sense of Multitouch</a> blog post</li>
|
|
||||||
<li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
|
<li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
|
||||||
<li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
|
<li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
|
||||||
<li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
|
<li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<h2>Try it out</h2>
|
||||||
|
|
||||||
|
<div class="download-box">
|
||||||
|
<a href="{@docRoot}shareables/training/InteractiveChart.zip"
|
||||||
|
class="button">Download the sample</a>
|
||||||
|
<p class="filename">InteractiveChart.zip</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p>This lesson describes how to use touch gestures to drag and scale on-screen
|
<p>This lesson describes how to use touch gestures to drag and scale on-screen
|
||||||
objects, using {@link android.view.View#onTouchEvent onTouchEvent()} to intercept
|
objects, using {@link android.view.View#onTouchEvent onTouchEvent()} to intercept
|
||||||
touch events. Here is the original <a
|
touch events.
|
||||||
href="http://code.google.com/p/android-touchexample/">source code</a>
|
|
||||||
for the examples used in this lesson.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 id="drag">Drag an Object</h2>
|
<h2 id="drag">Drag an Object</h2>
|
||||||
@@ -128,8 +134,6 @@ public boolean onTouchEvent(MotionEvent ev) {
|
|||||||
final float x = MotionEventCompat.getX(ev, pointerIndex);
|
final float x = MotionEventCompat.getX(ev, pointerIndex);
|
||||||
final float y = MotionEventCompat.getY(ev, pointerIndex);
|
final float y = MotionEventCompat.getY(ev, pointerIndex);
|
||||||
|
|
||||||
// Only move if the ScaleGestureDetector isn't processing a gesture.
|
|
||||||
if (!mScaleDetector.isInProgress()) {
|
|
||||||
// Calculate the distance moved
|
// Calculate the distance moved
|
||||||
final float dx = x - mLastTouchX;
|
final float dx = x - mLastTouchX;
|
||||||
final float dy = y - mLastTouchY;
|
final float dy = y - mLastTouchY;
|
||||||
@@ -138,7 +142,7 @@ public boolean onTouchEvent(MotionEvent ev) {
|
|||||||
mPosY += dy;
|
mPosY += dy;
|
||||||
|
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
|
||||||
// Remember this touch position for the next move event
|
// Remember this touch position for the next move event
|
||||||
mLastTouchX = x;
|
mLastTouchX = x;
|
||||||
mLastTouchY = y;
|
mLastTouchY = y;
|
||||||
@@ -175,6 +179,88 @@ public boolean onTouchEvent(MotionEvent ev) {
|
|||||||
return true;
|
return true;
|
||||||
}</pre>
|
}</pre>
|
||||||
|
|
||||||
|
<h2 id="pan">Drag to Pan</h2>
|
||||||
|
|
||||||
|
<p>The previous section showed an example of dragging an object around the screen. Another
|
||||||
|
common scenario is <em>panning</em>, which is when a user's dragging motion causes scrolling
|
||||||
|
in both the x and y axes. The above snippet directly intercepted the {@link android.view.MotionEvent}
|
||||||
|
actions to implement dragging. The snippet in this section takes advantage of the platform's
|
||||||
|
built-in support for common gestures. It overrides
|
||||||
|
{@link android.view.GestureDetector.OnGestureListener#onScroll onScroll()} in
|
||||||
|
{@link android.view.GestureDetector.SimpleOnGestureListener}.</p>
|
||||||
|
|
||||||
|
<p>To provide a little more context, {@link android.view.GestureDetector.OnGestureListener#onScroll onScroll()}
|
||||||
|
is called when a user is dragging his finger to pan the content.
|
||||||
|
{@link android.view.GestureDetector.OnGestureListener#onScroll onScroll()} is only called when
|
||||||
|
a finger is down; as soon as the finger is lifted from the screen, the gesture either ends,
|
||||||
|
or a fling gesture is started (if the finger was moving with some speed just before it was lifted).
|
||||||
|
For more discussion of scrolling vs. flinging, see <a href="scroll.html">Animating a Scroll Gesture</a>.</p>
|
||||||
|
|
||||||
|
<p>Here is the snippet for {@link android.view.GestureDetector.OnGestureListener#onScroll onScroll()}:
|
||||||
|
|
||||||
|
|
||||||
|
<pre>// 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;
|
||||||
|
}</pre>
|
||||||
|
|
||||||
|
<p>The implementation of {@link android.view.GestureDetector.OnGestureListener#onScroll onScroll()}
|
||||||
|
scrolls the viewport in response to the touch gesture:</p>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
|
||||||
<h2 id="scale">Use Touch to Perform Scaling</h2>
|
<h2 id="scale">Use Touch to Perform Scaling</h2>
|
||||||
|
|
||||||
<p>As discussed in <a href="detector.html">Detecting Common Gestures</a>,
|
<p>As discussed in <a href="detector.html">Detecting Common Gestures</a>,
|
||||||
@@ -191,10 +277,10 @@ Android provides
|
|||||||
{@link android.view.ScaleGestureDetector.SimpleOnScaleGestureListener}
|
{@link android.view.ScaleGestureDetector.SimpleOnScaleGestureListener}
|
||||||
as a helper class that you can extend if you don’t care about all of the reported events.</p>
|
as a helper class that you can extend if you don’t care about all of the reported events.</p>
|
||||||
|
|
||||||
<p>Here is a snippet that gives you the basic idea of how to perform scaling.
|
|
||||||
Here is the original <a
|
<h3>Basic scaling example</h3>
|
||||||
href="http://code.google.com/p/android-touchexample/">source code</a>
|
|
||||||
for the examples.</p>
|
<p>Here is a snippet that illustrates the basic ingredients involved in scaling.</p>
|
||||||
|
|
||||||
<pre>private ScaleGestureDetector mScaleDetector;
|
<pre>private ScaleGestureDetector mScaleDetector;
|
||||||
private float mScaleFactor = 1.f;
|
private float mScaleFactor = 1.f;
|
||||||
@@ -238,3 +324,88 @@ private class ScaleListener
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}</pre>
|
}</pre>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h3>More complex scaling example</h3>
|
||||||
|
<p>Here is a more complex example from the {@code InteractiveChart} sample provided with this class.
|
||||||
|
The {@code InteractiveChart} sample supports both scrolling (panning) and scaling with multiple fingers,
|
||||||
|
using the {@link android.view.ScaleGestureDetector} "span"
|
||||||
|
({@link android.view.ScaleGestureDetector#getCurrentSpanX getCurrentSpanX/Y}) and
|
||||||
|
"focus" ({@link android.view.ScaleGestureDetector#getFocusX getFocusX/Y}) features:</p>
|
||||||
|
|
||||||
|
<pre>@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.
|
||||||
|
@Override
|
||||||
|
public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
|
||||||
|
lastSpanX = ScaleGestureDetectorCompat.
|
||||||
|
getCurrentSpanX(scaleGestureDetector);
|
||||||
|
lastSpanY = ScaleGestureDetectorCompat.
|
||||||
|
getCurrentSpanY(scaleGestureDetector);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public 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;
|
||||||
|
}
|
||||||
|
};</pre>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ next.link=multi.html
|
|||||||
<!-- table of contents -->
|
<!-- table of contents -->
|
||||||
<h2>This lesson teaches you to</h2>
|
<h2>This lesson teaches you to</h2>
|
||||||
<ol>
|
<ol>
|
||||||
|
<li><a href="#term">Understand Scrolling Terminology</a></li>
|
||||||
<li><a href="#scroll">Implement Touch-Based Scrolling</a></li>
|
<li><a href="#scroll">Implement Touch-Based Scrolling</a></li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
@@ -24,12 +25,18 @@ next.link=multi.html
|
|||||||
<li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
|
<li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
|
||||||
</li>
|
</li>
|
||||||
<li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
|
<li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
|
||||||
<li><a href="http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html">Making Sense of Multitouch</a> blog post</li>
|
|
||||||
<li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
|
<li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
|
||||||
<li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
|
<li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
|
||||||
<li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
|
<li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<h2>Try it out</h2>
|
||||||
|
|
||||||
|
<div class="download-box">
|
||||||
|
<a href="{@docRoot}shareables/training/InteractiveChart.zip"
|
||||||
|
class="button">Download the sample</a>
|
||||||
|
<p class="filename">InteractiveChart.zip</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -45,7 +52,26 @@ a scrolling effect in response to touch gestures using <em>scrollers</em>.
|
|||||||
|
|
||||||
<p>You can use scrollers ({@link android.widget.Scroller} or {@link
|
<p>You can use scrollers ({@link android.widget.Scroller} or {@link
|
||||||
android.widget.OverScroller}) to collect the data you need to produce a
|
android.widget.OverScroller}) to collect the data you need to produce a
|
||||||
scrolling animation in response to a touch event.</p>
|
scrolling animation in response to a touch event. They are similar, but
|
||||||
|
{@link android.widget.OverScroller}
|
||||||
|
includes methods for indicating to users that they've reached the content edges
|
||||||
|
after a pan or fling gesture. The {@code InteractiveChart} sample
|
||||||
|
uses the the {@link android.widget.EdgeEffect} class
|
||||||
|
(actually the {@link android.support.v4.widget.EdgeEffectCompat} class)
|
||||||
|
to display a "glow" effect when users reach the content edges.</p>
|
||||||
|
|
||||||
|
<p class="note"><strong>Note:</strong> We recommend that you
|
||||||
|
use {@link android.widget.OverScroller} rather than {@link
|
||||||
|
android.widget.Scroller} for scrolling animations.
|
||||||
|
{@link android.widget.OverScroller} provides the best backward
|
||||||
|
compatibility with older devices.
|
||||||
|
<br />
|
||||||
|
Also note that you generally only need to use scrollers
|
||||||
|
when implementing scrolling yourself. {@link android.widget.ScrollView} and
|
||||||
|
{@link android.widget.HorizontalScrollView} do all of this for you if you nest your
|
||||||
|
layout within them.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
<p>A scroller is used to animate scrolling over time, using platform-standard
|
<p>A scroller is used to animate scrolling over time, using platform-standard
|
||||||
scrolling physics (friction, velocity, etc.). The scroller itself doesn't
|
scrolling physics (friction, velocity, etc.). The scroller itself doesn't
|
||||||
@@ -54,101 +80,280 @@ 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
|
responsibility to get and apply new coordinates at a rate that will make the
|
||||||
scrolling animation look smooth.</p>
|
scrolling animation look smooth.</p>
|
||||||
|
|
||||||
<p class="note"><strong>Note:</strong> You generally only need to use scrollers
|
|
||||||
when implementing scrolling yourself. {@link android.widget.ScrollView} and
|
|
||||||
{@link android.widget.HorizontalScrollView} do all this for you do all of this for you if you nest your layout within them.</p>
|
|
||||||
|
|
||||||
<h2 id = "scroll">Implement Touch-Based Scrolling</h2>
|
|
||||||
|
|
||||||
|
|
||||||
<p>This snippet illustrates the basics of using a scroller. It uses a
|
<h2 id="term">Understand Scrolling Terminology</h2>
|
||||||
{@link android.view.GestureDetector}, and overrides the
|
|
||||||
{@link android.view.GestureDetector.SimpleOnGestureListener} methods
|
|
||||||
{@link android.view.GestureDetector.OnGestureListener#onDown onDown()} and
|
|
||||||
{@link android.view.GestureDetector.OnGestureListener#onFling onFling()}. It also
|
|
||||||
overrides {@link android.view.GestureDetector.OnGestureListener#onScroll onScroll()}
|
|
||||||
to return {@code false} since you don't need to animate a scroll.</p>
|
|
||||||
|
|
||||||
|
<p>"Scrolling" is a word that can take on different meanings in Android, depending on the context.</p>
|
||||||
|
|
||||||
<p>It's common to use scrollers in conjunction with a fling gesture, but they
|
<p><strong>Scrolling</strong> 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
|
||||||
|
<em>panning</em>. The sample application provided with this class, {@code InteractiveChart}, illustrates
|
||||||
|
two different types of scrolling, dragging and flinging:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Dragging</strong> is the type of scrolling that occurs when a user drags her
|
||||||
|
finger across the touch screen. Simple dragging is often implemented by overriding
|
||||||
|
{@link android.view.GestureDetector.OnGestureListener#onScroll onScroll()} in
|
||||||
|
{@link android.view.GestureDetector.OnGestureListener}. For more discussion of dragging, see
|
||||||
|
<a href="dragging.jd">Dragging and Scaling</a>.</li>
|
||||||
|
|
||||||
|
<li><strong>Flinging</strong> 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
|
||||||
|
{@link android.view.GestureDetector.OnGestureListener#onFling onFling()}
|
||||||
|
in {@link android.view.GestureDetector.OnGestureListener}, and by using
|
||||||
|
a scroller object. This is the use
|
||||||
|
case that is the topic of this lesson.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>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
|
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 {@link
|
scrolling in response to a touch event. For example, you could override
|
||||||
android.view.View#onTouchEvent onTouchEvent()} to process touch events directly,
|
{@link android.view.View#onTouchEvent onTouchEvent()} to process touch
|
||||||
and produce a scrolling effect in response to those touch events.</p>
|
events directly, and produce a scrolling effect or a "snapping to page" animation
|
||||||
|
in response to those touch events.</p>
|
||||||
|
|
||||||
<pre>
|
|
||||||
private OverScroller mScroller = new OverScroller(context);
|
|
||||||
|
|
||||||
private GestureDetector.SimpleOnGestureListener mGestureListener
|
<h2 id="#scroll">Implement Touch-Based Scrolling</h2>
|
||||||
|
|
||||||
|
<p>This section describes how to use a scroller.
|
||||||
|
The snippet shown below comes from the {@code InteractiveChart} sample
|
||||||
|
provided with this class.
|
||||||
|
It uses a
|
||||||
|
{@link android.view.GestureDetector}, and overrides the
|
||||||
|
{@link android.view.GestureDetector.SimpleOnGestureListener} method
|
||||||
|
{@link android.view.GestureDetector.OnGestureListener#onFling onFling()}.
|
||||||
|
It uses {@link android.widget.OverScroller} to track the fling gesture.
|
||||||
|
If the user reaches the content edges
|
||||||
|
after the fling gesture, the app displays a "glow" effect.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="note"><strong>Note:</strong> The {@code InteractiveChart} sample app displays a
|
||||||
|
chart that you can zoom, pan, scroll, and so on. In the following snippet,
|
||||||
|
{@code 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.
|
||||||
|
{@code mCurrentViewport} represents the portion of the chart that is currently
|
||||||
|
visible in the screen. Because pixel offsets are generally treated as integers,
|
||||||
|
{@code mContentRect} is of the type {@link android.graphics.Rect}. Because the
|
||||||
|
graph domain and range are decimal/float values, {@code mCurrentViewport} is of
|
||||||
|
the type {@link android.graphics.RectF}.</p>
|
||||||
|
|
||||||
|
<p>The first part of the snippet shows the implementation of
|
||||||
|
{@link android.view.GestureDetector.OnGestureListener#onFling onFling()}:</p>
|
||||||
|
|
||||||
|
<pre>// 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() {
|
= new GestureDetector.SimpleOnGestureListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onDown(MotionEvent e) {
|
public boolean onDown(MotionEvent e) {
|
||||||
// Abort any active scroll animations and invalidate.
|
// Initiates the decay phase of any active edge effects.
|
||||||
|
releaseEdgeEffects();
|
||||||
|
mScrollerStartViewport.set(mCurrentViewport);
|
||||||
|
// Aborts any active scroll animations and invalidates.
|
||||||
mScroller.forceFinished(true);
|
mScroller.forceFinished(true);
|
||||||
// There is also a compatibility version:
|
ViewCompat.postInvalidateOnAnimation(InteractiveLineGraphView.this);
|
||||||
// ViewCompat.postInvalidateOnAnimation
|
|
||||||
postInvalidateOnAnimation();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
...
|
||||||
@Override
|
|
||||||
public boolean onScroll(MotionEvent e1, MotionEvent e2,
|
|
||||||
float distanceX, float distanceY) {
|
|
||||||
// You don't use a scroller in onScroll because you don't need to animate
|
|
||||||
// a scroll. The scroll occurs instantly in response to touch feedback.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onFling(MotionEvent e1, MotionEvent e2,
|
public boolean onFling(MotionEvent e1, MotionEvent e2,
|
||||||
float velocityX, float velocityY) {
|
float velocityX, float velocityY) {
|
||||||
// Before flinging, abort the current animation.
|
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);
|
mScroller.forceFinished(true);
|
||||||
// Begin the scroll animation
|
// Begins the animation
|
||||||
mScroller.fling(
|
mScroller.fling(
|
||||||
// Current scroll position
|
// Current scroll position
|
||||||
startX,
|
startX,
|
||||||
startY,
|
startY,
|
||||||
// Velocities, negated for natural touch response
|
velocityX,
|
||||||
(int) -velocityX,
|
velocityY,
|
||||||
(int) -velocityY,
|
/*
|
||||||
// Minimum and maximum scroll positions. The minimum scroll
|
* Minimum and maximum scroll positions. The minimum scroll
|
||||||
// position is generally zero and the maximum scroll position
|
* position is generally zero and the maximum scroll position
|
||||||
// is generally the content size less the screen size. So if the
|
* is generally the content size less the screen size. So if the
|
||||||
// content width is 1000 pixels and the screen width is 200
|
* content width is 1000 pixels and the screen width is 200
|
||||||
// pixels, the maximum scroll offset should be 800 pixels.
|
* pixels, the maximum scroll offset should be 800 pixels.
|
||||||
minX, maxX,
|
*/
|
||||||
minY, maxY,
|
0, surfaceSize.x - mContentRect.width(),
|
||||||
// The maximum overscroll bounds. This is useful when using
|
0, surfaceSize.y - mContentRect.height(),
|
||||||
// the EdgeEffect class to draw overscroll "glow" overlays.
|
// The edges of the content. This comes into play when using
|
||||||
|
// the EdgeEffect class to draw "glow" overlays.
|
||||||
mContentRect.width() / 2,
|
mContentRect.width() / 2,
|
||||||
mContentRect.height() / 2);
|
mContentRect.height() / 2);
|
||||||
// Invalidate to trigger computeScroll()
|
// Invalidates to trigger computeScroll()
|
||||||
postInvalidateOnAnimation();
|
ViewCompat.postInvalidateOnAnimation(this);
|
||||||
return true;
|
}</pre>
|
||||||
}
|
|
||||||
};
|
<p>When {@link android.view.GestureDetector.OnGestureListener#onFling onFling()} calls
|
||||||
|
{@link android.support.v4.view.ViewCompat#postInvalidateOnAnimation postInvalidateOnAnimation()},
|
||||||
|
it triggers
|
||||||
|
{@link android.view.View#computeScroll 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. </p>
|
||||||
|
|
||||||
|
<p>Most views pass the scroller object's x and y position directly to
|
||||||
|
{@link android.view.View#scrollTo scrollTo()}.
|
||||||
|
The following implementation of {@link android.view.View#computeScroll computeScroll()}
|
||||||
|
takes a different approach—it calls
|
||||||
|
{@link android.widget.OverScroller#computeScrollOffset 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
|
||||||
|
{@link android.support.v4.view.ViewCompat#postInvalidateOnAnimation postInvalidateOnAnimation()}
|
||||||
|
to trigger an invalidate on the view:</p>
|
||||||
|
|
||||||
|
<pre>// 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
|
@Override
|
||||||
public void computeScroll() {
|
public void computeScroll() {
|
||||||
super.computeScroll();
|
super.computeScroll();
|
||||||
|
|
||||||
// Compute the current scroll offsets. If this returns true, then the
|
boolean needsInvalidate = false;
|
||||||
// scroll has not yet finished.
|
|
||||||
|
// The scroller isn't finished, meaning a fling or programmatic pan
|
||||||
|
// operation is currently active.
|
||||||
if (mScroller.computeScrollOffset()) {
|
if (mScroller.computeScrollOffset()) {
|
||||||
|
Point surfaceSize = computeScrollSurfaceSize();
|
||||||
int currX = mScroller.getCurrX();
|
int currX = mScroller.getCurrX();
|
||||||
int currY = mScroller.getCurrY();
|
int currY = mScroller.getCurrY();
|
||||||
|
|
||||||
// Actually render the scrolled viewport, or actually scroll the
|
boolean canScrollX = (mCurrentViewport.left > AXIS_X_MIN
|
||||||
// view using View.scrollTo.
|
|| mCurrentViewport.right < AXIS_X_MAX);
|
||||||
|
boolean canScrollY = (mCurrentViewport.top > AXIS_Y_MIN
|
||||||
|
|| mCurrentViewport.bottom < AXIS_Y_MAX);
|
||||||
|
|
||||||
// If currX or currY are outside the bounds, render the overscroll
|
/*
|
||||||
// glow using EdgeEffect.
|
* If you are zoomed in and currX or currY is
|
||||||
|
* outside of bounds and you're not already
|
||||||
} else {
|
* showing overscroll, then render the overscroll
|
||||||
// The scroll has finished.
|
* 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;
|
||||||
|
}
|
||||||
|
...
|
||||||
|
}</pre>
|
||||||
|
|
||||||
|
<p>Here is the section of the code that performs the actual zoom:</p>
|
||||||
|
|
||||||
|
<pre>// 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);
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p>This is the {@code 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 {@code mContentRect}. If the chart is zoomed in 200% in both directions,
|
||||||
|
the returned size will be twice as large horizontally and vertically.</p>
|
||||||
|
|
||||||
|
<pre>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()));
|
||||||
}</pre>
|
}</pre>
|
||||||
|
|
||||||
<p>For another example of scroller usage, see the <a href="http://github.com/android/platform_frameworks_support/blob/master/v4/java/android/support/v4/view/ViewPager.java">source code</a> for the
|
<p>For another example of scroller usage, see the
|
||||||
{@link android.support.v4.view.ViewPager} class.</p>
|
<a href="http://github.com/android/platform_frameworks_support/blob/master/v4/java/android/support/v4/view/ViewPager.java">source code</a> for the
|
||||||
|
{@link android.support.v4.view.ViewPager} class. It scrolls in response to flings,
|
||||||
|
and uses scrolling to implement the "snapping to page" animation.</p>
|
||||||
|
|
||||||
|
|||||||
@@ -26,12 +26,19 @@ next.link=
|
|||||||
<li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
|
<li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
|
||||||
</li>
|
</li>
|
||||||
<li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
|
<li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
|
||||||
<li><a href="http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html">Making Sense of Multitouch</a> blog post</li>
|
|
||||||
<li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
|
<li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
|
||||||
<li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
|
<li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
|
||||||
<li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
|
<li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<h2>Try it out</h2>
|
||||||
|
|
||||||
|
<div class="download-box">
|
||||||
|
<a href="{@docRoot}shareables/training/InteractiveChart.zip"
|
||||||
|
class="button">Download the sample</a>
|
||||||
|
<p class="filename">InteractiveChart.zip</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user