2013-04-05 67 views
3

有一個由位圖填充的gridView,該位圖在位圖異步加載時生成動畫。 有時,拋出gridView某些項目無法正確呈現。動畫將觸發,但位圖將不會顯示。帶有動畫的列表視圖項無法正確呈現

我已經確認位圖確實存在(至少是數據),但它只是不呈現。

快速拋出gridView時會發生這種情況,但也發生在慢速滾動上。 看來回收再利用工作不正常。 這裏是我的代碼:

的ListView適配器:

@Override 
    public View getView(int position, View convertView, ViewGroup parent) 
    { 
     if(convertView == null){ 
      convertView = new FlipAnimatedCacheableImage(mContext); 
     } 
     final ImageInfo info = mapItem(getItem(position)); 
     FlipAnimatedCacheableImage image = (FlipAnimatedCacheableImage)convertView; 
     image.resetState(); 
     image.setTitle(info.title); 
     image.setSubTitle(info.subTitle); 
     image.loadImage(info.imgURL, false); 
     return convertView; 
    } 

代碼FlipAnimatedCacheableImage:

public class FlipAnimatedCacheableImage extends FrameLayout 
{ 
    private static final String TAG = FlipAnimatedCacheableImage.class.getCanonicalName(); 
    protected static final long DURATION = 300; 
    private ImageView mPlaceHolder; 
    private NetworkedCacheableImageView mCacheableImage; 
    private View mTextContainer; 
    private TextView mTitleTv; 
    private TextView mSubTitleTv; 
    private View mProgress; 

    private ImageLoadListener mListener = new ImageLoadListener() 
    { 

     private boolean isShown; 


     @Override 
     public void onImageLoaded(boolean animate) 
     { 
      if(animate){ 
       mProgress.setVisibility(View.GONE); 
       mPlaceHolder.setVisibility(View.VISIBLE); 
       mPlaceHolder.setRotationY(0); 
       mCacheableImage.setVisibility(View.VISIBLE); 
       mCacheableImage.setRotationY(-90); 
       mPlaceHolder.animate().rotationY(90).setDuration(DURATION).start(); 
       mCacheableImage.animate().rotationY(0).setDuration(DURATION).setStartDelay(DURATION).start(); 

       if(!TextUtils.isEmpty(mTitleTv.getText()) || !TextUtils.isEmpty(mSubTitleTv.getText())){ 
        mTextContainer.setVisibility(View.VISIBLE); 
        mTextContainer.setAlpha(0); 
        mTextContainer.animate().alpha(1).setDuration(DURATION).setStartDelay(DURATION * 2).start(); 
       } 
       else{ 
        mTextContainer.setVisibility(View.GONE); 
       } 
       isShown = true; 
       FlipAnimatedCacheableImage.this.invalidate(); 
      } 
      else{ 
       mPlaceHolder.setVisibility(View.GONE); 
       mProgress.setVisibility(View.GONE); 
       mCacheableImage.setVisibility(View.VISIBLE); 
       mCacheableImage.setRotationY(0); 
       mCacheableImage.clearAnimation(); 

       if(!TextUtils.isEmpty(mTitleTv.getText()) || !TextUtils.isEmpty(mSubTitleTv.getText())){ 
        mTextContainer.setVisibility(View.VISIBLE); 
        mTextContainer.setAlpha(1); 
       } 
       else{ 
        mTextContainer.setVisibility(View.GONE); 
       } 

       FlipAnimatedCacheableImage.this.invalidate(); 
      } 
     } 
    }; 


    public FlipAnimatedCacheableImage(Context context, boolean isLarge) 
    { 
     this(context, null, 0, isLarge); 
    } 

    public FlipAnimatedCacheableImage(Context context) 
    { 
     this(context, null); 
    } 

    public FlipAnimatedCacheableImage(Context context, AttributeSet attrs) 
    { 
     this(context, attrs, 0, false); 
    } 

    public FlipAnimatedCacheableImage(Context context, AttributeSet attrs, int defStyle, boolean isLarge) 
    { 
     super(context, attrs, defStyle); 
     LayoutInflater inflater = LayoutInflater.from(context); 

     inflater.inflate(R.layout.item_image_thumbnail, this); 

     mCacheableImage = (NetworkedCacheableImageView)this.findViewById(R.id.image_view); 
     mPlaceHolder = (ImageView)this.findViewById(R.id.place_holder); 
     mProgress = this.findViewById(R.id.progressBar); 

     // only used by small images 
     mTextContainer = this.findViewById(R.id.text_container); 
     mTitleTv = (TextView)this.findViewById(R.id.text_title); 
     mSubTitleTv = (TextView)this.findViewById(R.id.text_sub_title); 

     // listener to animate after loading 
     mCacheableImage.setLoadListener(mListener); 

     // set default state 
     mTextContainer.setVisibility(View.GONE); 
     mTitleTv.setVisibility(GONE); 
     mSubTitleTv.setVisibility(GONE); 
     if(isLarge){ 
      // adjust the size to the correct dimensions, ignore titleAnd subTitle 
      FrameLayout.LayoutParams imageParams = new FrameLayout.LayoutParams((int)context.getResources() 
       .getDimension(R.dimen.grid_image_width_large), (int)context.getResources().getDimension(
       R.dimen.grid_image_height_large)); 
      findViewById(R.id.content_wrapper).setLayoutParams(imageParams); 
     } 
     // this.setOnClickListener(listener); 
     resetState(); 

    } 

