Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 1 | /* |
| 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 | |
| 17 | package com.android.gallery3d.ui; |
| 18 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 19 | import android.graphics.Rect; |
Owen Lin | c907c32 | 2011-11-17 15:26:56 +0800 | [diff] [blame] | 20 | import android.os.Handler; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 21 | import android.view.GestureDetector; |
| 22 | import android.view.MotionEvent; |
| 23 | import android.view.animation.DecelerateInterpolator; |
| 24 | |
Ray Chen | 73e791c | 2011-10-04 15:19:44 +0800 | [diff] [blame] | 25 | import com.android.gallery3d.anim.Animation; |
Owen Lin | e681d65 | 2012-08-24 12:25:57 +0800 | [diff] [blame] | 26 | import com.android.gallery3d.app.AbstractGalleryActivity; |
Ray Chen | 73e791c | 2011-10-04 15:19:44 +0800 | [diff] [blame] | 27 | import com.android.gallery3d.common.Utils; |
John Reck | 8cb165a | 2012-12-11 14:42:27 -0800 | [diff] [blame] | 28 | import com.android.gallery3d.glrenderer.GLCanvas; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 29 | |
| 30 | public class SlotView extends GLView { |
| 31 | @SuppressWarnings("unused") |
| 32 | private static final String TAG = "SlotView"; |
| 33 | |
| 34 | private static final boolean WIDE = true; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 35 | private static final int INDEX_NONE = -1; |
| 36 | |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 37 | public static final int RENDER_MORE_PASS = 1; |
| 38 | public static final int RENDER_MORE_FRAME = 2; |
| 39 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 40 | public interface Listener { |
Chih-Chung Chang | 70a73a7 | 2011-09-19 11:09:39 +0800 | [diff] [blame] | 41 | public void onDown(int index); |
Yuli Huang | 4072ab9 | 2012-04-24 15:20:28 +0800 | [diff] [blame] | 42 | public void onUp(boolean followedByLongPress); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 43 | public void onSingleTapUp(int index); |
| 44 | public void onLongTap(int index); |
| 45 | public void onScrollPositionChanged(int position, int total); |
| 46 | } |
| 47 | |
| 48 | public static class SimpleListener implements Listener { |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 49 | @Override public void onDown(int index) {} |
Yuli Huang | 4072ab9 | 2012-04-24 15:20:28 +0800 | [diff] [blame] | 50 | @Override public void onUp(boolean followedByLongPress) {} |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 51 | @Override public void onSingleTapUp(int index) {} |
| 52 | @Override public void onLongTap(int index) {} |
| 53 | @Override public void onScrollPositionChanged(int position, int total) {} |
| 54 | } |
| 55 | |
| 56 | public static interface SlotRenderer { |
| 57 | public void prepareDrawing(); |
| 58 | public void onVisibleRangeChanged(int visibleStart, int visibleEnd); |
Owen Lin | 8123444 | 2012-03-26 18:28:48 +0800 | [diff] [blame] | 59 | public void onSlotSizeChanged(int width, int height); |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 60 | public int renderSlot(GLCanvas canvas, int index, int pass, int width, int height); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 61 | } |
| 62 | |
| 63 | private final GestureDetector mGestureDetector; |
| 64 | private final ScrollerHelper mScroller; |
| 65 | private final Paper mPaper = new Paper(); |
| 66 | |
| 67 | private Listener mListener; |
| 68 | private UserInteractionListener mUIListener; |
| 69 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 70 | private boolean mMoreAnimation = false; |
Owen Lin | f52afa9 | 2012-03-23 14:59:14 +0800 | [diff] [blame] | 71 | private SlotAnimation mAnimation = null; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 72 | private final Layout mLayout = new Layout(); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 73 | private int mStartIndex = INDEX_NONE; |
| 74 | |
| 75 | // whether the down action happened while the view is scrolling. |
| 76 | private boolean mDownInScrolling; |
| 77 | private int mOverscrollEffect = OVERSCROLL_3D; |
Owen Lin | c907c32 | 2011-11-17 15:26:56 +0800 | [diff] [blame] | 78 | private final Handler mHandler; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 79 | |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 80 | private SlotRenderer mRenderer; |
| 81 | |
| 82 | private int[] mRequestRenderSlots = new int[16]; |
| 83 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 84 | public static final int OVERSCROLL_3D = 0; |
| 85 | public static final int OVERSCROLL_SYSTEM = 1; |
| 86 | public static final int OVERSCROLL_NONE = 2; |
| 87 | |
Owen Lin | d705545 | 2012-03-29 14:27:58 +0800 | [diff] [blame] | 88 | // to prevent allocating memory |
| 89 | private final Rect mTempRect = new Rect(); |
| 90 | |
Owen Lin | e681d65 | 2012-08-24 12:25:57 +0800 | [diff] [blame] | 91 | public SlotView(AbstractGalleryActivity activity, Spec spec) { |
| 92 | mGestureDetector = new GestureDetector(activity, new MyGestureListener()); |
| 93 | mScroller = new ScrollerHelper(activity); |
Owen Lin | 3c21751 | 2012-04-17 15:38:36 +0800 | [diff] [blame] | 94 | mHandler = new SynchronizedHandler(activity.getGLRoot()); |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 95 | setSlotSpec(spec); |
| 96 | } |
| 97 | |
| 98 | public void setSlotRenderer(SlotRenderer slotDrawer) { |
| 99 | mRenderer = slotDrawer; |
| 100 | if (mRenderer != null) { |
Owen Lin | 8123444 | 2012-03-26 18:28:48 +0800 | [diff] [blame] | 101 | mRenderer.onSlotSizeChanged(mLayout.mSlotWidth, mLayout.mSlotHeight); |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 102 | mRenderer.onVisibleRangeChanged(getVisibleStart(), getVisibleEnd()); |
| 103 | } |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 104 | } |
| 105 | |
| 106 | public void setCenterIndex(int index) { |
| 107 | int slotCount = mLayout.mSlotCount; |
| 108 | if (index < 0 || index >= slotCount) { |
| 109 | return; |
| 110 | } |
Owen Lin | d705545 | 2012-03-29 14:27:58 +0800 | [diff] [blame] | 111 | Rect rect = mLayout.getSlotRect(index, mTempRect); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 112 | int position = WIDE |
| 113 | ? (rect.left + rect.right - getWidth()) / 2 |
| 114 | : (rect.top + rect.bottom - getHeight()) / 2; |
| 115 | setScrollPosition(position); |
| 116 | } |
| 117 | |
| 118 | public void makeSlotVisible(int index) { |
Owen Lin | d705545 | 2012-03-29 14:27:58 +0800 | [diff] [blame] | 119 | Rect rect = mLayout.getSlotRect(index, mTempRect); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 120 | int visibleBegin = WIDE ? mScrollX : mScrollY; |
| 121 | int visibleLength = WIDE ? getWidth() : getHeight(); |
| 122 | int visibleEnd = visibleBegin + visibleLength; |
| 123 | int slotBegin = WIDE ? rect.left : rect.top; |
| 124 | int slotEnd = WIDE ? rect.right : rect.bottom; |
| 125 | |
| 126 | int position = visibleBegin; |
| 127 | if (visibleLength < slotEnd - slotBegin) { |
| 128 | position = visibleBegin; |
| 129 | } else if (slotBegin < visibleBegin) { |
| 130 | position = slotBegin; |
| 131 | } else if (slotEnd > visibleEnd) { |
| 132 | position = slotEnd - visibleLength; |
| 133 | } |
| 134 | |
| 135 | setScrollPosition(position); |
| 136 | } |
| 137 | |
| 138 | public void setScrollPosition(int position) { |
| 139 | position = Utils.clamp(position, 0, mLayout.getScrollLimit()); |
| 140 | mScroller.setPosition(position); |
| 141 | updateScrollPosition(position, false); |
| 142 | } |
| 143 | |
Chih-Chung Chang | 07069de | 2011-09-14 20:50:28 +0800 | [diff] [blame] | 144 | public void setSlotSpec(Spec spec) { |
| 145 | mLayout.setSlotSpec(spec); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 146 | } |
| 147 | |
| 148 | @Override |
| 149 | public void addComponent(GLView view) { |
| 150 | throw new UnsupportedOperationException(); |
| 151 | } |
| 152 | |
| 153 | @Override |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 154 | protected void onLayout(boolean changeSize, int l, int t, int r, int b) { |
| 155 | if (!changeSize) return; |
Chih-Chung Chang | ca07852 | 2011-09-26 10:40:38 +0800 | [diff] [blame] | 156 | |
| 157 | // Make sure we are still at a resonable scroll position after the size |
| 158 | // is changed (like orientation change). We choose to keep the center |
| 159 | // visible slot still visible. This is arbitrary but reasonable. |
| 160 | int visibleIndex = |
| 161 | (mLayout.getVisibleStart() + mLayout.getVisibleEnd()) / 2; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 162 | mLayout.setSize(r - l, b - t); |
Chih-Chung Chang | ca07852 | 2011-09-26 10:40:38 +0800 | [diff] [blame] | 163 | makeSlotVisible(visibleIndex); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 164 | if (mOverscrollEffect == OVERSCROLL_3D) { |
| 165 | mPaper.setSize(r - l, b - t); |
| 166 | } |
| 167 | } |
| 168 | |
Owen Lin | f52afa9 | 2012-03-23 14:59:14 +0800 | [diff] [blame] | 169 | public void startScatteringAnimation(RelativePosition position) { |
| 170 | mAnimation = new ScatteringAnimation(position); |
| 171 | mAnimation.start(); |
| 172 | if (mLayout.mSlotCount != 0) invalidate(); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 173 | } |
| 174 | |
Owen Lin | f52afa9 | 2012-03-23 14:59:14 +0800 | [diff] [blame] | 175 | public void startRisingAnimation() { |
| 176 | mAnimation = new RisingAnimation(); |
| 177 | mAnimation.start(); |
| 178 | if (mLayout.mSlotCount != 0) invalidate(); |
| 179 | } |
| 180 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 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 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 197 | public Rect getSlotRect(int slotIndex) { |
Owen Lin | d705545 | 2012-03-29 14:27:58 +0800 | [diff] [blame] | 198 | return mLayout.getSlotRect(slotIndex, new Rect()); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 199 | } |
| 200 | |
| 201 | @Override |
| 202 | protected boolean onTouch(MotionEvent event) { |
| 203 | if (mUIListener != null) mUIListener.onUserInteraction(); |
| 204 | mGestureDetector.onTouchEvent(event); |
| 205 | switch (event.getAction()) { |
| 206 | case MotionEvent.ACTION_DOWN: |
| 207 | mDownInScrolling = !mScroller.isFinished(); |
| 208 | mScroller.forceFinished(); |
| 209 | break; |
Chih-Chung Chang | ca07852 | 2011-09-26 10:40:38 +0800 | [diff] [blame] | 210 | case MotionEvent.ACTION_UP: |
| 211 | mPaper.onRelease(); |
| 212 | invalidate(); |
| 213 | break; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 214 | } |
| 215 | return true; |
| 216 | } |
| 217 | |
| 218 | public void setListener(Listener listener) { |
| 219 | mListener = listener; |
| 220 | } |
| 221 | |
| 222 | public void setUserInteractionListener(UserInteractionListener listener) { |
| 223 | mUIListener = listener; |
| 224 | } |
| 225 | |
| 226 | public void setOverscrollEffect(int kind) { |
| 227 | mOverscrollEffect = kind; |
| 228 | mScroller.setOverfling(kind == OVERSCROLL_SYSTEM); |
| 229 | } |
| 230 | |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 231 | private static int[] expandIntArray(int array[], int capacity) { |
| 232 | while (array.length < capacity) { |
| 233 | array = new int[array.length * 2]; |
| 234 | } |
| 235 | return array; |
| 236 | } |
| 237 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 238 | @Override |
| 239 | protected void render(GLCanvas canvas) { |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 240 | super.render(canvas); |
| 241 | |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 242 | if (mRenderer == null) return; |
| 243 | mRenderer.prepareDrawing(); |
| 244 | |
Chih-Chung Chang | b3d0196 | 2012-02-17 10:02:27 +0800 | [diff] [blame] | 245 | long animTime = AnimationTime.get(); |
| 246 | boolean more = mScroller.advanceAnimation(animTime); |
Owen Lin | c88fabf | 2012-05-09 11:32:32 +0800 | [diff] [blame] | 247 | more |= mLayout.advanceAnimation(animTime); |
Chih-Chung Chang | ca07852 | 2011-09-26 10:40:38 +0800 | [diff] [blame] | 248 | int oldX = mScrollX; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 249 | updateScrollPosition(mScroller.getPosition(), false); |
Chih-Chung Chang | ca07852 | 2011-09-26 10:40:38 +0800 | [diff] [blame] | 250 | |
| 251 | boolean paperActive = false; |
| 252 | if (mOverscrollEffect == OVERSCROLL_3D) { |
| 253 | // Check if an edge is reached and notify mPaper if so. |
| 254 | int newX = mScrollX; |
| 255 | int limit = mLayout.getScrollLimit(); |
| 256 | if (oldX > 0 && newX == 0 || oldX < limit && newX == limit) { |
| 257 | float v = mScroller.getCurrVelocity(); |
| 258 | if (newX == limit) v = -v; |
Chih-Chung Chang | 1b2af5e | 2011-09-27 19:44:36 +0800 | [diff] [blame] | 259 | |
| 260 | // I don't know why, but getCurrVelocity() can return NaN. |
| 261 | if (!Float.isNaN(v)) { |
| 262 | mPaper.edgeReached(v); |
| 263 | } |
Chih-Chung Chang | ca07852 | 2011-09-26 10:40:38 +0800 | [diff] [blame] | 264 | } |
| 265 | paperActive = mPaper.advanceAnimation(); |
| 266 | } |
| 267 | |
| 268 | more |= paperActive; |
| 269 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 270 | if (mAnimation != null) { |
Chih-Chung Chang | b3d0196 | 2012-02-17 10:02:27 +0800 | [diff] [blame] | 271 | more |= mAnimation.calculate(animTime); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 272 | } |
| 273 | |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 274 | canvas.translate(-mScrollX, -mScrollY); |
| 275 | |
| 276 | int requestCount = 0; |
| 277 | int requestedSlot[] = expandIntArray(mRequestRenderSlots, |
| 278 | mLayout.mVisibleEnd - mLayout.mVisibleStart); |
| 279 | |
Owen Lin | f52afa9 | 2012-03-23 14:59:14 +0800 | [diff] [blame] | 280 | for (int i = mLayout.mVisibleEnd - 1; i >= mLayout.mVisibleStart; --i) { |
| 281 | int r = renderItem(canvas, i, 0, paperActive); |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 282 | if ((r & RENDER_MORE_FRAME) != 0) more = true; |
| 283 | if ((r & RENDER_MORE_PASS) != 0) requestedSlot[requestCount++] = i; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 284 | } |
| 285 | |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 286 | for (int pass = 1; requestCount != 0; ++pass) { |
| 287 | int newCount = 0; |
| 288 | for (int i = 0; i < requestCount; ++i) { |
| 289 | int r = renderItem(canvas, |
Owen Lin | f52afa9 | 2012-03-23 14:59:14 +0800 | [diff] [blame] | 290 | requestedSlot[i], pass, paperActive); |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 291 | if ((r & RENDER_MORE_FRAME) != 0) more = true; |
| 292 | if ((r & RENDER_MORE_PASS) != 0) requestedSlot[newCount++] = i; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 293 | } |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 294 | requestCount = newCount; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 295 | } |
| 296 | |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 297 | canvas.translate(mScrollX, mScrollY); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 298 | |
| 299 | if (more) invalidate(); |
Owen Lin | c907c32 | 2011-11-17 15:26:56 +0800 | [diff] [blame] | 300 | |
| 301 | final UserInteractionListener listener = mUIListener; |
| 302 | if (mMoreAnimation && !more && listener != null) { |
| 303 | mHandler.post(new Runnable() { |
| 304 | @Override |
| 305 | public void run() { |
| 306 | listener.onUserInteractionEnd(); |
| 307 | } |
| 308 | }); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 309 | } |
| 310 | mMoreAnimation = more; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 311 | } |
| 312 | |
Owen Lin | f52afa9 | 2012-03-23 14:59:14 +0800 | [diff] [blame] | 313 | private int renderItem( |
| 314 | GLCanvas canvas, int index, int pass, boolean paperActive) { |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 315 | canvas.save(GLCanvas.SAVE_FLAG_ALPHA | GLCanvas.SAVE_FLAG_MATRIX); |
Owen Lin | d705545 | 2012-03-29 14:27:58 +0800 | [diff] [blame] | 316 | Rect rect = mLayout.getSlotRect(index, mTempRect); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 317 | if (paperActive) { |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 318 | canvas.multiplyMatrix(mPaper.getTransform(rect, mScrollX), 0); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 319 | } else { |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 320 | canvas.translate(rect.left, rect.top, 0); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 321 | } |
Owen Lin | f52afa9 | 2012-03-23 14:59:14 +0800 | [diff] [blame] | 322 | if (mAnimation != null && mAnimation.isActive()) { |
| 323 | mAnimation.apply(canvas, index, rect); |
| 324 | } |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 325 | int result = mRenderer.renderSlot( |
| 326 | canvas, index, pass, rect.right - rect.left, rect.bottom - rect.top); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 327 | canvas.restore(); |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 328 | return result; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 329 | } |
| 330 | |
Owen Lin | f52afa9 | 2012-03-23 14:59:14 +0800 | [diff] [blame] | 331 | public static abstract class SlotAnimation extends Animation { |
| 332 | protected float mProgress = 0; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 333 | |
Owen Lin | f52afa9 | 2012-03-23 14:59:14 +0800 | [diff] [blame] | 334 | public SlotAnimation() { |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 335 | setInterpolator(new DecelerateInterpolator(4)); |
| 336 | setDuration(1500); |
| 337 | } |
| 338 | |
| 339 | @Override |
| 340 | protected void onCalculate(float progress) { |
Owen Lin | f52afa9 | 2012-03-23 14:59:14 +0800 | [diff] [blame] | 341 | mProgress = progress; |
| 342 | } |
| 343 | |
| 344 | abstract public void apply(GLCanvas canvas, int slotIndex, Rect target); |
| 345 | } |
| 346 | |
| 347 | public static class RisingAnimation extends SlotAnimation { |
| 348 | private static final int RISING_DISTANCE = 128; |
| 349 | |
| 350 | @Override |
| 351 | public void apply(GLCanvas canvas, int slotIndex, Rect target) { |
| 352 | canvas.translate(0, 0, RISING_DISTANCE * (1 - mProgress)); |
| 353 | } |
| 354 | } |
| 355 | |
| 356 | public static class ScatteringAnimation extends SlotAnimation { |
| 357 | private int PHOTO_DISTANCE = 1000; |
| 358 | private RelativePosition mCenter; |
| 359 | |
| 360 | public ScatteringAnimation(RelativePosition center) { |
| 361 | mCenter = center; |
| 362 | } |
| 363 | |
| 364 | @Override |
| 365 | public void apply(GLCanvas canvas, int slotIndex, Rect target) { |
| 366 | canvas.translate( |
| 367 | (mCenter.getX() - target.centerX()) * (1 - mProgress), |
| 368 | (mCenter.getY() - target.centerY()) * (1 - mProgress), |
| 369 | slotIndex * PHOTO_DISTANCE * (1 - mProgress)); |
| 370 | canvas.setAlpha(mProgress); |
| 371 | } |
| 372 | } |
| 373 | |
Chih-Chung Chang | 07069de | 2011-09-14 20:50:28 +0800 | [diff] [blame] | 374 | // This Spec class is used to specify the size of each slot in the SlotView. |
| 375 | // There are two ways to do it: |
| 376 | // |
| 377 | // (1) Specify slotWidth and slotHeight: they specify the width and height |
| 378 | // of each slot. The number of rows and the gap between slots will be |
| 379 | // determined automatically. |
| 380 | // (2) Specify rowsLand, rowsPort, and slotGap: they specify the number |
| 381 | // of rows in landscape/portrait mode and the gap between slots. The |
| 382 | // width and height of each slot is determined automatically. |
| 383 | // |
| 384 | // The initial value of -1 means they are not specified. |
| 385 | public static class Spec { |
| 386 | public int slotWidth = -1; |
| 387 | public int slotHeight = -1; |
Bobby Georgescu | 93d87ff | 2012-08-23 13:05:53 -0700 | [diff] [blame] | 388 | public int slotHeightAdditional = 0; |
Chih-Chung Chang | 07069de | 2011-09-14 20:50:28 +0800 | [diff] [blame] | 389 | |
| 390 | public int rowsLand = -1; |
| 391 | public int rowsPort = -1; |
| 392 | public int slotGap = -1; |
Chih-Chung Chang | 07069de | 2011-09-14 20:50:28 +0800 | [diff] [blame] | 393 | } |
| 394 | |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 395 | public class Layout { |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 396 | |
| 397 | private int mVisibleStart; |
| 398 | private int mVisibleEnd; |
| 399 | |
| 400 | private int mSlotCount; |
| 401 | private int mSlotWidth; |
| 402 | private int mSlotHeight; |
Chih-Chung Chang | 07069de | 2011-09-14 20:50:28 +0800 | [diff] [blame] | 403 | private int mSlotGap; |
| 404 | |
| 405 | private Spec mSpec; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 406 | |
| 407 | private int mWidth; |
| 408 | private int mHeight; |
| 409 | |
| 410 | private int mUnitCount; |
| 411 | private int mContentLength; |
| 412 | private int mScrollPosition; |
| 413 | |
Owen Lin | c88fabf | 2012-05-09 11:32:32 +0800 | [diff] [blame] | 414 | private IntegerAnimation mVerticalPadding = new IntegerAnimation(); |
| 415 | private IntegerAnimation mHorizontalPadding = new IntegerAnimation(); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 416 | |
Chih-Chung Chang | 07069de | 2011-09-14 20:50:28 +0800 | [diff] [blame] | 417 | public void setSlotSpec(Spec spec) { |
| 418 | mSpec = spec; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 419 | } |
| 420 | |
| 421 | public boolean setSlotCount(int slotCount) { |
Owen Lin | c88fabf | 2012-05-09 11:32:32 +0800 | [diff] [blame] | 422 | if (slotCount == mSlotCount) return false; |
| 423 | if (mSlotCount != 0) { |
| 424 | mHorizontalPadding.setEnabled(true); |
| 425 | mVerticalPadding.setEnabled(true); |
| 426 | } |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 427 | mSlotCount = slotCount; |
Owen Lin | c88fabf | 2012-05-09 11:32:32 +0800 | [diff] [blame] | 428 | int hPadding = mHorizontalPadding.getTarget(); |
| 429 | int vPadding = mVerticalPadding.getTarget(); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 430 | initLayoutParameters(); |
Owen Lin | c88fabf | 2012-05-09 11:32:32 +0800 | [diff] [blame] | 431 | return vPadding != mVerticalPadding.getTarget() |
| 432 | || hPadding != mHorizontalPadding.getTarget(); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 433 | } |
| 434 | |
Owen Lin | d705545 | 2012-03-29 14:27:58 +0800 | [diff] [blame] | 435 | public Rect getSlotRect(int index, Rect rect) { |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 436 | int col, row; |
| 437 | if (WIDE) { |
| 438 | col = index / mUnitCount; |
| 439 | row = index - col * mUnitCount; |
| 440 | } else { |
| 441 | row = index / mUnitCount; |
| 442 | col = index - row * mUnitCount; |
| 443 | } |
| 444 | |
Owen Lin | c88fabf | 2012-05-09 11:32:32 +0800 | [diff] [blame] | 445 | int x = mHorizontalPadding.get() + col * (mSlotWidth + mSlotGap); |
| 446 | int y = mVerticalPadding.get() + row * (mSlotHeight + mSlotGap); |
Owen Lin | d705545 | 2012-03-29 14:27:58 +0800 | [diff] [blame] | 447 | rect.set(x, y, x + mSlotWidth, y + mSlotHeight); |
| 448 | return rect; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 449 | } |
| 450 | |
Chih-Chung Chang | 07069de | 2011-09-14 20:50:28 +0800 | [diff] [blame] | 451 | public int getSlotWidth() { |
| 452 | return mSlotWidth; |
| 453 | } |
| 454 | |
| 455 | public int getSlotHeight() { |
| 456 | return mSlotHeight; |
| 457 | } |
| 458 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 459 | // Calculate |
| 460 | // (1) mUnitCount: the number of slots we can fit into one column (or row). |
| 461 | // (2) mContentLength: the width (or height) we need to display all the |
| 462 | // columns (rows). |
| 463 | // (3) padding[]: the vertical and horizontal padding we need in order |
| 464 | // to put the slots towards to the center of the display. |
| 465 | // |
| 466 | // The "major" direction is the direction the user can scroll. The other |
| 467 | // direction is the "minor" direction. |
| 468 | // |
| 469 | // The comments inside this method are the description when the major |
| 470 | // directon is horizontal (X), and the minor directon is vertical (Y). |
| 471 | private void initLayoutParameters( |
| 472 | int majorLength, int minorLength, /* The view width and height */ |
| 473 | int majorUnitSize, int minorUnitSize, /* The slot width and height */ |
| 474 | int[] padding) { |
Chih-Chung Chang | 07069de | 2011-09-14 20:50:28 +0800 | [diff] [blame] | 475 | int unitCount = (minorLength + mSlotGap) / (minorUnitSize + mSlotGap); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 476 | if (unitCount == 0) unitCount = 1; |
| 477 | mUnitCount = unitCount; |
| 478 | |
| 479 | // We put extra padding above and below the column. |
| 480 | int availableUnits = Math.min(mUnitCount, mSlotCount); |
Chih-Chung Chang | 07069de | 2011-09-14 20:50:28 +0800 | [diff] [blame] | 481 | int usedMinorLength = availableUnits * minorUnitSize + |
| 482 | (availableUnits - 1) * mSlotGap; |
| 483 | padding[0] = (minorLength - usedMinorLength) / 2; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 484 | |
| 485 | // Then calculate how many columns we need for all slots. |
| 486 | int count = ((mSlotCount + mUnitCount - 1) / mUnitCount); |
Chih-Chung Chang | 07069de | 2011-09-14 20:50:28 +0800 | [diff] [blame] | 487 | mContentLength = count * majorUnitSize + (count - 1) * mSlotGap; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 488 | |
| 489 | // If the content length is less then the screen width, put |
| 490 | // extra padding in left and right. |
| 491 | padding[1] = Math.max(0, (majorLength - mContentLength) / 2); |
| 492 | } |
| 493 | |
| 494 | private void initLayoutParameters() { |
Chih-Chung Chang | 07069de | 2011-09-14 20:50:28 +0800 | [diff] [blame] | 495 | // Initialize mSlotWidth and mSlotHeight from mSpec |
| 496 | if (mSpec.slotWidth != -1) { |
| 497 | mSlotGap = 0; |
| 498 | mSlotWidth = mSpec.slotWidth; |
| 499 | mSlotHeight = mSpec.slotHeight; |
| 500 | } else { |
| 501 | int rows = (mWidth > mHeight) ? mSpec.rowsLand : mSpec.rowsPort; |
| 502 | mSlotGap = mSpec.slotGap; |
| 503 | mSlotHeight = Math.max(1, (mHeight - (rows - 1) * mSlotGap) / rows); |
Bobby Georgescu | 93d87ff | 2012-08-23 13:05:53 -0700 | [diff] [blame] | 504 | mSlotWidth = mSlotHeight - mSpec.slotHeightAdditional; |
Chih-Chung Chang | 07069de | 2011-09-14 20:50:28 +0800 | [diff] [blame] | 505 | } |
| 506 | |
Owen Lin | 8123444 | 2012-03-26 18:28:48 +0800 | [diff] [blame] | 507 | if (mRenderer != null) { |
| 508 | mRenderer.onSlotSizeChanged(mSlotWidth, mSlotHeight); |
| 509 | } |
| 510 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 511 | int[] padding = new int[2]; |
| 512 | if (WIDE) { |
| 513 | initLayoutParameters(mWidth, mHeight, mSlotWidth, mSlotHeight, padding); |
Owen Lin | c88fabf | 2012-05-09 11:32:32 +0800 | [diff] [blame] | 514 | mVerticalPadding.startAnimateTo(padding[0]); |
| 515 | mHorizontalPadding.startAnimateTo(padding[1]); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 516 | } else { |
| 517 | initLayoutParameters(mHeight, mWidth, mSlotHeight, mSlotWidth, padding); |
Owen Lin | c88fabf | 2012-05-09 11:32:32 +0800 | [diff] [blame] | 518 | mVerticalPadding.startAnimateTo(padding[1]); |
| 519 | mHorizontalPadding.startAnimateTo(padding[0]); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 520 | } |
| 521 | updateVisibleSlotRange(); |
| 522 | } |
| 523 | |
| 524 | public void setSize(int width, int height) { |
| 525 | mWidth = width; |
| 526 | mHeight = height; |
| 527 | initLayoutParameters(); |
| 528 | } |
| 529 | |
| 530 | private void updateVisibleSlotRange() { |
| 531 | int position = mScrollPosition; |
| 532 | |
| 533 | if (WIDE) { |
Chih-Chung Chang | 07069de | 2011-09-14 20:50:28 +0800 | [diff] [blame] | 534 | int startCol = position / (mSlotWidth + mSlotGap); |
| 535 | int start = Math.max(0, mUnitCount * startCol); |
| 536 | int endCol = (position + mWidth + mSlotWidth + mSlotGap - 1) / |
| 537 | (mSlotWidth + mSlotGap); |
| 538 | int end = Math.min(mSlotCount, mUnitCount * endCol); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 539 | setVisibleRange(start, end); |
| 540 | } else { |
Chih-Chung Chang | 07069de | 2011-09-14 20:50:28 +0800 | [diff] [blame] | 541 | int startRow = position / (mSlotHeight + mSlotGap); |
| 542 | int start = Math.max(0, mUnitCount * startRow); |
| 543 | int endRow = (position + mHeight + mSlotHeight + mSlotGap - 1) / |
| 544 | (mSlotHeight + mSlotGap); |
| 545 | int end = Math.min(mSlotCount, mUnitCount * endRow); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 546 | setVisibleRange(start, end); |
| 547 | } |
| 548 | } |
| 549 | |
| 550 | public void setScrollPosition(int position) { |
| 551 | if (mScrollPosition == position) return; |
| 552 | mScrollPosition = position; |
| 553 | updateVisibleSlotRange(); |
| 554 | } |
| 555 | |
| 556 | private void setVisibleRange(int start, int end) { |
| 557 | if (start == mVisibleStart && end == mVisibleEnd) return; |
| 558 | if (start < end) { |
| 559 | mVisibleStart = start; |
| 560 | mVisibleEnd = end; |
| 561 | } else { |
| 562 | mVisibleStart = mVisibleEnd = 0; |
| 563 | } |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 564 | if (mRenderer != null) { |
| 565 | mRenderer.onVisibleRangeChanged(mVisibleStart, mVisibleEnd); |
| 566 | } |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 567 | } |
| 568 | |
| 569 | public int getVisibleStart() { |
| 570 | return mVisibleStart; |
| 571 | } |
| 572 | |
| 573 | public int getVisibleEnd() { |
| 574 | return mVisibleEnd; |
| 575 | } |
| 576 | |
| 577 | public int getSlotIndexByPosition(float x, float y) { |
Chih-Chung Chang | 07069de | 2011-09-14 20:50:28 +0800 | [diff] [blame] | 578 | int absoluteX = Math.round(x) + (WIDE ? mScrollPosition : 0); |
| 579 | int absoluteY = Math.round(y) + (WIDE ? 0 : mScrollPosition); |
| 580 | |
Owen Lin | c88fabf | 2012-05-09 11:32:32 +0800 | [diff] [blame] | 581 | absoluteX -= mHorizontalPadding.get(); |
| 582 | absoluteY -= mVerticalPadding.get(); |
Chih-Chung Chang | 07069de | 2011-09-14 20:50:28 +0800 | [diff] [blame] | 583 | |
Chih-Chung Chang | 809bff6 | 2011-10-31 11:45:11 +0800 | [diff] [blame] | 584 | if (absoluteX < 0 || absoluteY < 0) { |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 585 | return INDEX_NONE; |
| 586 | } |
| 587 | |
Chih-Chung Chang | 809bff6 | 2011-10-31 11:45:11 +0800 | [diff] [blame] | 588 | int columnIdx = absoluteX / (mSlotWidth + mSlotGap); |
| 589 | int rowIdx = absoluteY / (mSlotHeight + mSlotGap); |
| 590 | |
| 591 | if (!WIDE && columnIdx >= mUnitCount) { |
| 592 | return INDEX_NONE; |
| 593 | } |
| 594 | |
| 595 | if (WIDE && rowIdx >= mUnitCount) { |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 596 | return INDEX_NONE; |
| 597 | } |
Chih-Chung Chang | 07069de | 2011-09-14 20:50:28 +0800 | [diff] [blame] | 598 | |
| 599 | if (absoluteX % (mSlotWidth + mSlotGap) >= mSlotWidth) { |
| 600 | return INDEX_NONE; |
| 601 | } |
| 602 | |
| 603 | if (absoluteY % (mSlotHeight + mSlotGap) >= mSlotHeight) { |
| 604 | return INDEX_NONE; |
| 605 | } |
| 606 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 607 | int index = WIDE |
| 608 | ? (columnIdx * mUnitCount + rowIdx) |
| 609 | : (rowIdx * mUnitCount + columnIdx); |
| 610 | |
| 611 | return index >= mSlotCount ? INDEX_NONE : index; |
| 612 | } |
| 613 | |
| 614 | public int getScrollLimit() { |
| 615 | int limit = WIDE ? mContentLength - mWidth : mContentLength - mHeight; |
| 616 | return limit <= 0 ? 0 : limit; |
| 617 | } |
Owen Lin | c88fabf | 2012-05-09 11:32:32 +0800 | [diff] [blame] | 618 | |
| 619 | public boolean advanceAnimation(long animTime) { |
| 620 | // use '|' to make sure both sides will be executed |
| 621 | return mVerticalPadding.calculate(animTime) | mHorizontalPadding.calculate(animTime); |
| 622 | } |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 623 | } |
| 624 | |
Owen Lin | 3c21751 | 2012-04-17 15:38:36 +0800 | [diff] [blame] | 625 | private class MyGestureListener implements GestureDetector.OnGestureListener { |
Chih-Chung Chang | 70a73a7 | 2011-09-19 11:09:39 +0800 | [diff] [blame] | 626 | private boolean isDown; |
| 627 | |
| 628 | // We call the listener's onDown() when our onShowPress() is called and |
| 629 | // call the listener's onUp() when we receive any further event. |
| 630 | @Override |
| 631 | public void onShowPress(MotionEvent e) { |
Owen Lin | 3c21751 | 2012-04-17 15:38:36 +0800 | [diff] [blame] | 632 | GLRoot root = getGLRoot(); |
| 633 | root.lockRenderThread(); |
| 634 | try { |
| 635 | if (isDown) return; |
| 636 | int index = mLayout.getSlotIndexByPosition(e.getX(), e.getY()); |
| 637 | if (index != INDEX_NONE) { |
| 638 | isDown = true; |
| 639 | mListener.onDown(index); |
| 640 | } |
| 641 | } finally { |
| 642 | root.unlockRenderThread(); |
Chih-Chung Chang | 70a73a7 | 2011-09-19 11:09:39 +0800 | [diff] [blame] | 643 | } |
| 644 | } |
| 645 | |
Yuli Huang | 4072ab9 | 2012-04-24 15:20:28 +0800 | [diff] [blame] | 646 | private void cancelDown(boolean byLongPress) { |
Chih-Chung Chang | 70a73a7 | 2011-09-19 11:09:39 +0800 | [diff] [blame] | 647 | if (!isDown) return; |
| 648 | isDown = false; |
Yuli Huang | 4072ab9 | 2012-04-24 15:20:28 +0800 | [diff] [blame] | 649 | mListener.onUp(byLongPress); |
Chih-Chung Chang | 70a73a7 | 2011-09-19 11:09:39 +0800 | [diff] [blame] | 650 | } |
| 651 | |
| 652 | @Override |
| 653 | public boolean onDown(MotionEvent e) { |
| 654 | return false; |
| 655 | } |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 656 | |
| 657 | @Override |
| 658 | public boolean onFling(MotionEvent e1, |
| 659 | MotionEvent e2, float velocityX, float velocityY) { |
Yuli Huang | 4072ab9 | 2012-04-24 15:20:28 +0800 | [diff] [blame] | 660 | cancelDown(false); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 661 | int scrollLimit = mLayout.getScrollLimit(); |
| 662 | if (scrollLimit == 0) return false; |
| 663 | float velocity = WIDE ? velocityX : velocityY; |
| 664 | mScroller.fling((int) -velocity, 0, scrollLimit); |
| 665 | if (mUIListener != null) mUIListener.onUserInteractionBegin(); |
| 666 | invalidate(); |
| 667 | return true; |
| 668 | } |
| 669 | |
| 670 | @Override |
| 671 | public boolean onScroll(MotionEvent e1, |
| 672 | MotionEvent e2, float distanceX, float distanceY) { |
Yuli Huang | 4072ab9 | 2012-04-24 15:20:28 +0800 | [diff] [blame] | 673 | cancelDown(false); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 674 | float distance = WIDE ? distanceX : distanceY; |
Chih-Chung Chang | ca07852 | 2011-09-26 10:40:38 +0800 | [diff] [blame] | 675 | int overDistance = mScroller.startScroll( |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 676 | Math.round(distance), 0, mLayout.getScrollLimit()); |
Chih-Chung Chang | ca07852 | 2011-09-26 10:40:38 +0800 | [diff] [blame] | 677 | if (mOverscrollEffect == OVERSCROLL_3D && overDistance != 0) { |
| 678 | mPaper.overScroll(overDistance); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 679 | } |
| 680 | invalidate(); |
| 681 | return true; |
| 682 | } |
| 683 | |
| 684 | @Override |
| 685 | public boolean onSingleTapUp(MotionEvent e) { |
Yuli Huang | 4072ab9 | 2012-04-24 15:20:28 +0800 | [diff] [blame] | 686 | cancelDown(false); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 687 | if (mDownInScrolling) return true; |
| 688 | int index = mLayout.getSlotIndexByPosition(e.getX(), e.getY()); |
| 689 | if (index != INDEX_NONE) mListener.onSingleTapUp(index); |
| 690 | return true; |
| 691 | } |
| 692 | |
| 693 | @Override |
| 694 | public void onLongPress(MotionEvent e) { |
Yuli Huang | 4072ab9 | 2012-04-24 15:20:28 +0800 | [diff] [blame] | 695 | cancelDown(true); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 696 | if (mDownInScrolling) return; |
| 697 | lockRendering(); |
| 698 | try { |
| 699 | int index = mLayout.getSlotIndexByPosition(e.getX(), e.getY()); |
| 700 | if (index != INDEX_NONE) mListener.onLongTap(index); |
| 701 | } finally { |
| 702 | unlockRendering(); |
| 703 | } |
| 704 | } |
| 705 | } |
| 706 | |
| 707 | public void setStartIndex(int index) { |
| 708 | mStartIndex = index; |
| 709 | } |
| 710 | |
| 711 | // Return true if the layout parameters have been changed |
| 712 | public boolean setSlotCount(int slotCount) { |
| 713 | boolean changed = mLayout.setSlotCount(slotCount); |
| 714 | |
| 715 | // mStartIndex is applied the first time setSlotCount is called. |
| 716 | if (mStartIndex != INDEX_NONE) { |
| 717 | setCenterIndex(mStartIndex); |
| 718 | mStartIndex = INDEX_NONE; |
| 719 | } |
Yuli Huang | b8e94ce | 2012-02-13 19:55:58 +0800 | [diff] [blame] | 720 | // Reset the scroll position to avoid scrolling over the updated limit. |
| 721 | setScrollPosition(WIDE ? mScrollX : mScrollY); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 722 | return changed; |
| 723 | } |
| 724 | |
| 725 | public int getVisibleStart() { |
| 726 | return mLayout.getVisibleStart(); |
| 727 | } |
| 728 | |
| 729 | public int getVisibleEnd() { |
| 730 | return mLayout.getVisibleEnd(); |
| 731 | } |
| 732 | |
| 733 | public int getScrollX() { |
| 734 | return mScrollX; |
| 735 | } |
| 736 | |
| 737 | public int getScrollY() { |
| 738 | return mScrollY; |
| 739 | } |
Owen Lin | c88fabf | 2012-05-09 11:32:32 +0800 | [diff] [blame] | 740 | |
Bobby Georgescu | ee96fa8 | 2012-09-13 15:46:40 -0700 | [diff] [blame] | 741 | public Rect getSlotRect(int slotIndex, GLView rootPane) { |
| 742 | // Get slot rectangle relative to this root pane. |
| 743 | Rect offset = new Rect(); |
| 744 | rootPane.getBoundsOf(this, offset); |
| 745 | Rect r = getSlotRect(slotIndex); |
| 746 | r.offset(offset.left - getScrollX(), |
| 747 | offset.top - getScrollY()); |
| 748 | return r; |
| 749 | } |
| 750 | |
Owen Lin | c88fabf | 2012-05-09 11:32:32 +0800 | [diff] [blame] | 751 | private static class IntegerAnimation extends Animation { |
| 752 | private int mTarget; |
| 753 | private int mCurrent = 0; |
| 754 | private int mFrom = 0; |
| 755 | private boolean mEnabled = false; |
| 756 | |
| 757 | public void setEnabled(boolean enabled) { |
| 758 | mEnabled = enabled; |
| 759 | } |
| 760 | |
| 761 | public void startAnimateTo(int target) { |
| 762 | if (!mEnabled) { |
| 763 | mTarget = mCurrent = target; |
| 764 | return; |
| 765 | } |
| 766 | if (target == mTarget) return; |
| 767 | |
| 768 | mFrom = mCurrent; |
| 769 | mTarget = target; |
| 770 | setDuration(180); |
| 771 | start(); |
| 772 | } |
| 773 | |
| 774 | public int get() { |
| 775 | return mCurrent; |
| 776 | } |
| 777 | |
| 778 | public int getTarget() { |
| 779 | return mTarget; |
| 780 | } |
| 781 | |
| 782 | @Override |
| 783 | protected void onCalculate(float progress) { |
| 784 | mCurrent = Math.round(mFrom + progress * (mTarget - mFrom)); |
| 785 | if (progress == 1f) mEnabled = false; |
| 786 | } |
| 787 | } |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 788 | } |