带有动画的Listview项目无法正确呈现

时间:2013-04-05 17:10:07

标签: android android-listview android-animation

有一个由位图填充的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将从Chris Banes在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);
        }
    }

}

提前感谢您的帮助!

1 个答案:

答案 0 :(得分: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