GridViewSpecial.java revision 666ea1b28a76aeba74744148b15099254d918671
1666ea1b28a76aeba74744148b15099254d918671Owen Lin/* 2666ea1b28a76aeba74744148b15099254d918671Owen Lin * Copyright (C) 2009 The Android Open Source Project 3666ea1b28a76aeba74744148b15099254d918671Owen Lin * 4666ea1b28a76aeba74744148b15099254d918671Owen Lin * Licensed under the Apache License, Version 2.0 (the "License"); 5666ea1b28a76aeba74744148b15099254d918671Owen Lin * you may not use this file except in compliance with the License. 6666ea1b28a76aeba74744148b15099254d918671Owen Lin * You may obtain a copy of the License at 7666ea1b28a76aeba74744148b15099254d918671Owen Lin * 8666ea1b28a76aeba74744148b15099254d918671Owen Lin * http://www.apache.org/licenses/LICENSE-2.0 9666ea1b28a76aeba74744148b15099254d918671Owen Lin * 10666ea1b28a76aeba74744148b15099254d918671Owen Lin * Unless required by applicable law or agreed to in writing, software 11666ea1b28a76aeba74744148b15099254d918671Owen Lin * distributed under the License is distributed on an "AS IS" BASIS, 12666ea1b28a76aeba74744148b15099254d918671Owen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13666ea1b28a76aeba74744148b15099254d918671Owen Lin * See the License for the specific language governing permissions and 14666ea1b28a76aeba74744148b15099254d918671Owen Lin * limitations under the License. 15666ea1b28a76aeba74744148b15099254d918671Owen Lin */ 16666ea1b28a76aeba74744148b15099254d918671Owen Lin 17666ea1b28a76aeba74744148b15099254d918671Owen Linpackage com.android.camera; 18666ea1b28a76aeba74744148b15099254d918671Owen Lin 19666ea1b28a76aeba74744148b15099254d918671Owen Linimport static com.android.camera.Util.Assert; 20666ea1b28a76aeba74744148b15099254d918671Owen Lin 21666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.app.Activity; 22666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.content.Context; 23666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.graphics.Bitmap; 24666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.graphics.Canvas; 25666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.graphics.Paint; 26666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.graphics.Rect; 27666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.graphics.drawable.Drawable; 28666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.media.AudioManager; 29666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.os.Handler; 30666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.util.AttributeSet; 31666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.util.DisplayMetrics; 32666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.view.GestureDetector; 33666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.view.KeyEvent; 34666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.view.MotionEvent; 35666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.view.View; 36666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.view.ViewConfiguration; 37666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.view.GestureDetector.SimpleOnGestureListener; 38666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.widget.Scroller; 39666ea1b28a76aeba74744148b15099254d918671Owen Lin 40666ea1b28a76aeba74744148b15099254d918671Owen Linimport com.android.camera.gallery.IImage; 41666ea1b28a76aeba74744148b15099254d918671Owen Linimport com.android.camera.gallery.IImageList; 42666ea1b28a76aeba74744148b15099254d918671Owen Lin 43666ea1b28a76aeba74744148b15099254d918671Owen Linimport java.util.HashMap; 44666ea1b28a76aeba74744148b15099254d918671Owen Lin 45666ea1b28a76aeba74744148b15099254d918671Owen Linclass GridViewSpecial extends View { 46666ea1b28a76aeba74744148b15099254d918671Owen Lin @SuppressWarnings("unused") 47666ea1b28a76aeba74744148b15099254d918671Owen Lin private static final String TAG = "GridViewSpecial"; 48666ea1b28a76aeba74744148b15099254d918671Owen Lin private static final float MAX_FLING_VELOCITY = 2500; 49666ea1b28a76aeba74744148b15099254d918671Owen Lin 50666ea1b28a76aeba74744148b15099254d918671Owen Lin public static interface Listener { 51666ea1b28a76aeba74744148b15099254d918671Owen Lin public void onImageClicked(int index); 52666ea1b28a76aeba74744148b15099254d918671Owen Lin public void onImageTapped(int index); 53666ea1b28a76aeba74744148b15099254d918671Owen Lin public void onLayoutComplete(boolean changed); 54666ea1b28a76aeba74744148b15099254d918671Owen Lin 55666ea1b28a76aeba74744148b15099254d918671Owen Lin /** 56666ea1b28a76aeba74744148b15099254d918671Owen Lin * Invoked when the <code>GridViewSpecial</code> scrolls. 57666ea1b28a76aeba74744148b15099254d918671Owen Lin * 58666ea1b28a76aeba74744148b15099254d918671Owen Lin * @param scrollPosition the position of the scroller in the range 59666ea1b28a76aeba74744148b15099254d918671Owen Lin * [0, 1], when 0 means on the top and 1 means on the buttom 60666ea1b28a76aeba74744148b15099254d918671Owen Lin */ 61666ea1b28a76aeba74744148b15099254d918671Owen Lin public void onScroll(float scrollPosition); 62666ea1b28a76aeba74744148b15099254d918671Owen Lin } 63666ea1b28a76aeba74744148b15099254d918671Owen Lin 64666ea1b28a76aeba74744148b15099254d918671Owen Lin public static interface DrawAdapter { 65666ea1b28a76aeba74744148b15099254d918671Owen Lin public void drawImage(Canvas canvas, IImage image, 66666ea1b28a76aeba74744148b15099254d918671Owen Lin Bitmap b, int xPos, int yPos, int w, int h); 67666ea1b28a76aeba74744148b15099254d918671Owen Lin public void drawDecoration(Canvas canvas, IImage image, 68666ea1b28a76aeba74744148b15099254d918671Owen Lin int xPos, int yPos, int w, int h); 69666ea1b28a76aeba74744148b15099254d918671Owen Lin public boolean needsDecoration(); 70666ea1b28a76aeba74744148b15099254d918671Owen Lin } 71666ea1b28a76aeba74744148b15099254d918671Owen Lin 72666ea1b28a76aeba74744148b15099254d918671Owen Lin public static final int INDEX_NONE = -1; 73666ea1b28a76aeba74744148b15099254d918671Owen Lin 74666ea1b28a76aeba74744148b15099254d918671Owen Lin // There are two cell size we will use. It can be set by setSizeChoice(). 75666ea1b28a76aeba74744148b15099254d918671Owen Lin // The mLeftEdgePadding fields is filled in onLayout(). See the comments 76666ea1b28a76aeba74744148b15099254d918671Owen Lin // in onLayout() for details. 77666ea1b28a76aeba74744148b15099254d918671Owen Lin static class LayoutSpec { 78666ea1b28a76aeba74744148b15099254d918671Owen Lin LayoutSpec(int w, int h, int intercellSpacing, int leftEdgePadding, 79666ea1b28a76aeba74744148b15099254d918671Owen Lin DisplayMetrics metrics) { 80666ea1b28a76aeba74744148b15099254d918671Owen Lin mCellWidth = dpToPx(w, metrics); 81666ea1b28a76aeba74744148b15099254d918671Owen Lin mCellHeight = dpToPx(h, metrics); 82666ea1b28a76aeba74744148b15099254d918671Owen Lin mCellSpacing = dpToPx(intercellSpacing, metrics); 83666ea1b28a76aeba74744148b15099254d918671Owen Lin mLeftEdgePadding = dpToPx(leftEdgePadding, metrics); 84666ea1b28a76aeba74744148b15099254d918671Owen Lin } 85666ea1b28a76aeba74744148b15099254d918671Owen Lin int mCellWidth, mCellHeight; 86666ea1b28a76aeba74744148b15099254d918671Owen Lin int mCellSpacing; 87666ea1b28a76aeba74744148b15099254d918671Owen Lin int mLeftEdgePadding; 88666ea1b28a76aeba74744148b15099254d918671Owen Lin } 89666ea1b28a76aeba74744148b15099254d918671Owen Lin 90666ea1b28a76aeba74744148b15099254d918671Owen Lin private LayoutSpec [] mCellSizeChoices; 91666ea1b28a76aeba74744148b15099254d918671Owen Lin 92666ea1b28a76aeba74744148b15099254d918671Owen Lin private void initCellSize() { 93666ea1b28a76aeba74744148b15099254d918671Owen Lin Activity a = (Activity) getContext(); 94666ea1b28a76aeba74744148b15099254d918671Owen Lin DisplayMetrics metrics = new DisplayMetrics(); 95666ea1b28a76aeba74744148b15099254d918671Owen Lin a.getWindowManager().getDefaultDisplay().getMetrics(metrics); 96666ea1b28a76aeba74744148b15099254d918671Owen Lin mCellSizeChoices = new LayoutSpec[] { 97666ea1b28a76aeba74744148b15099254d918671Owen Lin new LayoutSpec(67, 67, 8, 0, metrics), 98666ea1b28a76aeba74744148b15099254d918671Owen Lin new LayoutSpec(92, 92, 8, 0, metrics), 99666ea1b28a76aeba74744148b15099254d918671Owen Lin }; 100666ea1b28a76aeba74744148b15099254d918671Owen Lin } 101666ea1b28a76aeba74744148b15099254d918671Owen Lin 102666ea1b28a76aeba74744148b15099254d918671Owen Lin // Converts dp to pixel. 103666ea1b28a76aeba74744148b15099254d918671Owen Lin private static int dpToPx(int dp, DisplayMetrics metrics) { 104666ea1b28a76aeba74744148b15099254d918671Owen Lin return (int) (metrics.density * dp); 105666ea1b28a76aeba74744148b15099254d918671Owen Lin } 106666ea1b28a76aeba74744148b15099254d918671Owen Lin 107666ea1b28a76aeba74744148b15099254d918671Owen Lin // These are set in init(). 108666ea1b28a76aeba74744148b15099254d918671Owen Lin private final Handler mHandler = new Handler(); 109666ea1b28a76aeba74744148b15099254d918671Owen Lin private GestureDetector mGestureDetector; 110666ea1b28a76aeba74744148b15099254d918671Owen Lin private ImageBlockManager mImageBlockManager; 111666ea1b28a76aeba74744148b15099254d918671Owen Lin 112666ea1b28a76aeba74744148b15099254d918671Owen Lin // These are set in set*() functions. 113666ea1b28a76aeba74744148b15099254d918671Owen Lin private ImageLoader mLoader; 114666ea1b28a76aeba74744148b15099254d918671Owen Lin private Listener mListener = null; 115666ea1b28a76aeba74744148b15099254d918671Owen Lin private DrawAdapter mDrawAdapter = null; 116666ea1b28a76aeba74744148b15099254d918671Owen Lin private IImageList mAllImages = ImageManager.makeEmptyImageList(); 117666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mSizeChoice = 1; // default is big cell size 118666ea1b28a76aeba74744148b15099254d918671Owen Lin 119666ea1b28a76aeba74744148b15099254d918671Owen Lin // These are set in onLayout(). 120666ea1b28a76aeba74744148b15099254d918671Owen Lin private LayoutSpec mSpec; 121666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mColumns; 122666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mMaxScrollY; 123666ea1b28a76aeba74744148b15099254d918671Owen Lin 124666ea1b28a76aeba74744148b15099254d918671Owen Lin // We can handle events only if onLayout() is completed. 125666ea1b28a76aeba74744148b15099254d918671Owen Lin private boolean mLayoutComplete = false; 126666ea1b28a76aeba74744148b15099254d918671Owen Lin 127666ea1b28a76aeba74744148b15099254d918671Owen Lin // Selection state 128666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mCurrentSelection = INDEX_NONE; 129666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mCurrentPressState = 0; 130666ea1b28a76aeba74744148b15099254d918671Owen Lin private static final int TAPPING_FLAG = 1; 131666ea1b28a76aeba74744148b15099254d918671Owen Lin private static final int CLICKING_FLAG = 2; 132666ea1b28a76aeba74744148b15099254d918671Owen Lin 133666ea1b28a76aeba74744148b15099254d918671Owen Lin // These are cached derived information. 134666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mCount; // Cache mImageList.getCount(); 135666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mRows; // Cache (mCount + mColumns - 1) / mColumns 136666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mBlockHeight; // Cache mSpec.mCellSpacing + mSpec.mCellHeight 137666ea1b28a76aeba74744148b15099254d918671Owen Lin 138666ea1b28a76aeba74744148b15099254d918671Owen Lin private boolean mRunning = false; 139666ea1b28a76aeba74744148b15099254d918671Owen Lin private Scroller mScroller = null; 140666ea1b28a76aeba74744148b15099254d918671Owen Lin 141666ea1b28a76aeba74744148b15099254d918671Owen Lin public GridViewSpecial(Context context, AttributeSet attrs) { 142666ea1b28a76aeba74744148b15099254d918671Owen Lin super(context, attrs); 143666ea1b28a76aeba74744148b15099254d918671Owen Lin init(context); 144666ea1b28a76aeba74744148b15099254d918671Owen Lin } 145666ea1b28a76aeba74744148b15099254d918671Owen Lin 146666ea1b28a76aeba74744148b15099254d918671Owen Lin private void init(Context context) { 147666ea1b28a76aeba74744148b15099254d918671Owen Lin setVerticalScrollBarEnabled(true); 148666ea1b28a76aeba74744148b15099254d918671Owen Lin initializeScrollbars(context.obtainStyledAttributes( 149666ea1b28a76aeba74744148b15099254d918671Owen Lin android.R.styleable.View)); 150666ea1b28a76aeba74744148b15099254d918671Owen Lin mGestureDetector = new GestureDetector(context, 151666ea1b28a76aeba74744148b15099254d918671Owen Lin new MyGestureDetector()); 152666ea1b28a76aeba74744148b15099254d918671Owen Lin setFocusableInTouchMode(true); 153666ea1b28a76aeba74744148b15099254d918671Owen Lin initCellSize(); 154666ea1b28a76aeba74744148b15099254d918671Owen Lin } 155666ea1b28a76aeba74744148b15099254d918671Owen Lin 156666ea1b28a76aeba74744148b15099254d918671Owen Lin private final Runnable mRedrawCallback = new Runnable() { 157666ea1b28a76aeba74744148b15099254d918671Owen Lin public void run() { 158666ea1b28a76aeba74744148b15099254d918671Owen Lin invalidate(); 159666ea1b28a76aeba74744148b15099254d918671Owen Lin } 160666ea1b28a76aeba74744148b15099254d918671Owen Lin }; 161666ea1b28a76aeba74744148b15099254d918671Owen Lin 162666ea1b28a76aeba74744148b15099254d918671Owen Lin public void setLoader(ImageLoader loader) { 163666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert(mRunning == false); 164666ea1b28a76aeba74744148b15099254d918671Owen Lin mLoader = loader; 165666ea1b28a76aeba74744148b15099254d918671Owen Lin } 166666ea1b28a76aeba74744148b15099254d918671Owen Lin 167666ea1b28a76aeba74744148b15099254d918671Owen Lin public void setListener(Listener listener) { 168666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert(mRunning == false); 169666ea1b28a76aeba74744148b15099254d918671Owen Lin mListener = listener; 170666ea1b28a76aeba74744148b15099254d918671Owen Lin } 171666ea1b28a76aeba74744148b15099254d918671Owen Lin 172666ea1b28a76aeba74744148b15099254d918671Owen Lin public void setDrawAdapter(DrawAdapter adapter) { 173666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert(mRunning == false); 174666ea1b28a76aeba74744148b15099254d918671Owen Lin mDrawAdapter = adapter; 175666ea1b28a76aeba74744148b15099254d918671Owen Lin } 176666ea1b28a76aeba74744148b15099254d918671Owen Lin 177666ea1b28a76aeba74744148b15099254d918671Owen Lin public void setImageList(IImageList list) { 178666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert(mRunning == false); 179666ea1b28a76aeba74744148b15099254d918671Owen Lin mAllImages = list; 180666ea1b28a76aeba74744148b15099254d918671Owen Lin mCount = mAllImages.getCount(); 181666ea1b28a76aeba74744148b15099254d918671Owen Lin } 182666ea1b28a76aeba74744148b15099254d918671Owen Lin 183666ea1b28a76aeba74744148b15099254d918671Owen Lin public void setSizeChoice(int choice) { 184666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert(mRunning == false); 185666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mSizeChoice == choice) return; 186666ea1b28a76aeba74744148b15099254d918671Owen Lin mSizeChoice = choice; 187666ea1b28a76aeba74744148b15099254d918671Owen Lin } 188666ea1b28a76aeba74744148b15099254d918671Owen Lin 189666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 190666ea1b28a76aeba74744148b15099254d918671Owen Lin public void onLayout(boolean changed, int left, int top, 191666ea1b28a76aeba74744148b15099254d918671Owen Lin int right, int bottom) { 192666ea1b28a76aeba74744148b15099254d918671Owen Lin super.onLayout(changed, left, top, right, bottom); 193666ea1b28a76aeba74744148b15099254d918671Owen Lin 194666ea1b28a76aeba74744148b15099254d918671Owen Lin if (!mRunning) { 195666ea1b28a76aeba74744148b15099254d918671Owen Lin return; 196666ea1b28a76aeba74744148b15099254d918671Owen Lin } 197666ea1b28a76aeba74744148b15099254d918671Owen Lin 198666ea1b28a76aeba74744148b15099254d918671Owen Lin mSpec = mCellSizeChoices[mSizeChoice]; 199666ea1b28a76aeba74744148b15099254d918671Owen Lin 200666ea1b28a76aeba74744148b15099254d918671Owen Lin int width = right - left; 201666ea1b28a76aeba74744148b15099254d918671Owen Lin 202666ea1b28a76aeba74744148b15099254d918671Owen Lin // The width is divided into following parts: 203666ea1b28a76aeba74744148b15099254d918671Owen Lin // 204666ea1b28a76aeba74744148b15099254d918671Owen Lin // LeftEdgePadding CellWidth (CellSpacing CellWidth)* RightEdgePadding 205666ea1b28a76aeba74744148b15099254d918671Owen Lin // 206666ea1b28a76aeba74744148b15099254d918671Owen Lin // We determine number of cells (columns) first, then the left and right 207666ea1b28a76aeba74744148b15099254d918671Owen Lin // padding are derived. We make left and right paddings the same size. 208666ea1b28a76aeba74744148b15099254d918671Owen Lin // 209666ea1b28a76aeba74744148b15099254d918671Owen Lin // The height is divided into following parts: 210666ea1b28a76aeba74744148b15099254d918671Owen Lin // 211666ea1b28a76aeba74744148b15099254d918671Owen Lin // CellSpacing (CellHeight CellSpacing)+ 212666ea1b28a76aeba74744148b15099254d918671Owen Lin 213666ea1b28a76aeba74744148b15099254d918671Owen Lin mColumns = 1 + (width - mSpec.mCellWidth) 214666ea1b28a76aeba74744148b15099254d918671Owen Lin / (mSpec.mCellWidth + mSpec.mCellSpacing); 215666ea1b28a76aeba74744148b15099254d918671Owen Lin 216666ea1b28a76aeba74744148b15099254d918671Owen Lin mSpec.mLeftEdgePadding = (width 217666ea1b28a76aeba74744148b15099254d918671Owen Lin - ((mColumns - 1) * mSpec.mCellSpacing) 218666ea1b28a76aeba74744148b15099254d918671Owen Lin - (mColumns * mSpec.mCellWidth)) / 2; 219666ea1b28a76aeba74744148b15099254d918671Owen Lin 220666ea1b28a76aeba74744148b15099254d918671Owen Lin mRows = (mCount + mColumns - 1) / mColumns; 221666ea1b28a76aeba74744148b15099254d918671Owen Lin mBlockHeight = mSpec.mCellSpacing + mSpec.mCellHeight; 222666ea1b28a76aeba74744148b15099254d918671Owen Lin mMaxScrollY = mSpec.mCellSpacing + (mRows * mBlockHeight) 223666ea1b28a76aeba74744148b15099254d918671Owen Lin - (bottom - top); 224666ea1b28a76aeba74744148b15099254d918671Owen Lin 225666ea1b28a76aeba74744148b15099254d918671Owen Lin // Put mScrollY in the valid range. This matters if mMaxScrollY is 226666ea1b28a76aeba74744148b15099254d918671Owen Lin // changed. For example, orientation changed from portrait to landscape. 227666ea1b28a76aeba74744148b15099254d918671Owen Lin mScrollY = Math.max(0, Math.min(mMaxScrollY, mScrollY)); 228666ea1b28a76aeba74744148b15099254d918671Owen Lin 229666ea1b28a76aeba74744148b15099254d918671Owen Lin generateOutlineBitmap(); 230666ea1b28a76aeba74744148b15099254d918671Owen Lin 231666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mImageBlockManager != null) { 232666ea1b28a76aeba74744148b15099254d918671Owen Lin mImageBlockManager.recycle(); 233666ea1b28a76aeba74744148b15099254d918671Owen Lin } 234666ea1b28a76aeba74744148b15099254d918671Owen Lin 235666ea1b28a76aeba74744148b15099254d918671Owen Lin mImageBlockManager = new ImageBlockManager(mHandler, mRedrawCallback, 236666ea1b28a76aeba74744148b15099254d918671Owen Lin mAllImages, mLoader, mDrawAdapter, mSpec, mColumns, width, 237666ea1b28a76aeba74744148b15099254d918671Owen Lin mOutline[OUTLINE_EMPTY]); 238666ea1b28a76aeba74744148b15099254d918671Owen Lin 239666ea1b28a76aeba74744148b15099254d918671Owen Lin mListener.onLayoutComplete(changed); 240666ea1b28a76aeba74744148b15099254d918671Owen Lin 241666ea1b28a76aeba74744148b15099254d918671Owen Lin moveDataWindow(); 242666ea1b28a76aeba74744148b15099254d918671Owen Lin 243666ea1b28a76aeba74744148b15099254d918671Owen Lin mLayoutComplete = true; 244666ea1b28a76aeba74744148b15099254d918671Owen Lin } 245666ea1b28a76aeba74744148b15099254d918671Owen Lin 246666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 247666ea1b28a76aeba74744148b15099254d918671Owen Lin protected int computeVerticalScrollRange() { 248666ea1b28a76aeba74744148b15099254d918671Owen Lin return mMaxScrollY + getHeight(); 249666ea1b28a76aeba74744148b15099254d918671Owen Lin } 250666ea1b28a76aeba74744148b15099254d918671Owen Lin 251666ea1b28a76aeba74744148b15099254d918671Owen Lin // We cache the three outlines from NinePatch to Bitmap to speed up 252666ea1b28a76aeba74744148b15099254d918671Owen Lin // drawing. The cache must be updated if the cell size is changed. 253666ea1b28a76aeba74744148b15099254d918671Owen Lin public static final int OUTLINE_EMPTY = 0; 254666ea1b28a76aeba74744148b15099254d918671Owen Lin public static final int OUTLINE_PRESSED = 1; 255666ea1b28a76aeba74744148b15099254d918671Owen Lin public static final int OUTLINE_SELECTED = 2; 256666ea1b28a76aeba74744148b15099254d918671Owen Lin 257666ea1b28a76aeba74744148b15099254d918671Owen Lin public Bitmap mOutline[] = new Bitmap[3]; 258666ea1b28a76aeba74744148b15099254d918671Owen Lin 259666ea1b28a76aeba74744148b15099254d918671Owen Lin private void generateOutlineBitmap() { 260666ea1b28a76aeba74744148b15099254d918671Owen Lin int w = mSpec.mCellWidth; 261666ea1b28a76aeba74744148b15099254d918671Owen Lin int h = mSpec.mCellHeight; 262666ea1b28a76aeba74744148b15099254d918671Owen Lin 263666ea1b28a76aeba74744148b15099254d918671Owen Lin for (int i = 0; i < mOutline.length; i++) { 264666ea1b28a76aeba74744148b15099254d918671Owen Lin mOutline[i] = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); 265666ea1b28a76aeba74744148b15099254d918671Owen Lin } 266666ea1b28a76aeba74744148b15099254d918671Owen Lin 267666ea1b28a76aeba74744148b15099254d918671Owen Lin Drawable cellOutline; 268666ea1b28a76aeba74744148b15099254d918671Owen Lin cellOutline = GridViewSpecial.this.getResources() 269666ea1b28a76aeba74744148b15099254d918671Owen Lin .getDrawable(android.R.drawable.gallery_thumb); 270666ea1b28a76aeba74744148b15099254d918671Owen Lin cellOutline.setBounds(0, 0, w, h); 271666ea1b28a76aeba74744148b15099254d918671Owen Lin Canvas canvas = new Canvas(); 272666ea1b28a76aeba74744148b15099254d918671Owen Lin 273666ea1b28a76aeba74744148b15099254d918671Owen Lin canvas.setBitmap(mOutline[OUTLINE_EMPTY]); 274666ea1b28a76aeba74744148b15099254d918671Owen Lin cellOutline.setState(EMPTY_STATE_SET); 275666ea1b28a76aeba74744148b15099254d918671Owen Lin cellOutline.draw(canvas); 276666ea1b28a76aeba74744148b15099254d918671Owen Lin 277666ea1b28a76aeba74744148b15099254d918671Owen Lin canvas.setBitmap(mOutline[OUTLINE_PRESSED]); 278666ea1b28a76aeba74744148b15099254d918671Owen Lin cellOutline.setState( 279666ea1b28a76aeba74744148b15099254d918671Owen Lin PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET); 280666ea1b28a76aeba74744148b15099254d918671Owen Lin cellOutline.draw(canvas); 281666ea1b28a76aeba74744148b15099254d918671Owen Lin 282666ea1b28a76aeba74744148b15099254d918671Owen Lin canvas.setBitmap(mOutline[OUTLINE_SELECTED]); 283666ea1b28a76aeba74744148b15099254d918671Owen Lin cellOutline.setState(ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET); 284666ea1b28a76aeba74744148b15099254d918671Owen Lin cellOutline.draw(canvas); 285666ea1b28a76aeba74744148b15099254d918671Owen Lin } 286666ea1b28a76aeba74744148b15099254d918671Owen Lin 287666ea1b28a76aeba74744148b15099254d918671Owen Lin private void moveDataWindow() { 288666ea1b28a76aeba74744148b15099254d918671Owen Lin // Calculate visible region according to scroll position. 289666ea1b28a76aeba74744148b15099254d918671Owen Lin int startRow = (mScrollY - mSpec.mCellSpacing) / mBlockHeight; 290666ea1b28a76aeba74744148b15099254d918671Owen Lin int endRow = (mScrollY + getHeight() - mSpec.mCellSpacing - 1) 291666ea1b28a76aeba74744148b15099254d918671Owen Lin / mBlockHeight + 1; 292666ea1b28a76aeba74744148b15099254d918671Owen Lin 293666ea1b28a76aeba74744148b15099254d918671Owen Lin // Limit startRow and endRow to the valid range. 294666ea1b28a76aeba74744148b15099254d918671Owen Lin // Make sure we handle the mRows == 0 case right. 295666ea1b28a76aeba74744148b15099254d918671Owen Lin startRow = Math.max(Math.min(startRow, mRows - 1), 0); 296666ea1b28a76aeba74744148b15099254d918671Owen Lin endRow = Math.max(Math.min(endRow, mRows), 0); 297666ea1b28a76aeba74744148b15099254d918671Owen Lin mImageBlockManager.setVisibleRows(startRow, endRow); 298666ea1b28a76aeba74744148b15099254d918671Owen Lin } 299666ea1b28a76aeba74744148b15099254d918671Owen Lin 300666ea1b28a76aeba74744148b15099254d918671Owen Lin // In MyGestureDetector we have to check canHandleEvent() because 301666ea1b28a76aeba74744148b15099254d918671Owen Lin // GestureDetector could queue events and fire them later. At that time 302666ea1b28a76aeba74744148b15099254d918671Owen Lin // stop() may have already been called and we can't handle the events. 303666ea1b28a76aeba74744148b15099254d918671Owen Lin private class MyGestureDetector extends SimpleOnGestureListener { 304666ea1b28a76aeba74744148b15099254d918671Owen Lin private AudioManager mAudioManager; 305666ea1b28a76aeba74744148b15099254d918671Owen Lin 306666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 307666ea1b28a76aeba74744148b15099254d918671Owen Lin public boolean onDown(MotionEvent e) { 308666ea1b28a76aeba74744148b15099254d918671Owen Lin if (!canHandleEvent()) return false; 309666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mScroller != null && !mScroller.isFinished()) { 310666ea1b28a76aeba74744148b15099254d918671Owen Lin mScroller.forceFinished(true); 311666ea1b28a76aeba74744148b15099254d918671Owen Lin return false; 312666ea1b28a76aeba74744148b15099254d918671Owen Lin } 313666ea1b28a76aeba74744148b15099254d918671Owen Lin int index = computeSelectedIndex(e.getX(), e.getY()); 314666ea1b28a76aeba74744148b15099254d918671Owen Lin if (index >= 0 && index < mCount) { 315666ea1b28a76aeba74744148b15099254d918671Owen Lin setSelectedIndex(index); 316666ea1b28a76aeba74744148b15099254d918671Owen Lin } else { 317666ea1b28a76aeba74744148b15099254d918671Owen Lin setSelectedIndex(INDEX_NONE); 318666ea1b28a76aeba74744148b15099254d918671Owen Lin } 319666ea1b28a76aeba74744148b15099254d918671Owen Lin return true; 320666ea1b28a76aeba74744148b15099254d918671Owen Lin } 321666ea1b28a76aeba74744148b15099254d918671Owen Lin 322666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 323666ea1b28a76aeba74744148b15099254d918671Owen Lin public boolean onFling(MotionEvent e1, MotionEvent e2, 324666ea1b28a76aeba74744148b15099254d918671Owen Lin float velocityX, float velocityY) { 325666ea1b28a76aeba74744148b15099254d918671Owen Lin if (!canHandleEvent()) return false; 326666ea1b28a76aeba74744148b15099254d918671Owen Lin if (velocityY > MAX_FLING_VELOCITY) { 327666ea1b28a76aeba74744148b15099254d918671Owen Lin velocityY = MAX_FLING_VELOCITY; 328666ea1b28a76aeba74744148b15099254d918671Owen Lin } else if (velocityY < -MAX_FLING_VELOCITY) { 329666ea1b28a76aeba74744148b15099254d918671Owen Lin velocityY = -MAX_FLING_VELOCITY; 330666ea1b28a76aeba74744148b15099254d918671Owen Lin } 331666ea1b28a76aeba74744148b15099254d918671Owen Lin 332666ea1b28a76aeba74744148b15099254d918671Owen Lin setSelectedIndex(INDEX_NONE); 333666ea1b28a76aeba74744148b15099254d918671Owen Lin mScroller = new Scroller(getContext()); 334666ea1b28a76aeba74744148b15099254d918671Owen Lin mScroller.fling(0, mScrollY, 0, -(int) velocityY, 0, 0, 0, 335666ea1b28a76aeba74744148b15099254d918671Owen Lin mMaxScrollY); 336666ea1b28a76aeba74744148b15099254d918671Owen Lin computeScroll(); 337666ea1b28a76aeba74744148b15099254d918671Owen Lin 338666ea1b28a76aeba74744148b15099254d918671Owen Lin return true; 339666ea1b28a76aeba74744148b15099254d918671Owen Lin } 340666ea1b28a76aeba74744148b15099254d918671Owen Lin 341666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 342666ea1b28a76aeba74744148b15099254d918671Owen Lin public void onLongPress(MotionEvent e) { 343666ea1b28a76aeba74744148b15099254d918671Owen Lin if (!canHandleEvent()) return; 344666ea1b28a76aeba74744148b15099254d918671Owen Lin performLongClick(); 345666ea1b28a76aeba74744148b15099254d918671Owen Lin } 346666ea1b28a76aeba74744148b15099254d918671Owen Lin 347666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 348666ea1b28a76aeba74744148b15099254d918671Owen Lin public boolean onScroll(MotionEvent e1, MotionEvent e2, 349666ea1b28a76aeba74744148b15099254d918671Owen Lin float distanceX, float distanceY) { 350666ea1b28a76aeba74744148b15099254d918671Owen Lin if (!canHandleEvent()) return false; 351666ea1b28a76aeba74744148b15099254d918671Owen Lin setSelectedIndex(INDEX_NONE); 352666ea1b28a76aeba74744148b15099254d918671Owen Lin scrollBy(0, (int) distanceY); 353666ea1b28a76aeba74744148b15099254d918671Owen Lin invalidate(); 354666ea1b28a76aeba74744148b15099254d918671Owen Lin return true; 355666ea1b28a76aeba74744148b15099254d918671Owen Lin } 356666ea1b28a76aeba74744148b15099254d918671Owen Lin 357666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 358666ea1b28a76aeba74744148b15099254d918671Owen Lin public boolean onSingleTapConfirmed(MotionEvent e) { 359666ea1b28a76aeba74744148b15099254d918671Owen Lin if (!canHandleEvent()) return false; 360666ea1b28a76aeba74744148b15099254d918671Owen Lin int index = computeSelectedIndex(e.getX(), e.getY()); 361666ea1b28a76aeba74744148b15099254d918671Owen Lin if (index >= 0 && index < mCount) { 362666ea1b28a76aeba74744148b15099254d918671Owen Lin // Play click sound. 363666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mAudioManager == null) { 364666ea1b28a76aeba74744148b15099254d918671Owen Lin mAudioManager = (AudioManager) getContext() 365666ea1b28a76aeba74744148b15099254d918671Owen Lin .getSystemService(Context.AUDIO_SERVICE); 366666ea1b28a76aeba74744148b15099254d918671Owen Lin } 367666ea1b28a76aeba74744148b15099254d918671Owen Lin mAudioManager.playSoundEffect(AudioManager.FX_KEY_CLICK); 368666ea1b28a76aeba74744148b15099254d918671Owen Lin 369666ea1b28a76aeba74744148b15099254d918671Owen Lin mListener.onImageTapped(index); 370666ea1b28a76aeba74744148b15099254d918671Owen Lin return true; 371666ea1b28a76aeba74744148b15099254d918671Owen Lin } 372666ea1b28a76aeba74744148b15099254d918671Owen Lin return false; 373666ea1b28a76aeba74744148b15099254d918671Owen Lin } 374666ea1b28a76aeba74744148b15099254d918671Owen Lin } 375666ea1b28a76aeba74744148b15099254d918671Owen Lin 376666ea1b28a76aeba74744148b15099254d918671Owen Lin public int getCurrentSelection() { 377666ea1b28a76aeba74744148b15099254d918671Owen Lin return mCurrentSelection; 378666ea1b28a76aeba74744148b15099254d918671Owen Lin } 379666ea1b28a76aeba74744148b15099254d918671Owen Lin 380666ea1b28a76aeba74744148b15099254d918671Owen Lin public void invalidateImage(int index) { 381666ea1b28a76aeba74744148b15099254d918671Owen Lin if (index != INDEX_NONE) { 382666ea1b28a76aeba74744148b15099254d918671Owen Lin mImageBlockManager.invalidateImage(index); 383666ea1b28a76aeba74744148b15099254d918671Owen Lin } 384666ea1b28a76aeba74744148b15099254d918671Owen Lin } 385666ea1b28a76aeba74744148b15099254d918671Owen Lin 386666ea1b28a76aeba74744148b15099254d918671Owen Lin /** 387666ea1b28a76aeba74744148b15099254d918671Owen Lin * 388666ea1b28a76aeba74744148b15099254d918671Owen Lin * @param index <code>INDEX_NONE</code> (-1) means remove selection. 389666ea1b28a76aeba74744148b15099254d918671Owen Lin */ 390666ea1b28a76aeba74744148b15099254d918671Owen Lin public void setSelectedIndex(int index) { 391666ea1b28a76aeba74744148b15099254d918671Owen Lin // A selection box will be shown for the image that being selected, 392666ea1b28a76aeba74744148b15099254d918671Owen Lin // (by finger or by the dpad center key). The selection box can be drawn 393666ea1b28a76aeba74744148b15099254d918671Owen Lin // in two colors. One color (yellow) is used when the the image is 394666ea1b28a76aeba74744148b15099254d918671Owen Lin // still being tapped or clicked (the finger is still on the touch 395666ea1b28a76aeba74744148b15099254d918671Owen Lin // screen or the dpad center key is not released). Another color 396666ea1b28a76aeba74744148b15099254d918671Owen Lin // (orange) is used after the finger leaves touch screen or the dpad 397666ea1b28a76aeba74744148b15099254d918671Owen Lin // center key is released. 398666ea1b28a76aeba74744148b15099254d918671Owen Lin 399666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mCurrentSelection == index) { 400666ea1b28a76aeba74744148b15099254d918671Owen Lin return; 401666ea1b28a76aeba74744148b15099254d918671Owen Lin } 402666ea1b28a76aeba74744148b15099254d918671Owen Lin // This happens when the last picture is deleted. 403666ea1b28a76aeba74744148b15099254d918671Owen Lin mCurrentSelection = Math.min(index, mCount - 1); 404666ea1b28a76aeba74744148b15099254d918671Owen Lin 405666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mCurrentSelection != INDEX_NONE) { 406666ea1b28a76aeba74744148b15099254d918671Owen Lin ensureVisible(mCurrentSelection); 407666ea1b28a76aeba74744148b15099254d918671Owen Lin } 408666ea1b28a76aeba74744148b15099254d918671Owen Lin invalidate(); 409666ea1b28a76aeba74744148b15099254d918671Owen Lin } 410666ea1b28a76aeba74744148b15099254d918671Owen Lin 411666ea1b28a76aeba74744148b15099254d918671Owen Lin public void scrollToImage(int index) { 412666ea1b28a76aeba74744148b15099254d918671Owen Lin Rect r = getRectForPosition(index); 413666ea1b28a76aeba74744148b15099254d918671Owen Lin scrollTo(0, r.top); 414666ea1b28a76aeba74744148b15099254d918671Owen Lin } 415666ea1b28a76aeba74744148b15099254d918671Owen Lin 416666ea1b28a76aeba74744148b15099254d918671Owen Lin public void scrollToVisible(int index) { 417666ea1b28a76aeba74744148b15099254d918671Owen Lin Rect r = getRectForPosition(index); 418666ea1b28a76aeba74744148b15099254d918671Owen Lin int top = getScrollY(); 419666ea1b28a76aeba74744148b15099254d918671Owen Lin int bottom = getScrollY() + getHeight(); 420666ea1b28a76aeba74744148b15099254d918671Owen Lin if (r.bottom > bottom) { 421666ea1b28a76aeba74744148b15099254d918671Owen Lin scrollTo(0, r.bottom - getHeight()); 422666ea1b28a76aeba74744148b15099254d918671Owen Lin } else if (r.top < top) { 423666ea1b28a76aeba74744148b15099254d918671Owen Lin scrollTo(0, r.top); 424666ea1b28a76aeba74744148b15099254d918671Owen Lin } 425666ea1b28a76aeba74744148b15099254d918671Owen Lin } 426666ea1b28a76aeba74744148b15099254d918671Owen Lin 427666ea1b28a76aeba74744148b15099254d918671Owen Lin private void ensureVisible(int pos) { 428666ea1b28a76aeba74744148b15099254d918671Owen Lin Rect r = getRectForPosition(pos); 429666ea1b28a76aeba74744148b15099254d918671Owen Lin int top = getScrollY(); 430666ea1b28a76aeba74744148b15099254d918671Owen Lin int bot = top + getHeight(); 431666ea1b28a76aeba74744148b15099254d918671Owen Lin 432666ea1b28a76aeba74744148b15099254d918671Owen Lin if (r.bottom > bot) { 433666ea1b28a76aeba74744148b15099254d918671Owen Lin mScroller = new Scroller(getContext()); 434666ea1b28a76aeba74744148b15099254d918671Owen Lin mScroller.startScroll(mScrollX, mScrollY, 0, 435666ea1b28a76aeba74744148b15099254d918671Owen Lin r.bottom - getHeight() - mScrollY, 200); 436666ea1b28a76aeba74744148b15099254d918671Owen Lin computeScroll(); 437666ea1b28a76aeba74744148b15099254d918671Owen Lin } else if (r.top < top) { 438666ea1b28a76aeba74744148b15099254d918671Owen Lin mScroller = new Scroller(getContext()); 439666ea1b28a76aeba74744148b15099254d918671Owen Lin mScroller.startScroll(mScrollX, mScrollY, 0, r.top - mScrollY, 200); 440666ea1b28a76aeba74744148b15099254d918671Owen Lin computeScroll(); 441666ea1b28a76aeba74744148b15099254d918671Owen Lin } 442666ea1b28a76aeba74744148b15099254d918671Owen Lin } 443666ea1b28a76aeba74744148b15099254d918671Owen Lin 444666ea1b28a76aeba74744148b15099254d918671Owen Lin public void start() { 445666ea1b28a76aeba74744148b15099254d918671Owen Lin // These must be set before start(). 446666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert(mLoader != null); 447666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert(mListener != null); 448666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert(mDrawAdapter != null); 449666ea1b28a76aeba74744148b15099254d918671Owen Lin mRunning = true; 450666ea1b28a76aeba74744148b15099254d918671Owen Lin requestLayout(); 451666ea1b28a76aeba74744148b15099254d918671Owen Lin } 452666ea1b28a76aeba74744148b15099254d918671Owen Lin 453666ea1b28a76aeba74744148b15099254d918671Owen Lin // If the the underlying data is changed, for example, 454666ea1b28a76aeba74744148b15099254d918671Owen Lin // an image is deleted, or the size choice is changed, 455666ea1b28a76aeba74744148b15099254d918671Owen Lin // The following sequence is needed: 456666ea1b28a76aeba74744148b15099254d918671Owen Lin // 457666ea1b28a76aeba74744148b15099254d918671Owen Lin // mGvs.stop(); 458666ea1b28a76aeba74744148b15099254d918671Owen Lin // mGvs.set...(...); 459666ea1b28a76aeba74744148b15099254d918671Owen Lin // mGvs.set...(...); 460666ea1b28a76aeba74744148b15099254d918671Owen Lin // mGvs.start(); 461666ea1b28a76aeba74744148b15099254d918671Owen Lin public void stop() { 462666ea1b28a76aeba74744148b15099254d918671Owen Lin // Remove the long press callback from the queue if we are going to 463666ea1b28a76aeba74744148b15099254d918671Owen Lin // stop. 464666ea1b28a76aeba74744148b15099254d918671Owen Lin mHandler.removeCallbacks(mLongPressCallback); 465666ea1b28a76aeba74744148b15099254d918671Owen Lin mScroller = null; 466666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mImageBlockManager != null) { 467666ea1b28a76aeba74744148b15099254d918671Owen Lin mImageBlockManager.recycle(); 468666ea1b28a76aeba74744148b15099254d918671Owen Lin mImageBlockManager = null; 469666ea1b28a76aeba74744148b15099254d918671Owen Lin } 470666ea1b28a76aeba74744148b15099254d918671Owen Lin mRunning = false; 471666ea1b28a76aeba74744148b15099254d918671Owen Lin mCurrentSelection = INDEX_NONE; 472666ea1b28a76aeba74744148b15099254d918671Owen Lin } 473666ea1b28a76aeba74744148b15099254d918671Owen Lin 474666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 475666ea1b28a76aeba74744148b15099254d918671Owen Lin public void onDraw(Canvas canvas) { 476666ea1b28a76aeba74744148b15099254d918671Owen Lin super.onDraw(canvas); 477666ea1b28a76aeba74744148b15099254d918671Owen Lin if (!canHandleEvent()) return; 478666ea1b28a76aeba74744148b15099254d918671Owen Lin mImageBlockManager.doDraw(canvas, getWidth(), getHeight(), mScrollY); 479666ea1b28a76aeba74744148b15099254d918671Owen Lin paintDecoration(canvas); 480666ea1b28a76aeba74744148b15099254d918671Owen Lin paintSelection(canvas); 481666ea1b28a76aeba74744148b15099254d918671Owen Lin moveDataWindow(); 482666ea1b28a76aeba74744148b15099254d918671Owen Lin } 483666ea1b28a76aeba74744148b15099254d918671Owen Lin 484666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 485666ea1b28a76aeba74744148b15099254d918671Owen Lin public void computeScroll() { 486666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mScroller != null) { 487666ea1b28a76aeba74744148b15099254d918671Owen Lin boolean more = mScroller.computeScrollOffset(); 488666ea1b28a76aeba74744148b15099254d918671Owen Lin scrollTo(0, mScroller.getCurrY()); 489666ea1b28a76aeba74744148b15099254d918671Owen Lin if (more) { 490666ea1b28a76aeba74744148b15099254d918671Owen Lin invalidate(); // So we draw again 491666ea1b28a76aeba74744148b15099254d918671Owen Lin } else { 492666ea1b28a76aeba74744148b15099254d918671Owen Lin mScroller = null; 493666ea1b28a76aeba74744148b15099254d918671Owen Lin } 494666ea1b28a76aeba74744148b15099254d918671Owen Lin } else { 495666ea1b28a76aeba74744148b15099254d918671Owen Lin super.computeScroll(); 496666ea1b28a76aeba74744148b15099254d918671Owen Lin } 497666ea1b28a76aeba74744148b15099254d918671Owen Lin } 498666ea1b28a76aeba74744148b15099254d918671Owen Lin 499666ea1b28a76aeba74744148b15099254d918671Owen Lin // Return the rectange for the thumbnail in the given position. 500666ea1b28a76aeba74744148b15099254d918671Owen Lin Rect getRectForPosition(int pos) { 501666ea1b28a76aeba74744148b15099254d918671Owen Lin int row = pos / mColumns; 502666ea1b28a76aeba74744148b15099254d918671Owen Lin int col = pos - (row * mColumns); 503666ea1b28a76aeba74744148b15099254d918671Owen Lin 504666ea1b28a76aeba74744148b15099254d918671Owen Lin int left = mSpec.mLeftEdgePadding 505666ea1b28a76aeba74744148b15099254d918671Owen Lin + (col * (mSpec.mCellWidth + mSpec.mCellSpacing)); 506666ea1b28a76aeba74744148b15099254d918671Owen Lin int top = row * mBlockHeight; 507666ea1b28a76aeba74744148b15099254d918671Owen Lin 508666ea1b28a76aeba74744148b15099254d918671Owen Lin return new Rect(left, top, 509666ea1b28a76aeba74744148b15099254d918671Owen Lin left + mSpec.mCellWidth + mSpec.mCellSpacing, 510666ea1b28a76aeba74744148b15099254d918671Owen Lin top + mSpec.mCellHeight + mSpec.mCellSpacing); 511666ea1b28a76aeba74744148b15099254d918671Owen Lin } 512666ea1b28a76aeba74744148b15099254d918671Owen Lin 513666ea1b28a76aeba74744148b15099254d918671Owen Lin // Inverse of getRectForPosition: from screen coordinate to image position. 514666ea1b28a76aeba74744148b15099254d918671Owen Lin int computeSelectedIndex(float xFloat, float yFloat) { 515666ea1b28a76aeba74744148b15099254d918671Owen Lin int x = (int) xFloat; 516666ea1b28a76aeba74744148b15099254d918671Owen Lin int y = (int) yFloat; 517666ea1b28a76aeba74744148b15099254d918671Owen Lin 518666ea1b28a76aeba74744148b15099254d918671Owen Lin int spacing = mSpec.mCellSpacing; 519666ea1b28a76aeba74744148b15099254d918671Owen Lin int leftSpacing = mSpec.mLeftEdgePadding; 520666ea1b28a76aeba74744148b15099254d918671Owen Lin 521666ea1b28a76aeba74744148b15099254d918671Owen Lin int row = (mScrollY + y - spacing) / (mSpec.mCellHeight + spacing); 522666ea1b28a76aeba74744148b15099254d918671Owen Lin int col = Math.min(mColumns - 1, 523666ea1b28a76aeba74744148b15099254d918671Owen Lin (x - leftSpacing) / (mSpec.mCellWidth + spacing)); 524666ea1b28a76aeba74744148b15099254d918671Owen Lin return (row * mColumns) + col; 525666ea1b28a76aeba74744148b15099254d918671Owen Lin } 526666ea1b28a76aeba74744148b15099254d918671Owen Lin 527666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 528666ea1b28a76aeba74744148b15099254d918671Owen Lin public boolean onTouchEvent(MotionEvent ev) { 529666ea1b28a76aeba74744148b15099254d918671Owen Lin if (!canHandleEvent()) { 530666ea1b28a76aeba74744148b15099254d918671Owen Lin return false; 531666ea1b28a76aeba74744148b15099254d918671Owen Lin } 532666ea1b28a76aeba74744148b15099254d918671Owen Lin switch (ev.getAction()) { 533666ea1b28a76aeba74744148b15099254d918671Owen Lin case MotionEvent.ACTION_DOWN: 534666ea1b28a76aeba74744148b15099254d918671Owen Lin mCurrentPressState |= TAPPING_FLAG; 535666ea1b28a76aeba74744148b15099254d918671Owen Lin invalidate(); 536666ea1b28a76aeba74744148b15099254d918671Owen Lin break; 537666ea1b28a76aeba74744148b15099254d918671Owen Lin case MotionEvent.ACTION_UP: 538666ea1b28a76aeba74744148b15099254d918671Owen Lin mCurrentPressState &= ~TAPPING_FLAG; 539666ea1b28a76aeba74744148b15099254d918671Owen Lin invalidate(); 540666ea1b28a76aeba74744148b15099254d918671Owen Lin break; 541666ea1b28a76aeba74744148b15099254d918671Owen Lin } 542666ea1b28a76aeba74744148b15099254d918671Owen Lin mGestureDetector.onTouchEvent(ev); 543666ea1b28a76aeba74744148b15099254d918671Owen Lin // Consume all events 544666ea1b28a76aeba74744148b15099254d918671Owen Lin return true; 545666ea1b28a76aeba74744148b15099254d918671Owen Lin } 546666ea1b28a76aeba74744148b15099254d918671Owen Lin 547666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 548666ea1b28a76aeba74744148b15099254d918671Owen Lin public void scrollBy(int x, int y) { 549666ea1b28a76aeba74744148b15099254d918671Owen Lin scrollTo(mScrollX + x, mScrollY + y); 550666ea1b28a76aeba74744148b15099254d918671Owen Lin } 551666ea1b28a76aeba74744148b15099254d918671Owen Lin 552666ea1b28a76aeba74744148b15099254d918671Owen Lin public void scrollTo(float scrollPosition) { 553666ea1b28a76aeba74744148b15099254d918671Owen Lin scrollTo(0, Math.round(scrollPosition * mMaxScrollY)); 554666ea1b28a76aeba74744148b15099254d918671Owen Lin } 555666ea1b28a76aeba74744148b15099254d918671Owen Lin 556666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 557666ea1b28a76aeba74744148b15099254d918671Owen Lin public void scrollTo(int x, int y) { 558666ea1b28a76aeba74744148b15099254d918671Owen Lin y = Math.max(0, Math.min(mMaxScrollY, y)); 559666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mSpec != null) { 560666ea1b28a76aeba74744148b15099254d918671Owen Lin mListener.onScroll((float) mScrollY / mMaxScrollY); 561666ea1b28a76aeba74744148b15099254d918671Owen Lin } 562666ea1b28a76aeba74744148b15099254d918671Owen Lin super.scrollTo(x, y); 563666ea1b28a76aeba74744148b15099254d918671Owen Lin } 564666ea1b28a76aeba74744148b15099254d918671Owen Lin 565666ea1b28a76aeba74744148b15099254d918671Owen Lin private boolean canHandleEvent() { 566666ea1b28a76aeba74744148b15099254d918671Owen Lin return mRunning && mLayoutComplete; 567666ea1b28a76aeba74744148b15099254d918671Owen Lin } 568666ea1b28a76aeba74744148b15099254d918671Owen Lin 569666ea1b28a76aeba74744148b15099254d918671Owen Lin private final Runnable mLongPressCallback = new Runnable() { 570666ea1b28a76aeba74744148b15099254d918671Owen Lin public void run() { 571666ea1b28a76aeba74744148b15099254d918671Owen Lin mCurrentPressState &= ~CLICKING_FLAG; 572666ea1b28a76aeba74744148b15099254d918671Owen Lin showContextMenu(); 573666ea1b28a76aeba74744148b15099254d918671Owen Lin } 574666ea1b28a76aeba74744148b15099254d918671Owen Lin }; 575666ea1b28a76aeba74744148b15099254d918671Owen Lin 576666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 577666ea1b28a76aeba74744148b15099254d918671Owen Lin public boolean onKeyDown(int keyCode, KeyEvent event) { 578666ea1b28a76aeba74744148b15099254d918671Owen Lin if (!canHandleEvent()) return false; 579666ea1b28a76aeba74744148b15099254d918671Owen Lin int sel = mCurrentSelection; 580666ea1b28a76aeba74744148b15099254d918671Owen Lin if (sel != INDEX_NONE) { 581666ea1b28a76aeba74744148b15099254d918671Owen Lin switch (keyCode) { 582666ea1b28a76aeba74744148b15099254d918671Owen Lin case KeyEvent.KEYCODE_DPAD_RIGHT: 583666ea1b28a76aeba74744148b15099254d918671Owen Lin if (sel != mCount - 1 && (sel % mColumns < mColumns - 1)) { 584666ea1b28a76aeba74744148b15099254d918671Owen Lin sel += 1; 585666ea1b28a76aeba74744148b15099254d918671Owen Lin } 586666ea1b28a76aeba74744148b15099254d918671Owen Lin break; 587666ea1b28a76aeba74744148b15099254d918671Owen Lin case KeyEvent.KEYCODE_DPAD_LEFT: 588666ea1b28a76aeba74744148b15099254d918671Owen Lin if (sel > 0 && (sel % mColumns != 0)) { 589666ea1b28a76aeba74744148b15099254d918671Owen Lin sel -= 1; 590666ea1b28a76aeba74744148b15099254d918671Owen Lin } 591666ea1b28a76aeba74744148b15099254d918671Owen Lin break; 592666ea1b28a76aeba74744148b15099254d918671Owen Lin case KeyEvent.KEYCODE_DPAD_UP: 593666ea1b28a76aeba74744148b15099254d918671Owen Lin if (sel >= mColumns) { 594666ea1b28a76aeba74744148b15099254d918671Owen Lin sel -= mColumns; 595666ea1b28a76aeba74744148b15099254d918671Owen Lin } 596666ea1b28a76aeba74744148b15099254d918671Owen Lin break; 597666ea1b28a76aeba74744148b15099254d918671Owen Lin case KeyEvent.KEYCODE_DPAD_DOWN: 598666ea1b28a76aeba74744148b15099254d918671Owen Lin sel = Math.min(mCount - 1, sel + mColumns); 599666ea1b28a76aeba74744148b15099254d918671Owen Lin break; 600666ea1b28a76aeba74744148b15099254d918671Owen Lin case KeyEvent.KEYCODE_DPAD_CENTER: 601666ea1b28a76aeba74744148b15099254d918671Owen Lin if (event.getRepeatCount() == 0) { 602666ea1b28a76aeba74744148b15099254d918671Owen Lin mCurrentPressState |= CLICKING_FLAG; 603666ea1b28a76aeba74744148b15099254d918671Owen Lin mHandler.postDelayed(mLongPressCallback, 604666ea1b28a76aeba74744148b15099254d918671Owen Lin ViewConfiguration.getLongPressTimeout()); 605666ea1b28a76aeba74744148b15099254d918671Owen Lin } 606666ea1b28a76aeba74744148b15099254d918671Owen Lin break; 607666ea1b28a76aeba74744148b15099254d918671Owen Lin default: 608666ea1b28a76aeba74744148b15099254d918671Owen Lin return super.onKeyDown(keyCode, event); 609666ea1b28a76aeba74744148b15099254d918671Owen Lin } 610666ea1b28a76aeba74744148b15099254d918671Owen Lin } else { 611666ea1b28a76aeba74744148b15099254d918671Owen Lin switch (keyCode) { 612666ea1b28a76aeba74744148b15099254d918671Owen Lin case KeyEvent.KEYCODE_DPAD_RIGHT: 613666ea1b28a76aeba74744148b15099254d918671Owen Lin case KeyEvent.KEYCODE_DPAD_LEFT: 614666ea1b28a76aeba74744148b15099254d918671Owen Lin case KeyEvent.KEYCODE_DPAD_UP: 615666ea1b28a76aeba74744148b15099254d918671Owen Lin case KeyEvent.KEYCODE_DPAD_DOWN: 616666ea1b28a76aeba74744148b15099254d918671Owen Lin int startRow = 617666ea1b28a76aeba74744148b15099254d918671Owen Lin (mScrollY - mSpec.mCellSpacing) / mBlockHeight; 618666ea1b28a76aeba74744148b15099254d918671Owen Lin int topPos = startRow * mColumns; 619666ea1b28a76aeba74744148b15099254d918671Owen Lin Rect r = getRectForPosition(topPos); 620666ea1b28a76aeba74744148b15099254d918671Owen Lin if (r.top < getScrollY()) { 621666ea1b28a76aeba74744148b15099254d918671Owen Lin topPos += mColumns; 622666ea1b28a76aeba74744148b15099254d918671Owen Lin } 623666ea1b28a76aeba74744148b15099254d918671Owen Lin topPos = Math.min(mCount - 1, topPos); 624666ea1b28a76aeba74744148b15099254d918671Owen Lin sel = topPos; 625666ea1b28a76aeba74744148b15099254d918671Owen Lin break; 626666ea1b28a76aeba74744148b15099254d918671Owen Lin default: 627666ea1b28a76aeba74744148b15099254d918671Owen Lin return super.onKeyDown(keyCode, event); 628666ea1b28a76aeba74744148b15099254d918671Owen Lin } 629666ea1b28a76aeba74744148b15099254d918671Owen Lin } 630666ea1b28a76aeba74744148b15099254d918671Owen Lin setSelectedIndex(sel); 631666ea1b28a76aeba74744148b15099254d918671Owen Lin return true; 632666ea1b28a76aeba74744148b15099254d918671Owen Lin } 633666ea1b28a76aeba74744148b15099254d918671Owen Lin 634666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 635666ea1b28a76aeba74744148b15099254d918671Owen Lin public boolean onKeyUp(int keyCode, KeyEvent event) { 636666ea1b28a76aeba74744148b15099254d918671Owen Lin if (!canHandleEvent()) return false; 637666ea1b28a76aeba74744148b15099254d918671Owen Lin 638666ea1b28a76aeba74744148b15099254d918671Owen Lin if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { 639666ea1b28a76aeba74744148b15099254d918671Owen Lin mCurrentPressState &= ~CLICKING_FLAG; 640666ea1b28a76aeba74744148b15099254d918671Owen Lin invalidate(); 641666ea1b28a76aeba74744148b15099254d918671Owen Lin 642666ea1b28a76aeba74744148b15099254d918671Owen Lin // The keyUp doesn't get called when the longpress menu comes up. We 643666ea1b28a76aeba74744148b15099254d918671Owen Lin // only get here when the user lets go of the center key before the 644666ea1b28a76aeba74744148b15099254d918671Owen Lin // longpress menu comes up. 645666ea1b28a76aeba74744148b15099254d918671Owen Lin mHandler.removeCallbacks(mLongPressCallback); 646666ea1b28a76aeba74744148b15099254d918671Owen Lin 647666ea1b28a76aeba74744148b15099254d918671Owen Lin // open the photo 648666ea1b28a76aeba74744148b15099254d918671Owen Lin mListener.onImageClicked(mCurrentSelection); 649666ea1b28a76aeba74744148b15099254d918671Owen Lin return true; 650666ea1b28a76aeba74744148b15099254d918671Owen Lin } 651666ea1b28a76aeba74744148b15099254d918671Owen Lin return super.onKeyUp(keyCode, event); 652666ea1b28a76aeba74744148b15099254d918671Owen Lin } 653666ea1b28a76aeba74744148b15099254d918671Owen Lin 654666ea1b28a76aeba74744148b15099254d918671Owen Lin private void paintDecoration(Canvas canvas) { 655666ea1b28a76aeba74744148b15099254d918671Owen Lin if (!mDrawAdapter.needsDecoration()) return; 656666ea1b28a76aeba74744148b15099254d918671Owen Lin 657666ea1b28a76aeba74744148b15099254d918671Owen Lin // Calculate visible region according to scroll position. 658666ea1b28a76aeba74744148b15099254d918671Owen Lin int startRow = (mScrollY - mSpec.mCellSpacing) / mBlockHeight; 659666ea1b28a76aeba74744148b15099254d918671Owen Lin int endRow = (mScrollY + getHeight() - mSpec.mCellSpacing - 1) 660666ea1b28a76aeba74744148b15099254d918671Owen Lin / mBlockHeight + 1; 661666ea1b28a76aeba74744148b15099254d918671Owen Lin 662666ea1b28a76aeba74744148b15099254d918671Owen Lin // Limit startRow and endRow to the valid range. 663666ea1b28a76aeba74744148b15099254d918671Owen Lin // Make sure we handle the mRows == 0 case right. 664666ea1b28a76aeba74744148b15099254d918671Owen Lin startRow = Math.max(Math.min(startRow, mRows - 1), 0); 665666ea1b28a76aeba74744148b15099254d918671Owen Lin endRow = Math.max(Math.min(endRow, mRows), 0); 666666ea1b28a76aeba74744148b15099254d918671Owen Lin 667666ea1b28a76aeba74744148b15099254d918671Owen Lin int startIndex = startRow * mColumns; 668666ea1b28a76aeba74744148b15099254d918671Owen Lin int endIndex = Math.min(endRow * mColumns, mCount); 669666ea1b28a76aeba74744148b15099254d918671Owen Lin 670666ea1b28a76aeba74744148b15099254d918671Owen Lin int xPos = mSpec.mLeftEdgePadding; 671666ea1b28a76aeba74744148b15099254d918671Owen Lin int yPos = mSpec.mCellSpacing + startRow * mBlockHeight; 672666ea1b28a76aeba74744148b15099254d918671Owen Lin int off = 0; 673666ea1b28a76aeba74744148b15099254d918671Owen Lin for (int i = startIndex; i < endIndex; i++) { 674666ea1b28a76aeba74744148b15099254d918671Owen Lin IImage image = mAllImages.getImageAt(i); 675666ea1b28a76aeba74744148b15099254d918671Owen Lin 676666ea1b28a76aeba74744148b15099254d918671Owen Lin mDrawAdapter.drawDecoration(canvas, image, xPos, yPos, 677666ea1b28a76aeba74744148b15099254d918671Owen Lin mSpec.mCellWidth, mSpec.mCellHeight); 678666ea1b28a76aeba74744148b15099254d918671Owen Lin 679666ea1b28a76aeba74744148b15099254d918671Owen Lin // Calculate next position 680666ea1b28a76aeba74744148b15099254d918671Owen Lin off += 1; 681666ea1b28a76aeba74744148b15099254d918671Owen Lin if (off == mColumns) { 682666ea1b28a76aeba74744148b15099254d918671Owen Lin xPos = mSpec.mLeftEdgePadding; 683666ea1b28a76aeba74744148b15099254d918671Owen Lin yPos += mBlockHeight; 684666ea1b28a76aeba74744148b15099254d918671Owen Lin off = 0; 685666ea1b28a76aeba74744148b15099254d918671Owen Lin } else { 686666ea1b28a76aeba74744148b15099254d918671Owen Lin xPos += mSpec.mCellWidth + mSpec.mCellSpacing; 687666ea1b28a76aeba74744148b15099254d918671Owen Lin } 688666ea1b28a76aeba74744148b15099254d918671Owen Lin } 689666ea1b28a76aeba74744148b15099254d918671Owen Lin } 690666ea1b28a76aeba74744148b15099254d918671Owen Lin 691666ea1b28a76aeba74744148b15099254d918671Owen Lin private void paintSelection(Canvas canvas) { 692666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mCurrentSelection == INDEX_NONE) return; 693666ea1b28a76aeba74744148b15099254d918671Owen Lin 694666ea1b28a76aeba74744148b15099254d918671Owen Lin int row = mCurrentSelection / mColumns; 695666ea1b28a76aeba74744148b15099254d918671Owen Lin int col = mCurrentSelection - (row * mColumns); 696666ea1b28a76aeba74744148b15099254d918671Owen Lin 697666ea1b28a76aeba74744148b15099254d918671Owen Lin int spacing = mSpec.mCellSpacing; 698666ea1b28a76aeba74744148b15099254d918671Owen Lin int leftSpacing = mSpec.mLeftEdgePadding; 699666ea1b28a76aeba74744148b15099254d918671Owen Lin int xPos = leftSpacing + (col * (mSpec.mCellWidth + spacing)); 700666ea1b28a76aeba74744148b15099254d918671Owen Lin int yTop = spacing + (row * mBlockHeight); 701666ea1b28a76aeba74744148b15099254d918671Owen Lin 702666ea1b28a76aeba74744148b15099254d918671Owen Lin int type = OUTLINE_SELECTED; 703666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mCurrentPressState != 0) { 704666ea1b28a76aeba74744148b15099254d918671Owen Lin type = OUTLINE_PRESSED; 705666ea1b28a76aeba74744148b15099254d918671Owen Lin } 706666ea1b28a76aeba74744148b15099254d918671Owen Lin canvas.drawBitmap(mOutline[type], xPos, yTop, null); 707666ea1b28a76aeba74744148b15099254d918671Owen Lin } 708666ea1b28a76aeba74744148b15099254d918671Owen Lin} 709666ea1b28a76aeba74744148b15099254d918671Owen Lin 710666ea1b28a76aeba74744148b15099254d918671Owen Linclass ImageBlockManager { 711666ea1b28a76aeba74744148b15099254d918671Owen Lin @SuppressWarnings("unused") 712666ea1b28a76aeba74744148b15099254d918671Owen Lin private static final String TAG = "ImageBlockManager"; 713666ea1b28a76aeba74744148b15099254d918671Owen Lin 714666ea1b28a76aeba74744148b15099254d918671Owen Lin // Number of rows we want to cache. 715666ea1b28a76aeba74744148b15099254d918671Owen Lin // Assume there are 6 rows per page, this caches 5 pages. 716666ea1b28a76aeba74744148b15099254d918671Owen Lin private static final int CACHE_ROWS = 30; 717666ea1b28a76aeba74744148b15099254d918671Owen Lin 718666ea1b28a76aeba74744148b15099254d918671Owen Lin // mCache maps from row number to the ImageBlock. 719666ea1b28a76aeba74744148b15099254d918671Owen Lin private final HashMap<Integer, ImageBlock> mCache; 720666ea1b28a76aeba74744148b15099254d918671Owen Lin 721666ea1b28a76aeba74744148b15099254d918671Owen Lin // These are parameters set in the constructor. 722666ea1b28a76aeba74744148b15099254d918671Owen Lin private final Handler mHandler; 723666ea1b28a76aeba74744148b15099254d918671Owen Lin private final Runnable mRedrawCallback; // Called after a row is loaded, 724666ea1b28a76aeba74744148b15099254d918671Owen Lin // so GridViewSpecial can draw 725666ea1b28a76aeba74744148b15099254d918671Owen Lin // again using the new images. 726666ea1b28a76aeba74744148b15099254d918671Owen Lin private final IImageList mImageList; 727666ea1b28a76aeba74744148b15099254d918671Owen Lin private final ImageLoader mLoader; 728666ea1b28a76aeba74744148b15099254d918671Owen Lin private final GridViewSpecial.DrawAdapter mDrawAdapter; 729666ea1b28a76aeba74744148b15099254d918671Owen Lin private final GridViewSpecial.LayoutSpec mSpec; 730666ea1b28a76aeba74744148b15099254d918671Owen Lin private final int mColumns; // Columns per row. 731666ea1b28a76aeba74744148b15099254d918671Owen Lin private final int mBlockWidth; // The width of an ImageBlock. 732666ea1b28a76aeba74744148b15099254d918671Owen Lin private final Bitmap mOutline; // The outline bitmap put on top of each 733666ea1b28a76aeba74744148b15099254d918671Owen Lin // image. 734666ea1b28a76aeba74744148b15099254d918671Owen Lin private final int mCount; // Cache mImageList.getCount(). 735666ea1b28a76aeba74744148b15099254d918671Owen Lin private final int mRows; // Cache (mCount + mColumns - 1) / mColumns 736666ea1b28a76aeba74744148b15099254d918671Owen Lin private final int mBlockHeight; // The height of an ImageBlock. 737666ea1b28a76aeba74744148b15099254d918671Owen Lin 738666ea1b28a76aeba74744148b15099254d918671Owen Lin // Visible row range: [mStartRow, mEndRow). Set by setVisibleRows(). 739666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mStartRow = 0; 740666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mEndRow = 0; 741666ea1b28a76aeba74744148b15099254d918671Owen Lin 742666ea1b28a76aeba74744148b15099254d918671Owen Lin ImageBlockManager(Handler handler, Runnable redrawCallback, 743666ea1b28a76aeba74744148b15099254d918671Owen Lin IImageList imageList, ImageLoader loader, 744666ea1b28a76aeba74744148b15099254d918671Owen Lin GridViewSpecial.DrawAdapter adapter, 745666ea1b28a76aeba74744148b15099254d918671Owen Lin GridViewSpecial.LayoutSpec spec, 746666ea1b28a76aeba74744148b15099254d918671Owen Lin int columns, int blockWidth, Bitmap outline) { 747666ea1b28a76aeba74744148b15099254d918671Owen Lin mHandler = handler; 748666ea1b28a76aeba74744148b15099254d918671Owen Lin mRedrawCallback = redrawCallback; 749666ea1b28a76aeba74744148b15099254d918671Owen Lin mImageList = imageList; 750666ea1b28a76aeba74744148b15099254d918671Owen Lin mLoader = loader; 751666ea1b28a76aeba74744148b15099254d918671Owen Lin mDrawAdapter = adapter; 752666ea1b28a76aeba74744148b15099254d918671Owen Lin mSpec = spec; 753666ea1b28a76aeba74744148b15099254d918671Owen Lin mColumns = columns; 754666ea1b28a76aeba74744148b15099254d918671Owen Lin mBlockWidth = blockWidth; 755666ea1b28a76aeba74744148b15099254d918671Owen Lin mOutline = outline; 756666ea1b28a76aeba74744148b15099254d918671Owen Lin mBlockHeight = mSpec.mCellSpacing + mSpec.mCellHeight; 757666ea1b28a76aeba74744148b15099254d918671Owen Lin mCount = imageList.getCount(); 758666ea1b28a76aeba74744148b15099254d918671Owen Lin mRows = (mCount + mColumns - 1) / mColumns; 759666ea1b28a76aeba74744148b15099254d918671Owen Lin mCache = new HashMap<Integer, ImageBlock>(); 760666ea1b28a76aeba74744148b15099254d918671Owen Lin mPendingRequest = 0; 761666ea1b28a76aeba74744148b15099254d918671Owen Lin initGraphics(); 762666ea1b28a76aeba74744148b15099254d918671Owen Lin } 763666ea1b28a76aeba74744148b15099254d918671Owen Lin 764666ea1b28a76aeba74744148b15099254d918671Owen Lin // Set the window of visible rows. Once set we will start to load them as 765666ea1b28a76aeba74744148b15099254d918671Owen Lin // soon as possible (if they are not already in cache). 766666ea1b28a76aeba74744148b15099254d918671Owen Lin public void setVisibleRows(int startRow, int endRow) { 767666ea1b28a76aeba74744148b15099254d918671Owen Lin if (startRow != mStartRow || endRow != mEndRow) { 768666ea1b28a76aeba74744148b15099254d918671Owen Lin mStartRow = startRow; 769666ea1b28a76aeba74744148b15099254d918671Owen Lin mEndRow = endRow; 770666ea1b28a76aeba74744148b15099254d918671Owen Lin startLoading(); 771666ea1b28a76aeba74744148b15099254d918671Owen Lin } 772666ea1b28a76aeba74744148b15099254d918671Owen Lin } 773666ea1b28a76aeba74744148b15099254d918671Owen Lin 774666ea1b28a76aeba74744148b15099254d918671Owen Lin int mPendingRequest; // Number of pending requests (sent to ImageLoader). 775666ea1b28a76aeba74744148b15099254d918671Owen Lin // We want to keep enough requests in ImageLoader's queue, but not too 776666ea1b28a76aeba74744148b15099254d918671Owen Lin // many. 777666ea1b28a76aeba74744148b15099254d918671Owen Lin static final int REQUESTS_LOW = 3; 778666ea1b28a76aeba74744148b15099254d918671Owen Lin static final int REQUESTS_HIGH = 6; 779666ea1b28a76aeba74744148b15099254d918671Owen Lin 780666ea1b28a76aeba74744148b15099254d918671Owen Lin // After clear requests currently in queue, start loading the thumbnails. 781666ea1b28a76aeba74744148b15099254d918671Owen Lin // We need to clear the queue first because the proper order of loading 782666ea1b28a76aeba74744148b15099254d918671Owen Lin // may have changed (because the visible region changed, or some images 783666ea1b28a76aeba74744148b15099254d918671Owen Lin // have been invalidated). 784666ea1b28a76aeba74744148b15099254d918671Owen Lin private void startLoading() { 785666ea1b28a76aeba74744148b15099254d918671Owen Lin clearLoaderQueue(); 786666ea1b28a76aeba74744148b15099254d918671Owen Lin continueLoading(); 787666ea1b28a76aeba74744148b15099254d918671Owen Lin } 788666ea1b28a76aeba74744148b15099254d918671Owen Lin 789666ea1b28a76aeba74744148b15099254d918671Owen Lin private void clearLoaderQueue() { 790666ea1b28a76aeba74744148b15099254d918671Owen Lin int[] tags = mLoader.clearQueue(); 791666ea1b28a76aeba74744148b15099254d918671Owen Lin for (int pos : tags) { 792666ea1b28a76aeba74744148b15099254d918671Owen Lin int row = pos / mColumns; 793666ea1b28a76aeba74744148b15099254d918671Owen Lin int col = pos - row * mColumns; 794666ea1b28a76aeba74744148b15099254d918671Owen Lin ImageBlock blk = mCache.get(row); 795666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert(blk != null); // We won't reuse the block if it has pending 796666ea1b28a76aeba74744148b15099254d918671Owen Lin // requests. See getEmptyBlock(). 797666ea1b28a76aeba74744148b15099254d918671Owen Lin blk.cancelRequest(col); 798666ea1b28a76aeba74744148b15099254d918671Owen Lin } 799666ea1b28a76aeba74744148b15099254d918671Owen Lin } 800666ea1b28a76aeba74744148b15099254d918671Owen Lin 801666ea1b28a76aeba74744148b15099254d918671Owen Lin // Scan the cache and send requests to ImageLoader if needed. 802666ea1b28a76aeba74744148b15099254d918671Owen Lin private void continueLoading() { 803666ea1b28a76aeba74744148b15099254d918671Owen Lin // Check if we still have enough requests in the queue. 804666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mPendingRequest >= REQUESTS_LOW) return; 805666ea1b28a76aeba74744148b15099254d918671Owen Lin 806666ea1b28a76aeba74744148b15099254d918671Owen Lin // Scan the visible rows. 807666ea1b28a76aeba74744148b15099254d918671Owen Lin for (int i = mStartRow; i < mEndRow; i++) { 808666ea1b28a76aeba74744148b15099254d918671Owen Lin if (scanOne(i)) return; 809666ea1b28a76aeba74744148b15099254d918671Owen Lin } 810666ea1b28a76aeba74744148b15099254d918671Owen Lin 811666ea1b28a76aeba74744148b15099254d918671Owen Lin int range = (CACHE_ROWS - (mEndRow - mStartRow)) / 2; 812666ea1b28a76aeba74744148b15099254d918671Owen Lin // Scan other rows. 813666ea1b28a76aeba74744148b15099254d918671Owen Lin // d is the distance between the row and visible region. 814666ea1b28a76aeba74744148b15099254d918671Owen Lin for (int d = 1; d <= range; d++) { 815666ea1b28a76aeba74744148b15099254d918671Owen Lin int after = mEndRow - 1 + d; 816666ea1b28a76aeba74744148b15099254d918671Owen Lin int before = mStartRow - d; 817666ea1b28a76aeba74744148b15099254d918671Owen Lin if (after >= mRows && before < 0) { 818666ea1b28a76aeba74744148b15099254d918671Owen Lin break; // Nothing more the scan. 819666ea1b28a76aeba74744148b15099254d918671Owen Lin } 820666ea1b28a76aeba74744148b15099254d918671Owen Lin if (after < mRows && scanOne(after)) return; 821666ea1b28a76aeba74744148b15099254d918671Owen Lin if (before >= 0 && scanOne(before)) return; 822666ea1b28a76aeba74744148b15099254d918671Owen Lin } 823666ea1b28a76aeba74744148b15099254d918671Owen Lin } 824666ea1b28a76aeba74744148b15099254d918671Owen Lin 825666ea1b28a76aeba74744148b15099254d918671Owen Lin // Returns true if we can stop scanning. 826666ea1b28a76aeba74744148b15099254d918671Owen Lin private boolean scanOne(int i) { 827666ea1b28a76aeba74744148b15099254d918671Owen Lin mPendingRequest += tryToLoad(i); 828666ea1b28a76aeba74744148b15099254d918671Owen Lin return mPendingRequest >= REQUESTS_HIGH; 829666ea1b28a76aeba74744148b15099254d918671Owen Lin } 830666ea1b28a76aeba74744148b15099254d918671Owen Lin 831666ea1b28a76aeba74744148b15099254d918671Owen Lin // Returns number of requests we issued for this row. 832666ea1b28a76aeba74744148b15099254d918671Owen Lin private int tryToLoad(int row) { 833666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert(row >= 0 && row < mRows); 834666ea1b28a76aeba74744148b15099254d918671Owen Lin ImageBlock blk = mCache.get(row); 835666ea1b28a76aeba74744148b15099254d918671Owen Lin if (blk == null) { 836666ea1b28a76aeba74744148b15099254d918671Owen Lin // Find an empty block 837666ea1b28a76aeba74744148b15099254d918671Owen Lin blk = getEmptyBlock(); 838666ea1b28a76aeba74744148b15099254d918671Owen Lin blk.setRow(row); 839666ea1b28a76aeba74744148b15099254d918671Owen Lin blk.invalidate(); 840666ea1b28a76aeba74744148b15099254d918671Owen Lin mCache.put(row, blk); 841666ea1b28a76aeba74744148b15099254d918671Owen Lin } 842666ea1b28a76aeba74744148b15099254d918671Owen Lin return blk.loadImages(); 843666ea1b28a76aeba74744148b15099254d918671Owen Lin } 844666ea1b28a76aeba74744148b15099254d918671Owen Lin 845666ea1b28a76aeba74744148b15099254d918671Owen Lin // Get an empty block for the cache. 846666ea1b28a76aeba74744148b15099254d918671Owen Lin private ImageBlock getEmptyBlock() { 847666ea1b28a76aeba74744148b15099254d918671Owen Lin // See if we can allocate a new block. 848666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mCache.size() < CACHE_ROWS) { 849666ea1b28a76aeba74744148b15099254d918671Owen Lin return new ImageBlock(); 850666ea1b28a76aeba74744148b15099254d918671Owen Lin } 851666ea1b28a76aeba74744148b15099254d918671Owen Lin // Reclaim the old block with largest distance from the visible region. 852666ea1b28a76aeba74744148b15099254d918671Owen Lin int bestDistance = -1; 853666ea1b28a76aeba74744148b15099254d918671Owen Lin int bestIndex = -1; 854666ea1b28a76aeba74744148b15099254d918671Owen Lin for (int index : mCache.keySet()) { 855666ea1b28a76aeba74744148b15099254d918671Owen Lin // Make sure we don't reclaim a block which still has pending 856666ea1b28a76aeba74744148b15099254d918671Owen Lin // request. 857666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mCache.get(index).hasPendingRequests()) { 858666ea1b28a76aeba74744148b15099254d918671Owen Lin continue; 859666ea1b28a76aeba74744148b15099254d918671Owen Lin } 860666ea1b28a76aeba74744148b15099254d918671Owen Lin int dist = 0; 861666ea1b28a76aeba74744148b15099254d918671Owen Lin if (index >= mEndRow) { 862666ea1b28a76aeba74744148b15099254d918671Owen Lin dist = index - mEndRow + 1; 863666ea1b28a76aeba74744148b15099254d918671Owen Lin } else if (index < mStartRow) { 864666ea1b28a76aeba74744148b15099254d918671Owen Lin dist = mStartRow - index; 865666ea1b28a76aeba74744148b15099254d918671Owen Lin } else { 866666ea1b28a76aeba74744148b15099254d918671Owen Lin // Inside the visible region. 867666ea1b28a76aeba74744148b15099254d918671Owen Lin continue; 868666ea1b28a76aeba74744148b15099254d918671Owen Lin } 869666ea1b28a76aeba74744148b15099254d918671Owen Lin if (dist > bestDistance) { 870666ea1b28a76aeba74744148b15099254d918671Owen Lin bestDistance = dist; 871666ea1b28a76aeba74744148b15099254d918671Owen Lin bestIndex = index; 872666ea1b28a76aeba74744148b15099254d918671Owen Lin } 873666ea1b28a76aeba74744148b15099254d918671Owen Lin } 874666ea1b28a76aeba74744148b15099254d918671Owen Lin return mCache.remove(bestIndex); 875666ea1b28a76aeba74744148b15099254d918671Owen Lin } 876666ea1b28a76aeba74744148b15099254d918671Owen Lin 877666ea1b28a76aeba74744148b15099254d918671Owen Lin public void invalidateImage(int index) { 878666ea1b28a76aeba74744148b15099254d918671Owen Lin int row = index / mColumns; 879666ea1b28a76aeba74744148b15099254d918671Owen Lin int col = index - (row * mColumns); 880666ea1b28a76aeba74744148b15099254d918671Owen Lin ImageBlock blk = mCache.get(row); 881666ea1b28a76aeba74744148b15099254d918671Owen Lin if (blk == null) return; 882666ea1b28a76aeba74744148b15099254d918671Owen Lin if ((blk.mCompletedMask & (1 << col)) != 0) { 883666ea1b28a76aeba74744148b15099254d918671Owen Lin blk.mCompletedMask &= ~(1 << col); 884666ea1b28a76aeba74744148b15099254d918671Owen Lin } 885666ea1b28a76aeba74744148b15099254d918671Owen Lin startLoading(); 886666ea1b28a76aeba74744148b15099254d918671Owen Lin } 887666ea1b28a76aeba74744148b15099254d918671Owen Lin 888666ea1b28a76aeba74744148b15099254d918671Owen Lin // After calling recycle(), the instance should not be used anymore. 889666ea1b28a76aeba74744148b15099254d918671Owen Lin public void recycle() { 890666ea1b28a76aeba74744148b15099254d918671Owen Lin for (ImageBlock blk : mCache.values()) { 891666ea1b28a76aeba74744148b15099254d918671Owen Lin blk.recycle(); 892666ea1b28a76aeba74744148b15099254d918671Owen Lin } 893666ea1b28a76aeba74744148b15099254d918671Owen Lin mCache.clear(); 894666ea1b28a76aeba74744148b15099254d918671Owen Lin mEmptyBitmap.recycle(); 895666ea1b28a76aeba74744148b15099254d918671Owen Lin } 896666ea1b28a76aeba74744148b15099254d918671Owen Lin 897666ea1b28a76aeba74744148b15099254d918671Owen Lin // Draw the images to the given canvas. 898666ea1b28a76aeba74744148b15099254d918671Owen Lin public void doDraw(Canvas canvas, int thisWidth, int thisHeight, 899666ea1b28a76aeba74744148b15099254d918671Owen Lin int scrollPos) { 900666ea1b28a76aeba74744148b15099254d918671Owen Lin final int height = mBlockHeight; 901666ea1b28a76aeba74744148b15099254d918671Owen Lin 902666ea1b28a76aeba74744148b15099254d918671Owen Lin // Note that currentBlock could be negative. 903666ea1b28a76aeba74744148b15099254d918671Owen Lin int currentBlock = (scrollPos < 0) 904666ea1b28a76aeba74744148b15099254d918671Owen Lin ? ((scrollPos - height + 1) / height) 905666ea1b28a76aeba74744148b15099254d918671Owen Lin : (scrollPos / height); 906666ea1b28a76aeba74744148b15099254d918671Owen Lin 907666ea1b28a76aeba74744148b15099254d918671Owen Lin while (true) { 908666ea1b28a76aeba74744148b15099254d918671Owen Lin final int yPos = currentBlock * height; 909666ea1b28a76aeba74744148b15099254d918671Owen Lin if (yPos >= scrollPos + thisHeight) { 910666ea1b28a76aeba74744148b15099254d918671Owen Lin break; 911666ea1b28a76aeba74744148b15099254d918671Owen Lin } 912666ea1b28a76aeba74744148b15099254d918671Owen Lin 913666ea1b28a76aeba74744148b15099254d918671Owen Lin ImageBlock blk = mCache.get(currentBlock); 914666ea1b28a76aeba74744148b15099254d918671Owen Lin if (blk != null) { 915666ea1b28a76aeba74744148b15099254d918671Owen Lin blk.doDraw(canvas, 0, yPos); 916666ea1b28a76aeba74744148b15099254d918671Owen Lin } else { 917666ea1b28a76aeba74744148b15099254d918671Owen Lin drawEmptyBlock(canvas, 0, yPos, currentBlock); 918666ea1b28a76aeba74744148b15099254d918671Owen Lin } 919666ea1b28a76aeba74744148b15099254d918671Owen Lin 920666ea1b28a76aeba74744148b15099254d918671Owen Lin currentBlock += 1; 921666ea1b28a76aeba74744148b15099254d918671Owen Lin } 922666ea1b28a76aeba74744148b15099254d918671Owen Lin } 923666ea1b28a76aeba74744148b15099254d918671Owen Lin 924666ea1b28a76aeba74744148b15099254d918671Owen Lin // Return number of columns in the given row. (This could be less than 925666ea1b28a76aeba74744148b15099254d918671Owen Lin // mColumns for the last row). 926666ea1b28a76aeba74744148b15099254d918671Owen Lin private int numColumns(int row) { 927666ea1b28a76aeba74744148b15099254d918671Owen Lin return Math.min(mColumns, mCount - row * mColumns); 928666ea1b28a76aeba74744148b15099254d918671Owen Lin } 929666ea1b28a76aeba74744148b15099254d918671Owen Lin 930666ea1b28a76aeba74744148b15099254d918671Owen Lin // Draw a block which has not been loaded. 931666ea1b28a76aeba74744148b15099254d918671Owen Lin private void drawEmptyBlock(Canvas canvas, int xPos, int yPos, int row) { 932666ea1b28a76aeba74744148b15099254d918671Owen Lin // Draw the background. 933666ea1b28a76aeba74744148b15099254d918671Owen Lin canvas.drawRect(xPos, yPos, xPos + mBlockWidth, yPos + mBlockHeight, 934666ea1b28a76aeba74744148b15099254d918671Owen Lin mBackgroundPaint); 935666ea1b28a76aeba74744148b15099254d918671Owen Lin 936666ea1b28a76aeba74744148b15099254d918671Owen Lin // Draw the empty images. 937666ea1b28a76aeba74744148b15099254d918671Owen Lin int x = xPos + mSpec.mLeftEdgePadding; 938666ea1b28a76aeba74744148b15099254d918671Owen Lin int y = yPos + mSpec.mCellSpacing; 939666ea1b28a76aeba74744148b15099254d918671Owen Lin int cols = numColumns(row); 940666ea1b28a76aeba74744148b15099254d918671Owen Lin 941666ea1b28a76aeba74744148b15099254d918671Owen Lin for (int i = 0; i < cols; i++) { 942666ea1b28a76aeba74744148b15099254d918671Owen Lin canvas.drawBitmap(mEmptyBitmap, x, y, null); 943666ea1b28a76aeba74744148b15099254d918671Owen Lin x += (mSpec.mCellWidth + mSpec.mCellSpacing); 944666ea1b28a76aeba74744148b15099254d918671Owen Lin } 945666ea1b28a76aeba74744148b15099254d918671Owen Lin } 946666ea1b28a76aeba74744148b15099254d918671Owen Lin 947666ea1b28a76aeba74744148b15099254d918671Owen Lin // mEmptyBitmap is what we draw if we the wanted block hasn't been loaded. 948666ea1b28a76aeba74744148b15099254d918671Owen Lin // (If the user scrolls too fast). It is a gray image with normal outline. 949666ea1b28a76aeba74744148b15099254d918671Owen Lin // mBackgroundPaint is used to draw the (black) background outside 950666ea1b28a76aeba74744148b15099254d918671Owen Lin // mEmptyBitmap. 951666ea1b28a76aeba74744148b15099254d918671Owen Lin Paint mBackgroundPaint; 952666ea1b28a76aeba74744148b15099254d918671Owen Lin private Bitmap mEmptyBitmap; 953666ea1b28a76aeba74744148b15099254d918671Owen Lin 954666ea1b28a76aeba74744148b15099254d918671Owen Lin private void initGraphics() { 955666ea1b28a76aeba74744148b15099254d918671Owen Lin mBackgroundPaint = new Paint(); 956666ea1b28a76aeba74744148b15099254d918671Owen Lin mBackgroundPaint.setStyle(Paint.Style.FILL); 957666ea1b28a76aeba74744148b15099254d918671Owen Lin mBackgroundPaint.setColor(0xFF000000); // black 958666ea1b28a76aeba74744148b15099254d918671Owen Lin mEmptyBitmap = Bitmap.createBitmap(mSpec.mCellWidth, mSpec.mCellHeight, 959666ea1b28a76aeba74744148b15099254d918671Owen Lin Bitmap.Config.RGB_565); 960666ea1b28a76aeba74744148b15099254d918671Owen Lin Canvas canvas = new Canvas(mEmptyBitmap); 961666ea1b28a76aeba74744148b15099254d918671Owen Lin canvas.drawRGB(0xDD, 0xDD, 0xDD); 962666ea1b28a76aeba74744148b15099254d918671Owen Lin canvas.drawBitmap(mOutline, 0, 0, null); 963666ea1b28a76aeba74744148b15099254d918671Owen Lin } 964666ea1b28a76aeba74744148b15099254d918671Owen Lin 965666ea1b28a76aeba74744148b15099254d918671Owen Lin // ImageBlock stores bitmap for one row. The loaded thumbnail images are 966666ea1b28a76aeba74744148b15099254d918671Owen Lin // drawn to mBitmap. mBitmap is later used in onDraw() of GridViewSpecial. 967666ea1b28a76aeba74744148b15099254d918671Owen Lin private class ImageBlock { 968666ea1b28a76aeba74744148b15099254d918671Owen Lin private Bitmap mBitmap; 969666ea1b28a76aeba74744148b15099254d918671Owen Lin private final Canvas mCanvas; 970666ea1b28a76aeba74744148b15099254d918671Owen Lin 971666ea1b28a76aeba74744148b15099254d918671Owen Lin // Columns which have been requested to the loader 972666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mRequestedMask; 973666ea1b28a76aeba74744148b15099254d918671Owen Lin 974666ea1b28a76aeba74744148b15099254d918671Owen Lin // Columns which have been completed from the loader 975666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mCompletedMask; 976666ea1b28a76aeba74744148b15099254d918671Owen Lin 977666ea1b28a76aeba74744148b15099254d918671Owen Lin // The row number this block represents. 978666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mRow; 979666ea1b28a76aeba74744148b15099254d918671Owen Lin 980666ea1b28a76aeba74744148b15099254d918671Owen Lin public ImageBlock() { 981666ea1b28a76aeba74744148b15099254d918671Owen Lin mBitmap = Bitmap.createBitmap(mBlockWidth, mBlockHeight, 982666ea1b28a76aeba74744148b15099254d918671Owen Lin Bitmap.Config.RGB_565); 983666ea1b28a76aeba74744148b15099254d918671Owen Lin mCanvas = new Canvas(mBitmap); 984666ea1b28a76aeba74744148b15099254d918671Owen Lin mRow = -1; 985666ea1b28a76aeba74744148b15099254d918671Owen Lin } 986666ea1b28a76aeba74744148b15099254d918671Owen Lin 987666ea1b28a76aeba74744148b15099254d918671Owen Lin public void setRow(int row) { 988666ea1b28a76aeba74744148b15099254d918671Owen Lin mRow = row; 989666ea1b28a76aeba74744148b15099254d918671Owen Lin } 990666ea1b28a76aeba74744148b15099254d918671Owen Lin 991666ea1b28a76aeba74744148b15099254d918671Owen Lin public void invalidate() { 992666ea1b28a76aeba74744148b15099254d918671Owen Lin // We do not change mRequestedMask or do cancelAllRequests() 993666ea1b28a76aeba74744148b15099254d918671Owen Lin // because the data coming from pending requests are valid. (We only 994666ea1b28a76aeba74744148b15099254d918671Owen Lin // invalidate data which has been drawn to the bitmap). 995666ea1b28a76aeba74744148b15099254d918671Owen Lin mCompletedMask = 0; 996666ea1b28a76aeba74744148b15099254d918671Owen Lin } 997666ea1b28a76aeba74744148b15099254d918671Owen Lin 998666ea1b28a76aeba74744148b15099254d918671Owen Lin // After recycle, the ImageBlock instance should not be accessed. 999666ea1b28a76aeba74744148b15099254d918671Owen Lin public void recycle() { 1000666ea1b28a76aeba74744148b15099254d918671Owen Lin cancelAllRequests(); 1001666ea1b28a76aeba74744148b15099254d918671Owen Lin mBitmap.recycle(); 1002666ea1b28a76aeba74744148b15099254d918671Owen Lin mBitmap = null; 1003666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1004666ea1b28a76aeba74744148b15099254d918671Owen Lin 1005666ea1b28a76aeba74744148b15099254d918671Owen Lin private boolean isVisible() { 1006666ea1b28a76aeba74744148b15099254d918671Owen Lin return mRow >= mStartRow && mRow < mEndRow; 1007666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1008666ea1b28a76aeba74744148b15099254d918671Owen Lin 1009666ea1b28a76aeba74744148b15099254d918671Owen Lin // Returns number of requests submitted to ImageLoader. 1010666ea1b28a76aeba74744148b15099254d918671Owen Lin public int loadImages() { 1011666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert(mRow != -1); 1012666ea1b28a76aeba74744148b15099254d918671Owen Lin 1013666ea1b28a76aeba74744148b15099254d918671Owen Lin int columns = numColumns(mRow); 1014666ea1b28a76aeba74744148b15099254d918671Owen Lin 1015666ea1b28a76aeba74744148b15099254d918671Owen Lin // Calculate what we need. 1016666ea1b28a76aeba74744148b15099254d918671Owen Lin int needMask = ((1 << columns) - 1) 1017666ea1b28a76aeba74744148b15099254d918671Owen Lin & ~(mCompletedMask | mRequestedMask); 1018666ea1b28a76aeba74744148b15099254d918671Owen Lin 1019666ea1b28a76aeba74744148b15099254d918671Owen Lin if (needMask == 0) { 1020666ea1b28a76aeba74744148b15099254d918671Owen Lin return 0; 1021666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1022666ea1b28a76aeba74744148b15099254d918671Owen Lin 1023666ea1b28a76aeba74744148b15099254d918671Owen Lin int retVal = 0; 1024666ea1b28a76aeba74744148b15099254d918671Owen Lin int base = mRow * mColumns; 1025666ea1b28a76aeba74744148b15099254d918671Owen Lin 1026666ea1b28a76aeba74744148b15099254d918671Owen Lin for (int col = 0; col < columns; col++) { 1027666ea1b28a76aeba74744148b15099254d918671Owen Lin if ((needMask & (1 << col)) == 0) { 1028666ea1b28a76aeba74744148b15099254d918671Owen Lin continue; 1029666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1030666ea1b28a76aeba74744148b15099254d918671Owen Lin 1031666ea1b28a76aeba74744148b15099254d918671Owen Lin int pos = base + col; 1032666ea1b28a76aeba74744148b15099254d918671Owen Lin 1033666ea1b28a76aeba74744148b15099254d918671Owen Lin final IImage image = mImageList.getImageAt(pos); 1034666ea1b28a76aeba74744148b15099254d918671Owen Lin if (image != null) { 1035666ea1b28a76aeba74744148b15099254d918671Owen Lin // This callback is passed to ImageLoader. It will invoke 1036666ea1b28a76aeba74744148b15099254d918671Owen Lin // loadImageDone() in the main thread. We limit the callback 1037666ea1b28a76aeba74744148b15099254d918671Owen Lin // thread to be in this very short function. All other 1038666ea1b28a76aeba74744148b15099254d918671Owen Lin // processing is done in the main thread. 1039666ea1b28a76aeba74744148b15099254d918671Owen Lin final int colFinal = col; 1040666ea1b28a76aeba74744148b15099254d918671Owen Lin ImageLoader.LoadedCallback cb = 1041666ea1b28a76aeba74744148b15099254d918671Owen Lin new ImageLoader.LoadedCallback() { 1042666ea1b28a76aeba74744148b15099254d918671Owen Lin public void run(final Bitmap b) { 1043666ea1b28a76aeba74744148b15099254d918671Owen Lin mHandler.post(new Runnable() { 1044666ea1b28a76aeba74744148b15099254d918671Owen Lin public void run() { 1045666ea1b28a76aeba74744148b15099254d918671Owen Lin loadImageDone(image, b, 1046666ea1b28a76aeba74744148b15099254d918671Owen Lin colFinal); 1047666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1048666ea1b28a76aeba74744148b15099254d918671Owen Lin }); 1049666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1050666ea1b28a76aeba74744148b15099254d918671Owen Lin }; 1051666ea1b28a76aeba74744148b15099254d918671Owen Lin // Load Image 1052666ea1b28a76aeba74744148b15099254d918671Owen Lin mLoader.getBitmap(image, cb, pos); 1053666ea1b28a76aeba74744148b15099254d918671Owen Lin mRequestedMask |= (1 << col); 1054666ea1b28a76aeba74744148b15099254d918671Owen Lin retVal += 1; 1055666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1056666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1057666ea1b28a76aeba74744148b15099254d918671Owen Lin 1058666ea1b28a76aeba74744148b15099254d918671Owen Lin return retVal; 1059666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1060666ea1b28a76aeba74744148b15099254d918671Owen Lin 1061666ea1b28a76aeba74744148b15099254d918671Owen Lin // Whether this block has pending requests. 1062666ea1b28a76aeba74744148b15099254d918671Owen Lin public boolean hasPendingRequests() { 1063666ea1b28a76aeba74744148b15099254d918671Owen Lin return mRequestedMask != 0; 1064666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1065666ea1b28a76aeba74744148b15099254d918671Owen Lin 1066666ea1b28a76aeba74744148b15099254d918671Owen Lin // Called when an image is loaded. 1067666ea1b28a76aeba74744148b15099254d918671Owen Lin private void loadImageDone(IImage image, Bitmap b, 1068666ea1b28a76aeba74744148b15099254d918671Owen Lin int col) { 1069666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mBitmap == null) return; // This block has been recycled. 1070666ea1b28a76aeba74744148b15099254d918671Owen Lin 1071666ea1b28a76aeba74744148b15099254d918671Owen Lin int spacing = mSpec.mCellSpacing; 1072666ea1b28a76aeba74744148b15099254d918671Owen Lin int leftSpacing = mSpec.mLeftEdgePadding; 1073666ea1b28a76aeba74744148b15099254d918671Owen Lin final int yPos = spacing; 1074666ea1b28a76aeba74744148b15099254d918671Owen Lin final int xPos = leftSpacing 1075666ea1b28a76aeba74744148b15099254d918671Owen Lin + (col * (mSpec.mCellWidth + spacing)); 1076666ea1b28a76aeba74744148b15099254d918671Owen Lin 1077666ea1b28a76aeba74744148b15099254d918671Owen Lin drawBitmap(image, b, xPos, yPos); 1078666ea1b28a76aeba74744148b15099254d918671Owen Lin 1079666ea1b28a76aeba74744148b15099254d918671Owen Lin if (b != null) { 1080666ea1b28a76aeba74744148b15099254d918671Owen Lin b.recycle(); 1081666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1082666ea1b28a76aeba74744148b15099254d918671Owen Lin 1083666ea1b28a76aeba74744148b15099254d918671Owen Lin int mask = (1 << col); 1084666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert((mCompletedMask & mask) == 0); 1085666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert((mRequestedMask & mask) != 0); 1086666ea1b28a76aeba74744148b15099254d918671Owen Lin mRequestedMask &= ~mask; 1087666ea1b28a76aeba74744148b15099254d918671Owen Lin mCompletedMask |= mask; 1088666ea1b28a76aeba74744148b15099254d918671Owen Lin mPendingRequest--; 1089666ea1b28a76aeba74744148b15099254d918671Owen Lin 1090666ea1b28a76aeba74744148b15099254d918671Owen Lin if (isVisible()) { 1091666ea1b28a76aeba74744148b15099254d918671Owen Lin mRedrawCallback.run(); 1092666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1093666ea1b28a76aeba74744148b15099254d918671Owen Lin 1094666ea1b28a76aeba74744148b15099254d918671Owen Lin // Kick start next block loading. 1095666ea1b28a76aeba74744148b15099254d918671Owen Lin continueLoading(); 1096666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1097666ea1b28a76aeba74744148b15099254d918671Owen Lin 1098666ea1b28a76aeba74744148b15099254d918671Owen Lin // Draw the loaded bitmap to the block bitmap. 1099666ea1b28a76aeba74744148b15099254d918671Owen Lin private void drawBitmap( 1100666ea1b28a76aeba74744148b15099254d918671Owen Lin IImage image, Bitmap b, int xPos, int yPos) { 1101666ea1b28a76aeba74744148b15099254d918671Owen Lin mDrawAdapter.drawImage(mCanvas, image, b, xPos, yPos, 1102666ea1b28a76aeba74744148b15099254d918671Owen Lin mSpec.mCellWidth, mSpec.mCellHeight); 1103666ea1b28a76aeba74744148b15099254d918671Owen Lin mCanvas.drawBitmap(mOutline, xPos, yPos, null); 1104666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1105666ea1b28a76aeba74744148b15099254d918671Owen Lin 1106666ea1b28a76aeba74744148b15099254d918671Owen Lin // Draw the block bitmap to the specified canvas. 1107666ea1b28a76aeba74744148b15099254d918671Owen Lin public void doDraw(Canvas canvas, int xPos, int yPos) { 1108666ea1b28a76aeba74744148b15099254d918671Owen Lin int cols = numColumns(mRow); 1109666ea1b28a76aeba74744148b15099254d918671Owen Lin 1110666ea1b28a76aeba74744148b15099254d918671Owen Lin if (cols == mColumns) { 1111666ea1b28a76aeba74744148b15099254d918671Owen Lin canvas.drawBitmap(mBitmap, xPos, yPos, null); 1112666ea1b28a76aeba74744148b15099254d918671Owen Lin } else { 1113666ea1b28a76aeba74744148b15099254d918671Owen Lin 1114666ea1b28a76aeba74744148b15099254d918671Owen Lin // This must be the last row -- we draw only part of the block. 1115666ea1b28a76aeba74744148b15099254d918671Owen Lin // Draw the background. 1116666ea1b28a76aeba74744148b15099254d918671Owen Lin canvas.drawRect(xPos, yPos, xPos + mBlockWidth, 1117666ea1b28a76aeba74744148b15099254d918671Owen Lin yPos + mBlockHeight, mBackgroundPaint); 1118666ea1b28a76aeba74744148b15099254d918671Owen Lin // Draw part of the block. 1119666ea1b28a76aeba74744148b15099254d918671Owen Lin int w = mSpec.mLeftEdgePadding 1120666ea1b28a76aeba74744148b15099254d918671Owen Lin + cols * (mSpec.mCellWidth + mSpec.mCellSpacing); 1121666ea1b28a76aeba74744148b15099254d918671Owen Lin Rect srcRect = new Rect(0, 0, w, mBlockHeight); 1122666ea1b28a76aeba74744148b15099254d918671Owen Lin Rect dstRect = new Rect(srcRect); 1123666ea1b28a76aeba74744148b15099254d918671Owen Lin dstRect.offset(xPos, yPos); 1124666ea1b28a76aeba74744148b15099254d918671Owen Lin canvas.drawBitmap(mBitmap, srcRect, dstRect, null); 1125666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1126666ea1b28a76aeba74744148b15099254d918671Owen Lin 1127666ea1b28a76aeba74744148b15099254d918671Owen Lin // Draw the part which has not been loaded. 1128666ea1b28a76aeba74744148b15099254d918671Owen Lin int isEmpty = ((1 << cols) - 1) & ~mCompletedMask; 1129666ea1b28a76aeba74744148b15099254d918671Owen Lin 1130666ea1b28a76aeba74744148b15099254d918671Owen Lin if (isEmpty != 0) { 1131666ea1b28a76aeba74744148b15099254d918671Owen Lin int x = xPos + mSpec.mLeftEdgePadding; 1132666ea1b28a76aeba74744148b15099254d918671Owen Lin int y = yPos + mSpec.mCellSpacing; 1133666ea1b28a76aeba74744148b15099254d918671Owen Lin 1134666ea1b28a76aeba74744148b15099254d918671Owen Lin for (int i = 0; i < cols; i++) { 1135666ea1b28a76aeba74744148b15099254d918671Owen Lin if ((isEmpty & (1 << i)) != 0) { 1136666ea1b28a76aeba74744148b15099254d918671Owen Lin canvas.drawBitmap(mEmptyBitmap, x, y, null); 1137666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1138666ea1b28a76aeba74744148b15099254d918671Owen Lin x += (mSpec.mCellWidth + mSpec.mCellSpacing); 1139666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1140666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1141666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1142666ea1b28a76aeba74744148b15099254d918671Owen Lin 1143666ea1b28a76aeba74744148b15099254d918671Owen Lin // Mark a request as cancelled. The request has already been removed 1144666ea1b28a76aeba74744148b15099254d918671Owen Lin // from the queue of ImageLoader, so we only need to mark the fact. 1145666ea1b28a76aeba74744148b15099254d918671Owen Lin public void cancelRequest(int col) { 1146666ea1b28a76aeba74744148b15099254d918671Owen Lin int mask = (1 << col); 1147666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert((mRequestedMask & mask) != 0); 1148666ea1b28a76aeba74744148b15099254d918671Owen Lin mRequestedMask &= ~mask; 1149666ea1b28a76aeba74744148b15099254d918671Owen Lin mPendingRequest--; 1150666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1151666ea1b28a76aeba74744148b15099254d918671Owen Lin 1152666ea1b28a76aeba74744148b15099254d918671Owen Lin // Try to cancel all pending requests for this block. After this 1153666ea1b28a76aeba74744148b15099254d918671Owen Lin // completes there could still be requests not cancelled (because it is 1154666ea1b28a76aeba74744148b15099254d918671Owen Lin // already in progress). We deal with that situation by setting mBitmap 1155666ea1b28a76aeba74744148b15099254d918671Owen Lin // to null in recycle() and check this in loadImageDone(). 1156666ea1b28a76aeba74744148b15099254d918671Owen Lin private void cancelAllRequests() { 1157666ea1b28a76aeba74744148b15099254d918671Owen Lin for (int i = 0; i < mColumns; i++) { 1158666ea1b28a76aeba74744148b15099254d918671Owen Lin int mask = (1 << i); 1159666ea1b28a76aeba74744148b15099254d918671Owen Lin if ((mRequestedMask & mask) != 0) { 1160666ea1b28a76aeba74744148b15099254d918671Owen Lin int pos = (mRow * mColumns) + i; 1161666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mLoader.cancel(mImageList.getImageAt(pos))) { 1162666ea1b28a76aeba74744148b15099254d918671Owen Lin mRequestedMask &= ~mask; 1163666ea1b28a76aeba74744148b15099254d918671Owen Lin mPendingRequest--; 1164666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1165666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1166666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1167666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1168666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1169666ea1b28a76aeba74744148b15099254d918671Owen Lin} 1170