1111import android .os .Message ;
1212import android .renderscript .RSRuntimeException ;
1313import android .support .annotation .Nullable ;
14+ import android .support .v4 .util .LruCache ;
1415import android .support .v4 .view .ViewPager ;
1516import android .util .AttributeSet ;
17+ import android .util .Log ;
1618import android .view .ViewGroup ;
1719import android .widget .FrameLayout ;
1820import android .widget .ImageView ;
2426import moe .codeest .ecardflow .mode .AnimMode ;
2527import moe .codeest .ecardflow .mode .BlurAnimMode ;
2628import moe .codeest .ecardflow .provider .ImageProvider ;
29+ import moe .codeest .ecardflow .support .RecyclingBitmapDrawable ;
2730import moe .codeest .ecardflow .util .FastBlur ;
2831import moe .codeest .ecardflow .util .RSBlur ;
2932
3336
3437public class ECardFlowLayout extends FrameLayout {
3538
36- private static final int SWITCH_ANIM_TIME = 300 ;
39+ private static final int SWITCH_ANIM_TIME = 300 ; //300 ms
3740 private static final int MSG_JUDGE_RESET = 0x1 ;
3841
3942 private Context mContext ;
4043 private ExecutorService mThreadPool ;
44+ private LruCache <String , RecyclingBitmapDrawable > mLruCache ;
4145 private MyHandler mHandler ;
4246 private NotifyRunnable mNotifyRunnable ;
4347
@@ -48,11 +52,12 @@ public class ECardFlowLayout extends FrameLayout{
4852 private ImageProvider mProvider ;
4953 private AnimMode mAnimMode ;
5054
51- private Bitmap curBp , lastBp , nextBp ;
55+ private RecyclingBitmapDrawable curBp , lastBp , nextBp ;
5256 private Bitmap curBlurBp , lastBlurBp , nextBlurBp ;
5357
5458 private int mCurDirection = AnimMode .D_RIGHT ;
5559 private int mSwitchAnimTime = SWITCH_ANIM_TIME ;
60+ private int mMinCacheSize ;
5661 private float mLastOffset ;
5762 private int mRadius ;
5863 private int mCurPosition ;
@@ -84,6 +89,7 @@ private void init() {
8489 mThreadPool = Executors .newCachedThreadPool ();
8590 mNotifyRunnable = new NotifyRunnable ();
8691 mHandler = new MyHandler (this );
92+ initCache ();
8793 mBlurImage = new ImageView (mContext );
8894 initImageView (mBlurImage );
8995 mBgImage = new ImageView (mContext );
@@ -142,9 +148,38 @@ public void onPageScrollStateChanged(int state) {
142148 });
143149 }
144150
151+ private void initCache () {
152+ int maxMemory = (int ) Runtime .getRuntime ().maxMemory () / 1024 ;
153+ int cacheSize = maxMemory / 5 ;
154+ mMinCacheSize = maxMemory / 8 ;
155+ mLruCache = new LruCache <String , RecyclingBitmapDrawable >(cacheSize ) {
156+ @ Override
157+ protected int sizeOf (String key , RecyclingBitmapDrawable value ) {
158+ return value .getBitmap ().getByteCount () / 1024 ;
159+ }
160+
161+ @ Override
162+ protected void entryRemoved (boolean evicted , String key , RecyclingBitmapDrawable oldValue , RecyclingBitmapDrawable newValue ) {
163+ super .entryRemoved (evicted , key , oldValue , newValue );
164+ if (evicted && oldValue != null ) {
165+ oldValue .setIsCached (false );
166+ }
167+ }
168+
169+ @ Override
170+ protected RecyclingBitmapDrawable create (String key ) {
171+ RecyclingBitmapDrawable bitmap = new RecyclingBitmapDrawable (getResources (), mProvider .onProvider (Integer .valueOf (key )));
172+ if (bitmap .getBitmap () == null )
173+ return null ;
174+ bitmap .setIsCached (true );
175+ return bitmap ;
176+ }
177+ };
178+ }
179+
145180 private void updateNextRes (final int position ) {
146181 mCurPosition = position ;
147- recycleBitmap (lastBp );
182+ detachBitmap (lastBp );
148183 lastBp = curBp ;
149184 curBp = nextBp ;
150185 if (mBlurImage != null ) {
@@ -156,9 +191,9 @@ private void updateNextRes(final int position) {
156191 mThreadPool .execute (new Runnable () {
157192 @ Override
158193 public void run () {
159- nextBp = mProvider . onProvider (position + 1 );
194+ nextBp = loadBitmap (position + 1 );
160195 if (mBlurImage != null ) {
161- nextBlurBp = blurBitmap (mProvider . onProvider ( position + 1 ) );
196+ nextBlurBp = blurBitmap (position + 1 );
162197 }
163198 sendMsg ();
164199 }
@@ -170,7 +205,7 @@ public void run() {
170205
171206 private void updateLastRes (final int position ) {
172207 mCurPosition = position ;
173- recycleBitmap (nextBp );
208+ detachBitmap (nextBp );
174209 nextBp = curBp ;
175210 curBp = lastBp ;
176211 if (mBlurImage != null ) {
@@ -182,9 +217,9 @@ private void updateLastRes(final int position) {
182217 mThreadPool .execute (new Runnable () {
183218 @ Override
184219 public void run () {
185- lastBp = mProvider . onProvider (position - 1 );
220+ lastBp = loadBitmap (position - 1 );
186221 if (mBlurImage != null ) {
187- lastBlurBp = blurBitmap (mProvider . onProvider ( position - 1 ) );
222+ lastBlurBp = blurBitmap (position - 1 );
188223 }
189224 sendMsg ();
190225 }
@@ -194,11 +229,10 @@ public void run() {
194229 }
195230 }
196231
197- private void startTrans (int targetPosition , ImageView targetImage , Bitmap startBp , Bitmap endBp ) {
232+ private void startTrans (int targetPosition , ImageView targetImage , RecyclingBitmapDrawable startBp , RecyclingBitmapDrawable endBp ) {
198233 if (endBp == null )
199- endBp = mProvider .onProvider (targetPosition );
200- TransitionDrawable td = new TransitionDrawable (new Drawable [] {new BitmapDrawable (mContext .getResources (), startBp ),
201- new BitmapDrawable (mContext .getResources (), endBp )});
234+ endBp = loadBitmap (targetPosition );
235+ TransitionDrawable td = new TransitionDrawable (new Drawable [] {startBp , endBp });
202236 targetImage .setImageDrawable (td );
203237 td .setCrossFadeEnabled (true );
204238 td .startTransition (mSwitchAnimTime );
@@ -211,7 +245,7 @@ private void switchBgToNext(final int targetPosition) {
211245 isSwitching = true ;
212246 startTrans (targetPosition + 1 , mBgImage , curBp , nextBp );
213247 if (mBlurImage != null ) {
214- startTrans (targetPosition + 1 , mBlurImage , curBlurBp , nextBlurBp );
248+ startTrans (targetPosition + 1 , mBlurImage , new RecyclingBitmapDrawable ( getResources (), curBlurBp ), new RecyclingBitmapDrawable ( getResources (), nextBlurBp ) );
215249 }
216250 mNotifyRunnable .setTarget (targetPosition , true );
217251 mBgImage .postDelayed (mNotifyRunnable , mSwitchAnimTime );
@@ -224,7 +258,7 @@ private void switchBgToLast(final int targetPosition) {
224258 isSwitching = true ;
225259 startTrans (targetPosition - 1 , mBgImage , curBp , lastBp );
226260 if (mBlurImage != null ) {
227- startTrans (targetPosition - 1 , mBlurImage , curBlurBp , lastBlurBp );
261+ startTrans (targetPosition - 1 , mBlurImage , new RecyclingBitmapDrawable ( getResources (), curBlurBp ), new RecyclingBitmapDrawable ( getResources (), lastBlurBp ) );
228262 }
229263 mNotifyRunnable .setTarget (targetPosition , false );
230264 mBgImage .postDelayed (mNotifyRunnable , mSwitchAnimTime );
@@ -236,15 +270,14 @@ private void jumpBgToTarget(final int targetPosition) {
236270 return ;
237271 }
238272 isSwitching = true ;
239- final Bitmap newBitmap = mProvider .onProvider (targetPosition );
240- TransitionDrawable td = new TransitionDrawable (new Drawable [] {new BitmapDrawable (mContext .getResources (), curBp ),
241- new BitmapDrawable (mContext .getResources (), newBitmap )});
273+ final RecyclingBitmapDrawable newBitmap = loadBitmap (targetPosition );
274+ TransitionDrawable td = new TransitionDrawable (new Drawable [] {curBp , newBitmap });
242275 mBgImage .setImageDrawable (td );
243276 td .setCrossFadeEnabled (true );
244277 td .startTransition (mSwitchAnimTime );
245278 if (mBlurImage != null ) {
246279 TransitionDrawable tdb = new TransitionDrawable (new Drawable [] {new BitmapDrawable (mContext .getResources (), curBlurBp ),
247- new BitmapDrawable (mContext .getResources (), blurBitmap (mProvider . onProvider ( targetPosition ) ))});
280+ new BitmapDrawable (mContext .getResources (), blurBitmap (targetPosition ))});
248281 mBlurImage .setImageDrawable (tdb );
249282 tdb .setCrossFadeEnabled (true );
250283 tdb .startTransition (mSwitchAnimTime );
@@ -255,19 +288,19 @@ public void run() {
255288 mThreadPool .execute (new Runnable () {
256289 @ Override
257290 public void run () {
258- recycleBitmap (nextBp );
259- recycleBitmap (lastBp );
260- recycleBitmap (curBp );
291+ detachBitmap (nextBp );
292+ detachBitmap (lastBp );
293+ detachBitmap (curBp );
261294 curBp = newBitmap ;
262- nextBp = mProvider . onProvider (targetPosition + 1 );
263- lastBp = mProvider . onProvider (targetPosition - 1 );
295+ nextBp = loadBitmap (targetPosition + 1 );
296+ lastBp = loadBitmap (targetPosition - 1 );
264297 if (mBlurImage != null ) {
265298 recycleBitmap (nextBlurBp );
266299 recycleBitmap (lastBlurBp );
267300 recycleBitmap (curBlurBp );
268- curBlurBp = blurBitmap (mProvider . onProvider ( targetPosition ) );
269- nextBlurBp = blurBitmap (mProvider . onProvider ( targetPosition + 1 ) );
270- lastBlurBp = blurBitmap (mProvider . onProvider ( targetPosition - 1 ) );
301+ curBlurBp = blurBitmap (targetPosition );
302+ nextBlurBp = blurBitmap (targetPosition + 1 );
303+ lastBlurBp = blurBitmap (targetPosition - 1 );
271304 }
272305 sendMsg ();
273306 }
@@ -298,41 +331,56 @@ private void judgeReset() {
298331 }
299332
300333 @ Nullable
301- private Bitmap blurBitmap (Bitmap bitmap ) {
302- if (bitmap == null ) {
334+ private Bitmap blurBitmap (int targetPosition ) {
335+ RecyclingBitmapDrawable bitmapDrawable = mLruCache .get (String .valueOf (targetPosition ));
336+ if (bitmapDrawable == null || bitmapDrawable .getBitmap () == null ) {
303337 return null ;
304338 }
305- Bitmap blurBitmap ;
339+ Bitmap blurBitmap = bitmapDrawable . getBitmap (). copy ( Bitmap . Config . ARGB_8888 , true ) ;
306340 if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .JELLY_BEAN_MR2 ) {
307341 try {
308- blurBitmap = RSBlur .blur (mContext , bitmap , mRadius );
342+ blurBitmap = RSBlur .blur (mContext , blurBitmap , mRadius );
309343 } catch (RSRuntimeException e ) {
310- blurBitmap = FastBlur .blur (bitmap , mRadius , true );
344+ blurBitmap = FastBlur .blur (blurBitmap , mRadius , true );
311345 }
312346 } else {
313- blurBitmap = FastBlur .blur (bitmap , mRadius , true );
347+ blurBitmap = FastBlur .blur (blurBitmap , mRadius , true );
314348 }
315349 return blurBitmap ;
316350 }
317351
352+ private RecyclingBitmapDrawable loadBitmap (int targetPosition ) {
353+ RecyclingBitmapDrawable bitmap = mLruCache .get (String .valueOf (targetPosition ));
354+ if (bitmap != null ) {
355+ bitmap .setIsDisplayed (true );
356+ }
357+ return bitmap ;
358+ }
359+
318360 private void recycleBitmap (Bitmap bitmap ) {
319361 if (bitmap != null )
320362 bitmap .recycle ();
321363 }
322364
365+ private void detachBitmap (RecyclingBitmapDrawable bitmap ) {
366+ if (bitmap != null ) {
367+ bitmap .setIsDisplayed (false );
368+ }
369+ }
370+
323371 public void setImageProvider (ImageProvider provider ) {
324372 mProvider = provider ;
325- curBp = mProvider . onProvider (0 );
326- nextBp = mProvider . onProvider (1 );
373+ curBp = loadBitmap (0 );
374+ nextBp = loadBitmap (1 );
327375 if (mAnimMode == null ) {
328376 throw new RuntimeException ("You should setAnimMode before setImageProvider" );
329377 }
330378 if (mBlurImage != null ) {
331- curBlurBp = blurBitmap (mProvider . onProvider ( 0 ) );
332- nextBlurBp = blurBitmap (mProvider . onProvider ( 1 ) );
333- mBlurImage .setImageBitmap (blurBitmap (curBlurBp ));
379+ curBlurBp = blurBitmap (0 );
380+ nextBlurBp = blurBitmap (1 );
381+ mBlurImage .setImageBitmap (blurBitmap (0 ));
334382 }
335- mBgImage .setImageBitmap (curBp );
383+ mBgImage .setImageBitmap (curBp . getBitmap () );
336384 }
337385
338386 public void setAnimMode (AnimMode animMode ) {
@@ -349,15 +397,24 @@ public void setSwitchAnimTime(int switchAnimTime) {
349397 mSwitchAnimTime = switchAnimTime ;
350398 }
351399
400+ public void setCacheSize (int megabytes ) {
401+ if (megabytes * 1024 >= mMinCacheSize ) {
402+ mLruCache .resize (megabytes * 1024 );
403+ } else {
404+ Log .w (getClass ().getName (), "Size is too small to resize" );
405+ }
406+ }
407+
352408 public void onDestroy () {
353409 mHandler .removeCallbacksAndMessages (null );
354410 if (!mThreadPool .isShutdown ()) {
355411 mThreadPool .shutdown ();
356412 }
357413 mHandler = null ;
358- recycleBitmap (curBp );
359- recycleBitmap (lastBp );
360- recycleBitmap (nextBp );
414+ detachBitmap (curBp );
415+ detachBitmap (lastBp );
416+ detachBitmap (nextBp );
417+ mLruCache .evictAll ();
361418 if (mBlurImage != null ) {
362419 recycleBitmap (curBlurBp );
363420 recycleBitmap (lastBlurBp );
0 commit comments