    public void resetState() 
    { 
     mCacheableImage.setVisibility(View.VISIBLE); 
     mCacheableImage.setRotationY(0); 
     mProgress.setVisibility(View.VISIBLE); 
     mPlaceHolder.setVisibility(View.VISIBLE); 
     mTextContainer.setVisibility(View.GONE); 
     mTitleTv.setVisibility(GONE); 
     mSubTitleTv.setVisibility(GONE); 
    } 


    public boolean loadImage(String url, boolean fullSize) 
    { 
     return mCacheableImage.loadImage(url, fullSize); 
    } 

    public void setTitle(String title) 
    { 
     mTitleTv.setText(title); 
     if(!TextUtils.isEmpty(title)){ 
      mTitleTv.setVisibility(View.VISIBLE); 
     } 
     else{ 
      mTitleTv.setVisibility(View.GONE); 

     } 
    } 

    public void setSubTitle(String subTitle) 
    { 
     mSubTitleTv.setText(subTitle); 
     if(!TextUtils.isEmpty(subTitle)){ 
      mSubTitleTv.setVisibility(View.VISIBLE); 
     } 
     else{ 
      mSubTitleTv.setVisibility(View.GONE); 
     } 
    } 

} 

FlipAnimatedCache將與由克里斯·巴內斯這裏https://github.com/chrisbanes/Android-BitmapCache

這裏寫高速緩存請求圖像是代碼:

/******************************************************************************* 
* Copyright 2011, 2013 Chris Banes. 
* 
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
* http://www.apache.org/licenses/LICENSE-2.0 
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed under the License is distributed on an "AS IS" BASIS, 
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License. 
*******************************************************************************/ 


/** 
* Simple extension of CacheableImageView which allows downloading of Images of 
* the Internet. 
* 
* This code isn't production quality, but works well enough for this sample.s 
* 
* @author Chris Banes 
* 
*/ 
public class NetworkedCacheableImageView extends CacheableImageView 
{ 
    private static final String TAG = NetworkedCacheableImageView.class.getCanonicalName(); 


    public interface ImageLoadListener 
    { 
     public void onImageLoaded(boolean animate); 
    } 

    /** 
    * This task simply fetches an Bitmap from the specified URL and wraps it in 
    * a wrapper. This implementation is NOT 'best practice' or production ready 
    * code. 
    */ 
    private static class ImageUrlAsyncTask extends AsyncTask<String, Void, CacheableBitmapDrawable> 
    { 

     private final BitmapLruCache mCache; 
     private final WeakReference<ImageView> mImageViewRef; 
     private final WeakReference<NetworkedCacheableImageView> viewRef; 
     private final BitmapFactory.Options mDecodeOpts; 

     private final ImageLoadListener mLoadListener; 
     private boolean outOfMemoryFailure; 
     private String mURL; 


     ImageUrlAsyncTask(ImageView imageView, BitmapLruCache cache, BitmapFactory.Options decodeOpts, 
      ImageLoadListener listener, NetworkedCacheableImageView view) 
     { 
      mCache = cache; 
      mLoadListener = listener; 
      mImageViewRef = new WeakReference<ImageView>(imageView); 
      viewRef = new WeakReference<NetworkedCacheableImageView>(view); 
      mDecodeOpts = decodeOpts; 

     } 

     @Override 
     protected CacheableBitmapDrawable doInBackground(String... params) 
     { 
      try{ 
       // Return early if the ImageView has disappeared. 
       if(null == mImageViewRef.get()){ 
        return null; 
       } 

       mURL = params[0]; 

       // Now we're not on the main thread we can check all caches 
       CacheableBitmapDrawable result = mCache.get(mURL, mDecodeOpts); 

       if(null == result){ 
        Log.w("CACHE", "Downloading: " + mURL); 

        // The bitmap isn't cached so download from the web 
        HttpURLConnection conn = (HttpURLConnection)new URL(mURL).openConnection(); 
        InputStream is = new BufferedInputStream(conn.getInputStream()); 

        // Add to cache 
        result = mCache.put(mURL, is, mDecodeOpts); 
       } 
       else{ 
        Log.w("CACHE", "Got from Disk Cache: " + mURL); 
       } 

       return result; 

      } 
      catch(IOException e){ 
       Log.e("Error downloading image", e.toString()); 
      } 
      catch(OutOfMemoryError e){ 
       Log.e(TAG, "running out of memory, trimming image memory cache"); 
       outOfMemoryFailure = true; 
      } 

      return null; 
     } 

