SlotView.java revision f9a0a4306d589b4a4e20554fed512a603426bfa1
1f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin/* 2f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Copyright (C) 2010 The Android Open Source Project 3f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * 4f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Licensed under the Apache License, Version 2.0 (the "License"); 5f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * you may not use this file except in compliance with the License. 6f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * You may obtain a copy of the License at 7f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * 8f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * http://www.apache.org/licenses/LICENSE-2.0 9f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * 10f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Unless required by applicable law or agreed to in writing, software 11f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * distributed under the License is distributed on an "AS IS" BASIS, 12f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * See the License for the specific language governing permissions and 14f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * limitations under the License. 15f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin */ 16f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 17f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpackage com.android.gallery3d.ui; 18f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 19f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.anim.Animation; 20f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.common.Utils; 21f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.ui.PositionRepository.Position; 22f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.util.LinkedNode; 23f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 24f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.content.Context; 25f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.Rect; 26f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.view.GestureDetector; 27f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.view.MotionEvent; 28f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.view.animation.DecelerateInterpolator; 29f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 30f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.ArrayList; 31f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.HashMap; 32f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 33f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpublic class SlotView extends GLView { 34f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @SuppressWarnings("unused") 35f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final String TAG = "SlotView"; 36f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 37f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final boolean WIDE = true; 38f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 39f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int INDEX_NONE = -1; 40f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 41f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public interface Listener { 42f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void onSingleTapUp(int index); 43f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void onLongTap(int index); 44f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void onScrollPositionChanged(int position, int total); 45f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 46f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 47f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public static class SimpleListener implements Listener { 48f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void onSingleTapUp(int index) {} 49f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void onLongTap(int index) {} 50f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void onScrollPositionChanged(int position, int total) {} 51f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 52f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 53f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private final GestureDetector mGestureDetector; 54f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private final ScrollerHelper mScroller; 55f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private final Paper mPaper = new Paper(); 56f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 57f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private Listener mListener; 58f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private UserInteractionListener mUIListener; 59f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 60f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Use linked hash map to keep the rendering order 61f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private HashMap<DisplayItem, ItemEntry> mItems = 62f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin new HashMap<DisplayItem, ItemEntry>(); 63f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 64f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public LinkedNode.List<ItemEntry> mItemList = LinkedNode.newList(); 65f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 66f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // This is used for multipass rendering 67f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private ArrayList<ItemEntry> mCurrentItems = new ArrayList<ItemEntry>(); 68f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private ArrayList<ItemEntry> mNextItems = new ArrayList<ItemEntry>(); 69f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 70f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private boolean mMoreAnimation = false; 71f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private MyAnimation mAnimation = null; 72f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private final Position mTempPosition = new Position(); 73f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private final Layout mLayout = new Layout(); 74f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private PositionProvider mPositions; 75f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mStartIndex = INDEX_NONE; 76f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 77f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // whether the down action happened while the view is scrolling. 78f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private boolean mDownInScrolling; 79f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mOverscrollEffect = OVERSCROLL_3D; 80f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 81f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public static final int OVERSCROLL_3D = 0; 82f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public static final int OVERSCROLL_SYSTEM = 1; 83f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public static final int OVERSCROLL_NONE = 2; 84f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 85f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public SlotView(Context context) { 86f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mGestureDetector = 87f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin new GestureDetector(context, new MyGestureListener()); 88f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mScroller = new ScrollerHelper(context); 89f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 90f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 91f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void setCenterIndex(int index) { 92f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int slotCount = mLayout.mSlotCount; 93f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (index < 0 || index >= slotCount) { 94f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return; 95f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 96f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Rect rect = mLayout.getSlotRect(index); 97f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int position = WIDE 98f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin ? (rect.left + rect.right - getWidth()) / 2 99f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin : (rect.top + rect.bottom - getHeight()) / 2; 100f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin setScrollPosition(position); 101f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 102f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 103f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void makeSlotVisible(int index) { 104f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Rect rect = mLayout.getSlotRect(index); 105f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int visibleBegin = WIDE ? mScrollX : mScrollY; 106f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int visibleLength = WIDE ? getWidth() : getHeight(); 107f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int visibleEnd = visibleBegin + visibleLength; 108f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int slotBegin = WIDE ? rect.left : rect.top; 109f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int slotEnd = WIDE ? rect.right : rect.bottom; 110f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 111f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int position = visibleBegin; 112f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (visibleLength < slotEnd - slotBegin) { 113f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin position = visibleBegin; 114f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else if (slotBegin < visibleBegin) { 115f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin position = slotBegin; 116f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else if (slotEnd > visibleEnd) { 117f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin position = slotEnd - visibleLength; 118f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 119f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 120f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin setScrollPosition(position); 121f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 122f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 123f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void setScrollPosition(int position) { 124f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin position = Utils.clamp(position, 0, mLayout.getScrollLimit()); 125f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mScroller.setPosition(position); 126f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin updateScrollPosition(position, false); 127f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 128f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 129f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void setSlotSize(int slotWidth, int slotHeight) { 130f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mLayout.setSlotSize(slotWidth, slotHeight); 131f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 132f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 133f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @Override 134f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void addComponent(GLView view) { 135f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin throw new UnsupportedOperationException(); 136f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 137f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 138f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @Override 139f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public boolean removeComponent(GLView view) { 140f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin throw new UnsupportedOperationException(); 141f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 142f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 143f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @Override 144f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin protected void onLayout(boolean changeSize, int l, int t, int r, int b) { 145f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (!changeSize) return; 146f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mLayout.setSize(r - l, b - t); 147f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin onLayoutChanged(r - l, b - t); 148f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mOverscrollEffect == OVERSCROLL_3D) { 149f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mPaper.setSize(r - l, b - t); 150f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 151f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 152f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 153f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin protected void onLayoutChanged(int width, int height) { 154f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 155f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 156f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void startTransition(PositionProvider position) { 157f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mPositions = position; 158f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mAnimation = new MyAnimation(); 159f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mAnimation.start(); 160f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mItems.size() != 0) invalidate(); 161f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 162f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 163f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void savePositions(PositionRepository repository) { 164f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin repository.clear(); 165f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin LinkedNode.List<ItemEntry> list = mItemList; 166f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin ItemEntry entry = list.getFirst(); 167f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Position position = new Position(); 168f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin while (entry != null) { 169f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin position.set(entry.target); 170f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin position.x -= mScrollX; 171f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin position.y -= mScrollY; 172f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin repository.putPosition(entry.item.getIdentity(), position); 173f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin entry = list.nextOf(entry); 174f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 175f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 176f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 177f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private void updateScrollPosition(int position, boolean force) { 178f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (!force && (WIDE ? position == mScrollX : position == mScrollY)) return; 179f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (WIDE) { 180f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mScrollX = position; 181f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else { 182f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mScrollY = position; 183f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 184f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mLayout.setScrollPosition(position); 185f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin onScrollPositionChanged(position); 186f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 187f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 188f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin protected void onScrollPositionChanged(int newPosition) { 189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int limit = mLayout.getScrollLimit(); 190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mListener.onScrollPositionChanged(newPosition, limit); 191f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 192f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 193f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void putDisplayItem(Position target, Position base, DisplayItem item) { 194f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin ItemEntry entry = new ItemEntry(item, target, base); 195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mItemList.insertLast(entry); 196f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mItems.put(item, entry); 197f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 198f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 199f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void removeDisplayItem(DisplayItem item) { 200f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin ItemEntry entry = mItems.remove(item); 201f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (entry != null) entry.remove(); 202f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 203f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 204f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public Rect getSlotRect(int slotIndex) { 205f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return mLayout.getSlotRect(slotIndex); 206f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 207f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 208f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @Override 209f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin protected boolean onTouch(MotionEvent event) { 210f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mUIListener != null) mUIListener.onUserInteraction(); 211f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mGestureDetector.onTouchEvent(event); 212f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin switch (event.getAction()) { 213f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin case MotionEvent.ACTION_DOWN: 214f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mDownInScrolling = !mScroller.isFinished(); 215f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mScroller.forceFinished(); 216f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin break; 217f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 218f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return true; 219f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 220f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 221f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void setListener(Listener listener) { 222f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mListener = listener; 223f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 224f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 225f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void setUserInteractionListener(UserInteractionListener listener) { 226f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mUIListener = listener; 227f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 228f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 229f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void setOverscrollEffect(int kind) { 230f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mOverscrollEffect = kind; 231f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mScroller.setOverfling(kind == OVERSCROLL_SYSTEM); 232f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 233f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 234f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @Override 235f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin protected void render(GLCanvas canvas) { 236f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin canvas.save(GLCanvas.SAVE_FLAG_CLIP); 237f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin canvas.clipRect(0, 0, getWidth(), getHeight()); 238f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin super.render(canvas); 239f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 240f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin long currentTimeMillis = canvas.currentAnimationTimeMillis(); 241f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin boolean more = mScroller.advanceAnimation(currentTimeMillis); 242f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin boolean paperActive = (mOverscrollEffect == OVERSCROLL_3D) 243f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin && mPaper.advanceAnimation(currentTimeMillis); 244f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin updateScrollPosition(mScroller.getPosition(), false); 245f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin float interpolate = 1f; 246f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mAnimation != null) { 247f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin more |= mAnimation.calculate(currentTimeMillis); 248f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin interpolate = mAnimation.value; 249f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 250f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 251f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin more |= paperActive; 252f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 253f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (WIDE) { 254f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin canvas.translate(-mScrollX, 0, 0); 255f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else { 256f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin canvas.translate(0, -mScrollY, 0); 257f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 258f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 259f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin LinkedNode.List<ItemEntry> list = mItemList; 260f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin for (ItemEntry entry = list.getLast(); entry != null;) { 261f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (renderItem(canvas, entry, interpolate, 0, paperActive)) { 262f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mCurrentItems.add(entry); 263f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 264f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin entry = list.previousOf(entry); 265f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 266f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 267f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int pass = 1; 268f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin while (!mCurrentItems.isEmpty()) { 269f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin for (int i = 0, n = mCurrentItems.size(); i < n; i++) { 270f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin ItemEntry entry = mCurrentItems.get(i); 271f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (renderItem(canvas, entry, interpolate, pass, paperActive)) { 272f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mNextItems.add(entry); 273f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 274f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 275f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mCurrentItems.clear(); 276f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // swap mNextItems with mCurrentItems 277f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin ArrayList<ItemEntry> tmp = mNextItems; 278f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mNextItems = mCurrentItems; 279f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mCurrentItems = tmp; 280f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin pass += 1; 281f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 282f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 283f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (WIDE) { 284f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin canvas.translate(mScrollX, 0, 0); 285f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else { 286f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin canvas.translate(0, mScrollY, 0); 287f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 288f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 289f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (more) invalidate(); 290f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mMoreAnimation && !more && mUIListener != null) { 291f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mUIListener.onUserInteractionEnd(); 292f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 293f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mMoreAnimation = more; 294f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin canvas.restore(); 295f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 296f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 297f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private boolean renderItem(GLCanvas canvas, ItemEntry entry, 298f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin float interpolate, int pass, boolean paperActive) { 299f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin canvas.save(GLCanvas.SAVE_FLAG_ALPHA | GLCanvas.SAVE_FLAG_MATRIX); 300f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Position position = entry.target; 301f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mPositions != null) { 302f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin position = mTempPosition; 303f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin position.set(entry.target); 304f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin position.x -= mScrollX; 305f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin position.y -= mScrollY; 306f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Position source = mPositions 307f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin .getPosition(entry.item.getIdentity(), position); 308f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin source.x += mScrollX; 309f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin source.y += mScrollY; 310f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin position = mTempPosition; 311f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Position.interpolate( 312f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin source, entry.target, position, interpolate); 313f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 314f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin canvas.multiplyAlpha(position.alpha); 315f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (paperActive) { 316f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin canvas.multiplyMatrix(mPaper.getTransform( 317f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin position, entry.base, mScrollX, mScrollY), 0); 318f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else { 319f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin canvas.translate(position.x, position.y, position.z); 320f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 321f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin canvas.rotate(position.theta, 0, 0, 1); 322f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin boolean more = entry.item.render(canvas, pass); 323f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin canvas.restore(); 324f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return more; 325f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 326f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 327f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public static class MyAnimation extends Animation { 328f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public float value; 329f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 330f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public MyAnimation() { 331f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin setInterpolator(new DecelerateInterpolator(4)); 332f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin setDuration(1500); 333f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 334f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 335f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @Override 336f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin protected void onCalculate(float progress) { 337f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin value = progress; 338f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 339f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 340f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 341f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static class ItemEntry extends LinkedNode { 342f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public DisplayItem item; 343f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public Position target; 344f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public Position base; 345f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 346f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public ItemEntry(DisplayItem item, Position target, Position base) { 347f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin this.item = item; 348f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin this.target = target; 349f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin this.base = base; 350f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 351f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 352f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 353f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public static class Layout { 354f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 355f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mVisibleStart; 356f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mVisibleEnd; 357f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 358f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mSlotCount; 359f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mSlotWidth; 360f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mSlotHeight; 361f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 362f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mWidth; 363f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mHeight; 364f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 365f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mUnitCount; 366f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mContentLength; 367f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mScrollPosition; 368f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 369f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mVerticalPadding; 370f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mHorizontalPadding; 371f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 372f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void setSlotSize(int slotWidth, int slotHeight) { 373f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mSlotWidth = slotWidth; 374f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mSlotHeight = slotHeight; 375f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 376f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 377f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public boolean setSlotCount(int slotCount) { 378f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mSlotCount = slotCount; 379f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int hPadding = mHorizontalPadding; 380f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int vPadding = mVerticalPadding; 381f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin initLayoutParameters(); 382f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return vPadding != mVerticalPadding || hPadding != mHorizontalPadding; 383f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 384f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 385f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public Rect getSlotRect(int index) { 386f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int col, row; 387f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (WIDE) { 388f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin col = index / mUnitCount; 389f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin row = index - col * mUnitCount; 390f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else { 391f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin row = index / mUnitCount; 392f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin col = index - row * mUnitCount; 393f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 394f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 395f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int x = mHorizontalPadding + col * mSlotWidth; 396f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int y = mVerticalPadding + row * mSlotHeight; 397f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return new Rect(x, y, x + mSlotWidth, y + mSlotHeight); 398f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 399f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 400f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public int getContentLength() { 401f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return mContentLength; 402f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 403f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 404f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Calculate 405f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // (1) mUnitCount: the number of slots we can fit into one column (or row). 406f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // (2) mContentLength: the width (or height) we need to display all the 407f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // columns (rows). 408f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // (3) padding[]: the vertical and horizontal padding we need in order 409f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // to put the slots towards to the center of the display. 410f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // 411f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // The "major" direction is the direction the user can scroll. The other 412f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // direction is the "minor" direction. 413f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // 414f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // The comments inside this method are the description when the major 415f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // directon is horizontal (X), and the minor directon is vertical (Y). 416f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private void initLayoutParameters( 417f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int majorLength, int minorLength, /* The view width and height */ 418f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int majorUnitSize, int minorUnitSize, /* The slot width and height */ 419f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int[] padding) { 420f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int unitCount = minorLength / minorUnitSize; 421f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (unitCount == 0) unitCount = 1; 422f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mUnitCount = unitCount; 423f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 424f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // We put extra padding above and below the column. 425f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int availableUnits = Math.min(mUnitCount, mSlotCount); 426f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin padding[0] = (minorLength - availableUnits * minorUnitSize) / 2; 427f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 428f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Then calculate how many columns we need for all slots. 429f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int count = ((mSlotCount + mUnitCount - 1) / mUnitCount); 430f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mContentLength = count * majorUnitSize; 431f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 432f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // If the content length is less then the screen width, put 433f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // extra padding in left and right. 434f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin padding[1] = Math.max(0, (majorLength - mContentLength) / 2); 435f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 436f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 437f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private void initLayoutParameters() { 438f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int[] padding = new int[2]; 439f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (WIDE) { 440f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin initLayoutParameters(mWidth, mHeight, mSlotWidth, mSlotHeight, padding); 441f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mVerticalPadding = padding[0]; 442f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mHorizontalPadding = padding[1]; 443f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else { 444f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin initLayoutParameters(mHeight, mWidth, mSlotHeight, mSlotWidth, padding); 445f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mVerticalPadding = padding[1]; 446f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mHorizontalPadding = padding[0]; 447f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 448f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin updateVisibleSlotRange(); 449f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 450f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 451f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void setSize(int width, int height) { 452f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mWidth = width; 453f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mHeight = height; 454f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin initLayoutParameters(); 455f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 456f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 457f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private void updateVisibleSlotRange() { 458f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int position = mScrollPosition; 459f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 460f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (WIDE) { 461f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int start = Math.max(0, (position / mSlotWidth) * mUnitCount); 462f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int end = Math.min(mSlotCount, mUnitCount 463f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * (position + mWidth + mSlotWidth - 1) / mSlotWidth); 464f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin setVisibleRange(start, end); 465f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else { 466f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int start = Math.max(0, mUnitCount * (position / mSlotHeight)); 467f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int end = Math.min(mSlotCount, mUnitCount 468f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * (position + mHeight + mSlotHeight - 1) / mSlotHeight); 469f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin setVisibleRange(start, end); 470f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 471f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 472f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 473f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void setScrollPosition(int position) { 474f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mScrollPosition == position) return; 475f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mScrollPosition = position; 476f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin updateVisibleSlotRange(); 477f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 478f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 479f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private void setVisibleRange(int start, int end) { 480f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (start == mVisibleStart && end == mVisibleEnd) return; 481f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (start < end) { 482f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mVisibleStart = start; 483f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mVisibleEnd = end; 484f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else { 485f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mVisibleStart = mVisibleEnd = 0; 486f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 487f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 488f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 489f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public int getVisibleStart() { 490f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return mVisibleStart; 491f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 492f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 493f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public int getVisibleEnd() { 494f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return mVisibleEnd; 495f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 496f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 497f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public int getSlotIndexByPosition(float x, float y) { 498f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin float absoluteX = x + (WIDE ? mScrollPosition : 0); 499f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin absoluteX -= mHorizontalPadding; 500f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int columnIdx = (int) (absoluteX + 0.5) / mSlotWidth; 501f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if ((absoluteX - mSlotWidth * columnIdx) < 0 502f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin || (!WIDE && columnIdx >= mUnitCount)) { 503f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return INDEX_NONE; 504f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 505f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 506f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin float absoluteY = y + (WIDE ? 0 : mScrollPosition); 507f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin absoluteY -= mVerticalPadding; 508f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int rowIdx = (int) (absoluteY + 0.5) / mSlotHeight; 509f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (((absoluteY - mSlotHeight * rowIdx) < 0) 510f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin || (WIDE && rowIdx >= mUnitCount)) { 511f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return INDEX_NONE; 512f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 513f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int index = WIDE 514f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin ? (columnIdx * mUnitCount + rowIdx) 515f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin : (rowIdx * mUnitCount + columnIdx); 516f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 517f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return index >= mSlotCount ? INDEX_NONE : index; 518f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 519f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 520f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public int getScrollLimit() { 521f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int limit = WIDE ? mContentLength - mWidth : mContentLength - mHeight; 522f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return limit <= 0 ? 0 : limit; 523f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 524f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 525f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 526f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private class MyGestureListener 527f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin extends GestureDetector.SimpleOnGestureListener { 528f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 529f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @Override 530f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public boolean onFling(MotionEvent e1, 531f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin MotionEvent e2, float velocityX, float velocityY) { 532f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int scrollLimit = mLayout.getScrollLimit(); 533f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (scrollLimit == 0) return false; 534f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin float velocity = WIDE ? velocityX : velocityY; 535f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mScroller.fling((int) -velocity, 0, scrollLimit); 536f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mUIListener != null) mUIListener.onUserInteractionBegin(); 537f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin invalidate(); 538f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return true; 539f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 540f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 541f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @Override 542f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public boolean onScroll(MotionEvent e1, 543f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin MotionEvent e2, float distanceX, float distanceY) { 544f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin float distance = WIDE ? distanceX : distanceY; 545f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin boolean canMove = mScroller.startScroll( 546f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Math.round(distance), 0, mLayout.getScrollLimit()); 547f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mOverscrollEffect == OVERSCROLL_3D && !canMove) { 548f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mPaper.overScroll(distance); 549f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 550f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin invalidate(); 551f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return true; 552f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 553f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 554f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @Override 555f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public boolean onSingleTapUp(MotionEvent e) { 556f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mDownInScrolling) return true; 557f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int index = mLayout.getSlotIndexByPosition(e.getX(), e.getY()); 558f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (index != INDEX_NONE) mListener.onSingleTapUp(index); 559f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return true; 560f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 561f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 562f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @Override 563f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void onLongPress(MotionEvent e) { 564f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mDownInScrolling) return; 565f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin lockRendering(); 566f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin try { 567f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int index = mLayout.getSlotIndexByPosition(e.getX(), e.getY()); 568f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (index != INDEX_NONE) mListener.onLongTap(index); 569f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } finally { 570f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin unlockRendering(); 571f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 572f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 573f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 574f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 575f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void setStartIndex(int index) { 576f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mStartIndex = index; 577f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 578f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 579f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Return true if the layout parameters have been changed 580f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public boolean setSlotCount(int slotCount) { 581f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin boolean changed = mLayout.setSlotCount(slotCount); 582f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 583f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // mStartIndex is applied the first time setSlotCount is called. 584f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mStartIndex != INDEX_NONE) { 585f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin setCenterIndex(mStartIndex); 586f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mStartIndex = INDEX_NONE; 587f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 588f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin updateScrollPosition(WIDE ? mScrollX : mScrollY, true); 589f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return changed; 590f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 591f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 592f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public int getVisibleStart() { 593f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return mLayout.getVisibleStart(); 594f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 595f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 596f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public int getVisibleEnd() { 597f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return mLayout.getVisibleEnd(); 598f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 599f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 600f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public int getScrollX() { 601f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return mScrollX; 602f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 603f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 604f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public int getScrollY() { 605f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return mScrollY; 606f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 607f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin} 608