有一个由位图填充的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);
}
}
}
提前感谢您的帮助!
答案 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。