     @Override 
     protected void onPostExecute(final CacheableBitmapDrawable result) 
     { 
      // super.onPostExecute(result); 
      if(outOfMemoryFailure || result == null || !result.hasValidBitmap()){ 
       mCache.trimMemory(); 
       // viewRef.get().loadImageAsync(mURL, mDecodeOpts); 
       Log.e(TAG, "image probably did not load::" + mURL); 
      } 
      else{ 
       if(BuildConfig.DEBUG){ 
        Log.d("bitmapCache", "NetworkedCacheableImageView.ImageUrlAsyncTask.onPostExecute() WIDTH:" 
         + result.getBitmap().getWidth()); 
        Log.d("bitmapCache", "NetworkedCacheableImageView.ImageUrlAsyncTask.onPostExecute() HEIGHT:" 
         + result.getBitmap().getHeight()); 

       } 
       Log.i(TAG, "RESULT for :" + mURL + " mImageViewRef::" + mImageViewRef + " outOfMemoryFailure::" 
        + outOfMemoryFailure); 
       if(result != null){ 
        Log.i(TAG, "RESULT object for :" + mURL + " result:hasValidBitmap" + result.hasValidBitmap() 
         + " result:isReferencedByCache" + result.isReferencedByCache() + " result.isBeingDisplayed() " 
         + result.isBeingDisplayed()); 
       } 

       Runnable runnable = new Runnable() 
       { 

        @Override 
        public void run() 
        { 
         if(mImageViewRef != null){ 
          final ImageView iv = mImageViewRef.get(); 
          Log.e(TAG, "RESULT image view reference :" + mURL + " view ref::" + iv); 

          if(null != iv){ 
           iv.setImageDrawable(result); 
           if(mLoadListener != null){ 
            mLoadListener.onImageLoaded(true); 
           } 
          } 

         } 

        } 
       }; 

       Handler handler = new Handler(); 
       handler.postDelayed(runnable, 50); 

      } 
      super.onPostExecute(result); 
     } 
    } 


    private final BitmapLruCache mCache; 
    private ImageUrlAsyncTask mCurrentTask; 
    private ImageLoadListener mListener; 


    public NetworkedCacheableImageView(Context context, AttributeSet attrs) 
    { 
     super(context, attrs); 
     mCache = WatchApplication.getBitmapCache(); 

    } 

    public void setLoadListener(ImageLoadListener listener) 
    { 
     mListener = listener; 
    } 

    public void removeListener() 
    { 
     mListener = null; 
    } 

    /** 
    * Loads the Bitmap. 
    * 
    * @param url 
    *  - URL of image 
    * @param fullSize 
    *  - Whether the image should be kept at the original size 
    * @return true if the bitmap was found in the cache 
    */ 
    public boolean loadImage(String url, final boolean fullSize) 
    { 
     setImageDrawable(null); 
     // First check whether there's already a task running, if so cancel it 
     if(TextUtils.isEmpty(url)) 
      return false; 
     if(null != mCurrentTask){ 
      mCurrentTask.cancel(false); 
     } 

     // Check to see if the memory cache already has the bitmap. We can 
     // safely do 
     // this on the main thread. 
     BitmapDrawable wrapper = mCache.getFromMemoryCache(url); 

     if(null != wrapper){ 
      // The cache has it, so just display it 
      if(BuildConfig.DEBUG){ 
       Log.w(TAG, "CACHE. FOUND IN MEMORY:" + url); 
      } 
      setImageDrawable(wrapper); 
      if(mListener != null){ 
       mListener.onImageLoaded(false); 
      } 
      return true; 
     } 
     else{ 
      // Memory Cache doesn't have the URL, do threaded request... 

      BitmapFactory.Options decodeOpts = null; 

      if(!fullSize){ 
       decodeOpts = new BitmapFactory.Options(); 
       // decodeOpts.inDensity = DisplayMetrics.DENSITY_XHIGH; 
       decodeOpts.inPurgeable = true; 
       decodeOpts.outHeight = this.getHeight(); 
       decodeOpts.outWidth = this.getWidth(); 
      } 

      loadImageAsync(url, decodeOpts); 

      return false; 
     } 
    } 

    public void loadImageAsync(String url, BitmapFactory.Options decodeOpts) 
    { 
     mCurrentTask = new ImageUrlAsyncTask(this, mCache, decodeOpts, mListener, this); 

     if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){ 
      SDK11.executeOnThreadPool(mCurrentTask, url); 
     } 
     else{ 
      mCurrentTask.execute(url); 
     } 
    } 

} 

在此先感謝您的幫助!

回答

0

兩個值得嘗試的事情 -

對於鏈動畫操作使用監聽器:

mPlaceHolder.animate() 
    .alpha(0f) 
    .scaleX(0.9f) 
    .scaleY(0.9f) 
    .rotationY(90) 
    .setDuration(DURATION) 
    .setListener(new AnimatorListenerAdapter() { 
       @Override 
       public void onAnimationEnd(Animator animation) { 
        mCacheableImage.setImageDrawable(drawable); 
        mCacheableImage.animate() 
          .alpha(1f) 
          .scaleY(1f) 
          .scaleX(1f) 
          .rotationY(0) 
          .setDuration(DURATION) 
          .setListener(null); 
       } 
    }); 

使用setHasTransientState(),保證意見沒有在ListView/GridView的回收。有關更多信息,請參閱此DevByte video