blob: 3a4de39650faf33b081887b1864ea58bdb4ac2e4 [file] [log] [blame]
Owen Lina2fba682011-08-17 22:07:43 +08001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.gallery3d.ui;
18
19import com.android.gallery3d.anim.Animation;
20import com.android.gallery3d.common.Utils;
21import com.android.gallery3d.ui.PositionRepository.Position;
22import com.android.gallery3d.util.LinkedNode;
23
24import android.content.Context;
25import android.graphics.Rect;
26import android.view.GestureDetector;
27import android.view.MotionEvent;
28import android.view.animation.DecelerateInterpolator;
29
30import java.util.ArrayList;
31import java.util.HashMap;
32
33public class SlotView extends GLView {
34 @SuppressWarnings("unused")
35 private static final String TAG = "SlotView";
36
37 private static final boolean WIDE = true;
38
39 private static final int INDEX_NONE = -1;
40
41 public interface Listener {
Chih-Chung Chang70a73a72011-09-19 11:09:39 +080042 public void onDown(int index);
43 public void onUp();
Owen Lina2fba682011-08-17 22:07:43 +080044 public void onSingleTapUp(int index);
45 public void onLongTap(int index);
46 public void onScrollPositionChanged(int position, int total);
47 }
48
49 public static class SimpleListener implements Listener {
Chih-Chung Chang70a73a72011-09-19 11:09:39 +080050 public void onDown(int index) {}
51 public void onUp() {}
Owen Lina2fba682011-08-17 22:07:43 +080052 public void onSingleTapUp(int index) {}
53 public void onLongTap(int index) {}
54 public void onScrollPositionChanged(int position, int total) {}
55 }
56
57 private final GestureDetector mGestureDetector;
58 private final ScrollerHelper mScroller;
59 private final Paper mPaper = new Paper();
60
61 private Listener mListener;
62 private UserInteractionListener mUIListener;
63
64 // Use linked hash map to keep the rendering order
65 private HashMap<DisplayItem, ItemEntry> mItems =
66 new HashMap<DisplayItem, ItemEntry>();
67
68 public LinkedNode.List<ItemEntry> mItemList = LinkedNode.newList();
69
70 // This is used for multipass rendering
71 private ArrayList<ItemEntry> mCurrentItems = new ArrayList<ItemEntry>();
72 private ArrayList<ItemEntry> mNextItems = new ArrayList<ItemEntry>();
73
74 private boolean mMoreAnimation = false;
75 private MyAnimation mAnimation = null;
76 private final Position mTempPosition = new Position();
77 private final Layout mLayout = new Layout();
78 private PositionProvider mPositions;
79 private int mStartIndex = INDEX_NONE;
80
81 // whether the down action happened while the view is scrolling.
82 private boolean mDownInScrolling;
83 private int mOverscrollEffect = OVERSCROLL_3D;
84
85 public static final int OVERSCROLL_3D = 0;
86 public static final int OVERSCROLL_SYSTEM = 1;
87 public static final int OVERSCROLL_NONE = 2;
88
89 public SlotView(Context context) {
90 mGestureDetector =
91 new GestureDetector(context, new MyGestureListener());
92 mScroller = new ScrollerHelper(context);
93 }
94
95 public void setCenterIndex(int index) {
96 int slotCount = mLayout.mSlotCount;
97 if (index < 0 || index >= slotCount) {
98 return;
99 }
100 Rect rect = mLayout.getSlotRect(index);
101 int position = WIDE
102 ? (rect.left + rect.right - getWidth()) / 2
103 : (rect.top + rect.bottom - getHeight()) / 2;
104 setScrollPosition(position);
105 }
106
107 public void makeSlotVisible(int index) {
108 Rect rect = mLayout.getSlotRect(index);
109 int visibleBegin = WIDE ? mScrollX : mScrollY;
110 int visibleLength = WIDE ? getWidth() : getHeight();
111 int visibleEnd = visibleBegin + visibleLength;
112 int slotBegin = WIDE ? rect.left : rect.top;
113 int slotEnd = WIDE ? rect.right : rect.bottom;
114
115 int position = visibleBegin;
116 if (visibleLength < slotEnd - slotBegin) {
117 position = visibleBegin;
118 } else if (slotBegin < visibleBegin) {
119 position = slotBegin;
120 } else if (slotEnd > visibleEnd) {
121 position = slotEnd - visibleLength;
122 }
123
124 setScrollPosition(position);
125 }
126
127 public void setScrollPosition(int position) {
128 position = Utils.clamp(position, 0, mLayout.getScrollLimit());
129 mScroller.setPosition(position);
130 updateScrollPosition(position, false);
131 }
132
Chih-Chung Chang07069de2011-09-14 20:50:28 +0800133 public void setSlotSpec(Spec spec) {
134 mLayout.setSlotSpec(spec);
Owen Lina2fba682011-08-17 22:07:43 +0800135 }
136
137 @Override
138 public void addComponent(GLView view) {
139 throw new UnsupportedOperationException();
140 }
141
142 @Override
143 public boolean removeComponent(GLView view) {
144 throw new UnsupportedOperationException();
145 }
146
147 @Override
148 protected void onLayout(boolean changeSize, int l, int t, int r, int b) {
149 if (!changeSize) return;
150 mLayout.setSize(r - l, b - t);
151 onLayoutChanged(r - l, b - t);
152 if (mOverscrollEffect == OVERSCROLL_3D) {
153 mPaper.setSize(r - l, b - t);
154 }
155 }
156
157 protected void onLayoutChanged(int width, int height) {
158 }
159
160 public void startTransition(PositionProvider position) {
161 mPositions = position;
162 mAnimation = new MyAnimation();
163 mAnimation.start();
164 if (mItems.size() != 0) invalidate();
165 }
166
167 public void savePositions(PositionRepository repository) {
168 repository.clear();
169 LinkedNode.List<ItemEntry> list = mItemList;
170 ItemEntry entry = list.getFirst();
171 Position position = new Position();
172 while (entry != null) {
173 position.set(entry.target);
174 position.x -= mScrollX;
175 position.y -= mScrollY;
176 repository.putPosition(entry.item.getIdentity(), position);
177 entry = list.nextOf(entry);
178 }
179 }
180
181 private void updateScrollPosition(int position, boolean force) {
182 if (!force && (WIDE ? position == mScrollX : position == mScrollY)) return;
183 if (WIDE) {
184 mScrollX = position;
185 } else {
186 mScrollY = position;
187 }
188 mLayout.setScrollPosition(position);
189 onScrollPositionChanged(position);
190 }
191
192 protected void onScrollPositionChanged(int newPosition) {
193 int limit = mLayout.getScrollLimit();
194 mListener.onScrollPositionChanged(newPosition, limit);
195 }
196
197 public void putDisplayItem(Position target, Position base, DisplayItem item) {
Chih-Chung Chang07069de2011-09-14 20:50:28 +0800198 item.setBox(mLayout.getSlotWidth(), mLayout.getSlotHeight());
Owen Lina2fba682011-08-17 22:07:43 +0800199 ItemEntry entry = new ItemEntry(item, target, base);
200 mItemList.insertLast(entry);
201 mItems.put(item, entry);
202 }
203
204 public void removeDisplayItem(DisplayItem item) {
205 ItemEntry entry = mItems.remove(item);
206 if (entry != null) entry.remove();
207 }
208
209 public Rect getSlotRect(int slotIndex) {
210 return mLayout.getSlotRect(slotIndex);
211 }
212
213 @Override
214 protected boolean onTouch(MotionEvent event) {
215 if (mUIListener != null) mUIListener.onUserInteraction();
216 mGestureDetector.onTouchEvent(event);
217 switch (event.getAction()) {
218 case MotionEvent.ACTION_DOWN:
219 mDownInScrolling = !mScroller.isFinished();
220 mScroller.forceFinished();
221 break;
222 }
223 return true;
224 }
225
226 public void setListener(Listener listener) {
227 mListener = listener;
228 }
229
230 public void setUserInteractionListener(UserInteractionListener listener) {
231 mUIListener = listener;
232 }
233
234 public void setOverscrollEffect(int kind) {
235 mOverscrollEffect = kind;
236 mScroller.setOverfling(kind == OVERSCROLL_SYSTEM);
237 }
238
239 @Override
240 protected void render(GLCanvas canvas) {
Owen Lina2fba682011-08-17 22:07:43 +0800241 super.render(canvas);
242
243 long currentTimeMillis = canvas.currentAnimationTimeMillis();
244 boolean more = mScroller.advanceAnimation(currentTimeMillis);
245 boolean paperActive = (mOverscrollEffect == OVERSCROLL_3D)
246 && mPaper.advanceAnimation(currentTimeMillis);
247 updateScrollPosition(mScroller.getPosition(), false);
248 float interpolate = 1f;
249 if (mAnimation != null) {
250 more |= mAnimation.calculate(currentTimeMillis);
251 interpolate = mAnimation.value;
252 }
253
254 more |= paperActive;
255
256 if (WIDE) {
257 canvas.translate(-mScrollX, 0, 0);
258 } else {
259 canvas.translate(0, -mScrollY, 0);
260 }
261
262 LinkedNode.List<ItemEntry> list = mItemList;
263 for (ItemEntry entry = list.getLast(); entry != null;) {
264 if (renderItem(canvas, entry, interpolate, 0, paperActive)) {
265 mCurrentItems.add(entry);
266 }
267 entry = list.previousOf(entry);
268 }
269
270 int pass = 1;
271 while (!mCurrentItems.isEmpty()) {
272 for (int i = 0, n = mCurrentItems.size(); i < n; i++) {
273 ItemEntry entry = mCurrentItems.get(i);
274 if (renderItem(canvas, entry, interpolate, pass, paperActive)) {
275 mNextItems.add(entry);
276 }
277 }
278 mCurrentItems.clear();
279 // swap mNextItems with mCurrentItems
280 ArrayList<ItemEntry> tmp = mNextItems;
281 mNextItems = mCurrentItems;
282 mCurrentItems = tmp;
283 pass += 1;
284 }
285
286 if (WIDE) {
287 canvas.translate(mScrollX, 0, 0);
288 } else {
289 canvas.translate(0, mScrollY, 0);
290 }
291
292 if (more) invalidate();
293 if (mMoreAnimation && !more && mUIListener != null) {
294 mUIListener.onUserInteractionEnd();
295 }
296 mMoreAnimation = more;
Owen Lina2fba682011-08-17 22:07:43 +0800297 }
298
299 private boolean renderItem(GLCanvas canvas, ItemEntry entry,
300 float interpolate, int pass, boolean paperActive) {
301 canvas.save(GLCanvas.SAVE_FLAG_ALPHA | GLCanvas.SAVE_FLAG_MATRIX);
302 Position position = entry.target;
303 if (mPositions != null) {
304 position = mTempPosition;
305 position.set(entry.target);
306 position.x -= mScrollX;
307 position.y -= mScrollY;
308 Position source = mPositions
309 .getPosition(entry.item.getIdentity(), position);
310 source.x += mScrollX;
311 source.y += mScrollY;
312 position = mTempPosition;
313 Position.interpolate(
314 source, entry.target, position, interpolate);
315 }
316 canvas.multiplyAlpha(position.alpha);
317 if (paperActive) {
318 canvas.multiplyMatrix(mPaper.getTransform(
319 position, entry.base, mScrollX, mScrollY), 0);
320 } else {
321 canvas.translate(position.x, position.y, position.z);
322 }
323 canvas.rotate(position.theta, 0, 0, 1);
324 boolean more = entry.item.render(canvas, pass);
325 canvas.restore();
326 return more;
327 }
328
329 public static class MyAnimation extends Animation {
330 public float value;
331
332 public MyAnimation() {
333 setInterpolator(new DecelerateInterpolator(4));
334 setDuration(1500);
335 }
336
337 @Override
338 protected void onCalculate(float progress) {
339 value = progress;
340 }
341 }
342
343 private static class ItemEntry extends LinkedNode {
344 public DisplayItem item;
345 public Position target;
346 public Position base;
347
348 public ItemEntry(DisplayItem item, Position target, Position base) {
349 this.item = item;
350 this.target = target;
351 this.base = base;
352 }
353 }
354
Chih-Chung Chang07069de2011-09-14 20:50:28 +0800355 // This Spec class is used to specify the size of each slot in the SlotView.
356 // There are two ways to do it:
357 //
358 // (1) Specify slotWidth and slotHeight: they specify the width and height
359 // of each slot. The number of rows and the gap between slots will be
360 // determined automatically.
361 // (2) Specify rowsLand, rowsPort, and slotGap: they specify the number
362 // of rows in landscape/portrait mode and the gap between slots. The
363 // width and height of each slot is determined automatically.
364 //
365 // The initial value of -1 means they are not specified.
366 public static class Spec {
367 public int slotWidth = -1;
368 public int slotHeight = -1;
369
370 public int rowsLand = -1;
371 public int rowsPort = -1;
372 public int slotGap = -1;
373
374 static Spec newWithSize(int width, int height) {
375 Spec s = new Spec();
376 s.slotWidth = width;
377 s.slotHeight = height;
378 return s;
379 }
380
381 static Spec newWithRows(int rowsLand, int rowsPort, int slotGap) {
382 Spec s = new Spec();
383 s.rowsLand = rowsLand;
384 s.rowsPort = rowsPort;
385 s.slotGap = slotGap;
386 return s;
387 }
388 }
389
Owen Lina2fba682011-08-17 22:07:43 +0800390 public static class Layout {
391
392 private int mVisibleStart;
393 private int mVisibleEnd;
394
395 private int mSlotCount;
396 private int mSlotWidth;
397 private int mSlotHeight;
Chih-Chung Chang07069de2011-09-14 20:50:28 +0800398 private int mSlotGap;
399
400 private Spec mSpec;
Owen Lina2fba682011-08-17 22:07:43 +0800401
402 private int mWidth;
403 private int mHeight;
404
405 private int mUnitCount;
406 private int mContentLength;
407 private int mScrollPosition;
408
409 private int mVerticalPadding;
410 private int mHorizontalPadding;
411
Chih-Chung Chang07069de2011-09-14 20:50:28 +0800412 public void setSlotSpec(Spec spec) {
413 mSpec = spec;
Owen Lina2fba682011-08-17 22:07:43 +0800414 }
415
416 public boolean setSlotCount(int slotCount) {
417 mSlotCount = slotCount;
418 int hPadding = mHorizontalPadding;
419 int vPadding = mVerticalPadding;
420 initLayoutParameters();
421 return vPadding != mVerticalPadding || hPadding != mHorizontalPadding;
422 }
423
424 public Rect getSlotRect(int index) {
425 int col, row;
426 if (WIDE) {
427 col = index / mUnitCount;
428 row = index - col * mUnitCount;
429 } else {
430 row = index / mUnitCount;
431 col = index - row * mUnitCount;
432 }
433
Chih-Chung Chang07069de2011-09-14 20:50:28 +0800434 int x = mHorizontalPadding + col * (mSlotWidth + mSlotGap);
435 int y = mVerticalPadding + row * (mSlotHeight + mSlotGap);
Owen Lina2fba682011-08-17 22:07:43 +0800436 return new Rect(x, y, x + mSlotWidth, y + mSlotHeight);
437 }
438
Chih-Chung Chang07069de2011-09-14 20:50:28 +0800439 public int getSlotWidth() {
440 return mSlotWidth;
441 }
442
443 public int getSlotHeight() {
444 return mSlotHeight;
445 }
446
Owen Lina2fba682011-08-17 22:07:43 +0800447 public int getContentLength() {
448 return mContentLength;
449 }
450
451 // Calculate
452 // (1) mUnitCount: the number of slots we can fit into one column (or row).
453 // (2) mContentLength: the width (or height) we need to display all the
454 // columns (rows).
455 // (3) padding[]: the vertical and horizontal padding we need in order
456 // to put the slots towards to the center of the display.
457 //
458 // The "major" direction is the direction the user can scroll. The other
459 // direction is the "minor" direction.
460 //
461 // The comments inside this method are the description when the major
462 // directon is horizontal (X), and the minor directon is vertical (Y).
463 private void initLayoutParameters(
464 int majorLength, int minorLength, /* The view width and height */
465 int majorUnitSize, int minorUnitSize, /* The slot width and height */
466 int[] padding) {
Chih-Chung Chang07069de2011-09-14 20:50:28 +0800467 int unitCount = (minorLength + mSlotGap) / (minorUnitSize + mSlotGap);
Owen Lina2fba682011-08-17 22:07:43 +0800468 if (unitCount == 0) unitCount = 1;
469 mUnitCount = unitCount;
470
471 // We put extra padding above and below the column.
472 int availableUnits = Math.min(mUnitCount, mSlotCount);
Chih-Chung Chang07069de2011-09-14 20:50:28 +0800473 int usedMinorLength = availableUnits * minorUnitSize +
474 (availableUnits - 1) * mSlotGap;
475 padding[0] = (minorLength - usedMinorLength) / 2;
Owen Lina2fba682011-08-17 22:07:43 +0800476
477 // Then calculate how many columns we need for all slots.
478 int count = ((mSlotCount + mUnitCount - 1) / mUnitCount);
Chih-Chung Chang07069de2011-09-14 20:50:28 +0800479 mContentLength = count * majorUnitSize + (count - 1) * mSlotGap;
Owen Lina2fba682011-08-17 22:07:43 +0800480
481 // If the content length is less then the screen width, put
482 // extra padding in left and right.
483 padding[1] = Math.max(0, (majorLength - mContentLength) / 2);
484 }
485
486 private void initLayoutParameters() {
Chih-Chung Chang07069de2011-09-14 20:50:28 +0800487 // Initialize mSlotWidth and mSlotHeight from mSpec
488 if (mSpec.slotWidth != -1) {
489 mSlotGap = 0;
490 mSlotWidth = mSpec.slotWidth;
491 mSlotHeight = mSpec.slotHeight;
492 } else {
493 int rows = (mWidth > mHeight) ? mSpec.rowsLand : mSpec.rowsPort;
494 mSlotGap = mSpec.slotGap;
495 mSlotHeight = Math.max(1, (mHeight - (rows - 1) * mSlotGap) / rows);
496 mSlotWidth = mSlotHeight;
497 }
498
Owen Lina2fba682011-08-17 22:07:43 +0800499 int[] padding = new int[2];
500 if (WIDE) {
501 initLayoutParameters(mWidth, mHeight, mSlotWidth, mSlotHeight, padding);
502 mVerticalPadding = padding[0];
503 mHorizontalPadding = padding[1];
504 } else {
505 initLayoutParameters(mHeight, mWidth, mSlotHeight, mSlotWidth, padding);
506 mVerticalPadding = padding[1];
507 mHorizontalPadding = padding[0];
508 }
509 updateVisibleSlotRange();
510 }
511
512 public void setSize(int width, int height) {
513 mWidth = width;
514 mHeight = height;
515 initLayoutParameters();
516 }
517
518 private void updateVisibleSlotRange() {
519 int position = mScrollPosition;
520
521 if (WIDE) {
Chih-Chung Chang07069de2011-09-14 20:50:28 +0800522 int startCol = position / (mSlotWidth + mSlotGap);
523 int start = Math.max(0, mUnitCount * startCol);
524 int endCol = (position + mWidth + mSlotWidth + mSlotGap - 1) /
525 (mSlotWidth + mSlotGap);
526 int end = Math.min(mSlotCount, mUnitCount * endCol);
Owen Lina2fba682011-08-17 22:07:43 +0800527 setVisibleRange(start, end);
528 } else {
Chih-Chung Chang07069de2011-09-14 20:50:28 +0800529 int startRow = position / (mSlotHeight + mSlotGap);
530 int start = Math.max(0, mUnitCount * startRow);
531 int endRow = (position + mHeight + mSlotHeight + mSlotGap - 1) /
532 (mSlotHeight + mSlotGap);
533 int end = Math.min(mSlotCount, mUnitCount * endRow);
Owen Lina2fba682011-08-17 22:07:43 +0800534 setVisibleRange(start, end);
535 }
536 }
537
538 public void setScrollPosition(int position) {
539 if (mScrollPosition == position) return;
540 mScrollPosition = position;
541 updateVisibleSlotRange();
542 }
543
544 private void setVisibleRange(int start, int end) {
545 if (start == mVisibleStart && end == mVisibleEnd) return;
546 if (start < end) {
547 mVisibleStart = start;
548 mVisibleEnd = end;
549 } else {
550 mVisibleStart = mVisibleEnd = 0;
551 }
552 }
553
554 public int getVisibleStart() {
555 return mVisibleStart;
556 }
557
558 public int getVisibleEnd() {
559 return mVisibleEnd;
560 }
561
562 public int getSlotIndexByPosition(float x, float y) {
Chih-Chung Chang07069de2011-09-14 20:50:28 +0800563 int absoluteX = Math.round(x) + (WIDE ? mScrollPosition : 0);
564 int absoluteY = Math.round(y) + (WIDE ? 0 : mScrollPosition);
565
Owen Lina2fba682011-08-17 22:07:43 +0800566 absoluteX -= mHorizontalPadding;
Chih-Chung Chang07069de2011-09-14 20:50:28 +0800567 absoluteY -= mVerticalPadding;
568
569 int columnIdx = absoluteX / (mSlotWidth + mSlotGap);
570 int rowIdx = absoluteY / (mSlotHeight + mSlotGap);
571
572 if (columnIdx < 0 || (!WIDE && columnIdx >= mUnitCount)) {
Owen Lina2fba682011-08-17 22:07:43 +0800573 return INDEX_NONE;
574 }
575
Chih-Chung Chang07069de2011-09-14 20:50:28 +0800576 if (rowIdx < 0 || (WIDE && rowIdx >= mUnitCount)) {
Owen Lina2fba682011-08-17 22:07:43 +0800577 return INDEX_NONE;
578 }
Chih-Chung Chang07069de2011-09-14 20:50:28 +0800579
580 if (absoluteX % (mSlotWidth + mSlotGap) >= mSlotWidth) {
581 return INDEX_NONE;
582 }
583
584 if (absoluteY % (mSlotHeight + mSlotGap) >= mSlotHeight) {
585 return INDEX_NONE;
586 }
587
Owen Lina2fba682011-08-17 22:07:43 +0800588 int index = WIDE
589 ? (columnIdx * mUnitCount + rowIdx)
590 : (rowIdx * mUnitCount + columnIdx);
591
592 return index >= mSlotCount ? INDEX_NONE : index;
593 }
594
595 public int getScrollLimit() {
596 int limit = WIDE ? mContentLength - mWidth : mContentLength - mHeight;
597 return limit <= 0 ? 0 : limit;
598 }
599 }
600
Chih-Chung Chang70a73a72011-09-19 11:09:39 +0800601 private class MyGestureListener implements
602 GestureDetector.OnGestureListener {
603 private boolean isDown;
604
605 // We call the listener's onDown() when our onShowPress() is called and
606 // call the listener's onUp() when we receive any further event.
607 @Override
608 public void onShowPress(MotionEvent e) {
609 if (isDown) return;
610 int index = mLayout.getSlotIndexByPosition(e.getX(), e.getY());
611 if (index != INDEX_NONE) {
612 isDown = true;
613 mListener.onDown(index);
614 }
615 }
616
617 private void cancelDown() {
618 if (!isDown) return;
619 isDown = false;
620 mListener.onUp();
621 }
622
623 @Override
624 public boolean onDown(MotionEvent e) {
625 return false;
626 }
Owen Lina2fba682011-08-17 22:07:43 +0800627
628 @Override
629 public boolean onFling(MotionEvent e1,
630 MotionEvent e2, float velocityX, float velocityY) {
Chih-Chung Chang70a73a72011-09-19 11:09:39 +0800631 cancelDown();
Owen Lina2fba682011-08-17 22:07:43 +0800632 int scrollLimit = mLayout.getScrollLimit();
633 if (scrollLimit == 0) return false;
634 float velocity = WIDE ? velocityX : velocityY;
635 mScroller.fling((int) -velocity, 0, scrollLimit);
636 if (mUIListener != null) mUIListener.onUserInteractionBegin();
637 invalidate();
638 return true;
639 }
640
641 @Override
642 public boolean onScroll(MotionEvent e1,
643 MotionEvent e2, float distanceX, float distanceY) {
Chih-Chung Chang70a73a72011-09-19 11:09:39 +0800644 cancelDown();
Owen Lina2fba682011-08-17 22:07:43 +0800645 float distance = WIDE ? distanceX : distanceY;
646 boolean canMove = mScroller.startScroll(
647 Math.round(distance), 0, mLayout.getScrollLimit());
648 if (mOverscrollEffect == OVERSCROLL_3D && !canMove) {
649 mPaper.overScroll(distance);
650 }
651 invalidate();
652 return true;
653 }
654
655 @Override
656 public boolean onSingleTapUp(MotionEvent e) {
Chih-Chung Chang70a73a72011-09-19 11:09:39 +0800657 cancelDown();
Owen Lina2fba682011-08-17 22:07:43 +0800658 if (mDownInScrolling) return true;
659 int index = mLayout.getSlotIndexByPosition(e.getX(), e.getY());
660 if (index != INDEX_NONE) mListener.onSingleTapUp(index);
661 return true;
662 }
663
664 @Override
665 public void onLongPress(MotionEvent e) {
Chih-Chung Chang70a73a72011-09-19 11:09:39 +0800666 cancelDown();
Owen Lina2fba682011-08-17 22:07:43 +0800667 if (mDownInScrolling) return;
668 lockRendering();
669 try {
670 int index = mLayout.getSlotIndexByPosition(e.getX(), e.getY());
671 if (index != INDEX_NONE) mListener.onLongTap(index);
672 } finally {
673 unlockRendering();
674 }
675 }
676 }
677
678 public void setStartIndex(int index) {
679 mStartIndex = index;
680 }
681
682 // Return true if the layout parameters have been changed
683 public boolean setSlotCount(int slotCount) {
684 boolean changed = mLayout.setSlotCount(slotCount);
685
686 // mStartIndex is applied the first time setSlotCount is called.
687 if (mStartIndex != INDEX_NONE) {
688 setCenterIndex(mStartIndex);
689 mStartIndex = INDEX_NONE;
690 }
691 updateScrollPosition(WIDE ? mScrollX : mScrollY, true);
692 return changed;
693 }
694
695 public int getVisibleStart() {
696 return mLayout.getVisibleStart();
697 }
698
699 public int getVisibleEnd() {
700 return mLayout.getVisibleEnd();
701 }
702
703 public int getScrollX() {
704 return mScrollX;
705 }
706
707 public int getScrollY() {
708 return mScrollY;
709 }
710}