我的listview工作得非常好。当我快速向上或向下滚动时,它每次显示相同的3-4张图片,然后慢慢变成右图像。
检索我认为需要修复的位图的代码
public Bitmap retrieveBitmap(String url) throws Exception {
InputStream inputStream = null;
try {
inputStream = this.retrieveStream(url);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
final Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options);
return bitmap;
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
在我添加选项样本大小之前,它一直在我滚动时崩溃,我只是将其复制到另一个堆栈问题但不确定它究竟是做什么,除了以某种方式减小大小。
适配器类
package com.example.jdmb;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class MoviesAdapter extends ArrayAdapter<Movies> {
private ArrayList<Movies> movieData;
private Activity context;
public MoviesAdapter(Context context, int resource,
ArrayList<Movies> objects) {
super(context, resource, objects);
this.context = (Activity) context;
this.movieData = objects;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
ViewHolder holder;
if (view == null) {
LayoutInflater vi = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = vi.inflate(R.layout.list_row, parent, false);
holder = new ViewHolder();
holder.id = (TextView) view.findViewById(R.id.movieId);
holder.title = (TextView) view.findViewById(R.id.movieTitle);
holder.vote_avg = (TextView) view.findViewById(R.id.movieAvg);
holder.backdrop_path = (ImageView) view
.findViewById(R.id.movieBackdrop);
holder.release_date = (TextView) view
.findViewById(R.id.movieRelease);
holder.original_title = (TextView) view
.findViewById(R.id.movieTitle2);
holder.vote_count = (TextView) view.findViewById(R.id.movieCount);
holder.adult = (TextView) view.findViewById(R.id.movieAdult);
holder.poster = (ImageView) view.findViewById(R.id.moviePoster);
holder.popularity = (TextView) view
.findViewById(R.id.moviePopularity);
view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
}
Movies movie = movieData.get(position);
if (movie != null) {
// check to see if each individual textview is null.
// if not, assign some text!
holder.id.setText("id: " + movie.getId());
holder.title.setText("title: " + movie.getTitle());
holder.vote_avg.setText("vote_avg: " + movie.getVote_average());
DownloadBitmap.downloadBitmap(holder.backdrop_path,
movie.getBackdrop_path());
holder.release_date.setText("release_date: "
+ movie.getRelease_date());
holder.original_title.setText("original_title: "
+ movie.getOriginal_title());
holder.vote_count.setText("vote_count: " + movie.getVote_count());
holder.adult.setText("adult: " + movie.isAdult());
DownloadBitmap
.downloadBitmap(holder.poster, movie.getPoster_path());
holder.popularity.setText("popularity: " + movie.getPopularity());
}
return view;
}
private static class ViewHolder {
TextView id;
TextView title;
TextView vote_avg;
ImageView backdrop_path;
TextView release_date;
TextView original_title;
TextView vote_count;
TextView adult;
ImageView poster;
TextView popularity;
}
}
下载课程
package com.example.jdmb;
import java.io.InputStream;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.os.AsyncTask;
import android.widget.ImageView;
public class DownloadBitmap {
static Bitmap image;
public static void downloadBitmap(final ImageView imageView,
final String url) {
new AsyncTask<Bitmap, Void, Bitmap>() {
@Override
protected Bitmap doInBackground(Bitmap... params) {
HttpRetriever retriever = new HttpRetriever();
try {
image = retriever.retrieveBitmap(url);
} catch (Exception e) {
}
return image;
}
@Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
if (imageView != null) {
imageView.setImageBitmap(result);
}
}
}.execute();
}
}
我的代码的哪一部分让我在滚动时看到相同的照片,我认为它与回收图像有关,但不知道如何通过它。
答案 0 :(得分:2)
回收的ImageView会显示您告诉它的最后一个位图。您需要设置holder.backdrop_path&amp;在返回视图之前或在AsyncTask的onPreExecute之前将holder.poster发送到通用资源。
像这样:
package com.example.jdmb;
import java.io.InputStream;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.widget.ImageView;
public class DownloadBitmap {
static Bitmap image;
public static void downloadBitmap(final ImageView imageView,
final String url) {
new AsyncTask<Bitmap, Void, Bitmap>() {
protected void onPreExecute(){
//This could also be an internal resource instead of null
imageView.setImageBitmap(null);
}
@Override
protected Bitmap doInBackground(Bitmap... params) {
HttpRetriever retriever = new HttpRetriever();
InputStream is = retriever.retrieveStream(url);
image = BitmapFactory.decodeStream(is);
return image;
}
@Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
imageView.setImageBitmap(result);
}
}.execute();
}
}
我会建议一种更强大的方法来完成这项任务。此解决方案将要求您使用Volley Library,并且大多数代码都可以在Google IO source中找到。如果有帮助的话,Here是一个涵盖相同主题的视频。
下载Volley并将其添加到您的构建路径中。 我们将使用Volley ImageLoader的Google IO超类。
*
* Copyright 2012 Google Inc.
*
* 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.
*/
package com.google.android.apps.iosched.util;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
import android.net.http.AndroidHttpClient;
import android.os.Build;
import android.os.Environment;
import android.support.v4.app.FragmentActivity;
import android.widget.ImageView;
import com.android.volley.Cache;
import com.android.volley.Network;
import com.android.volley.RequestQueue;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.BasicNetwork;
import com.android.volley.toolbox.DiskBasedCache;
import com.android.volley.toolbox.HttpClientStack;
import com.android.volley.toolbox.HurlStack;
import java.io.File;
import java.util.ArrayList;
/**
* A class that wraps up remote image loading requests using the Volley library combined with a
* memory cache. An single instance of this class should be created once when your Activity or
* Fragment is created, then use {@link #get(String, android.widget.ImageView)} or one of
* the variations to queue the image to be fetched and loaded from the network. Loading images
* in a {@link android.widget.ListView} or {@link android.widget.GridView} is also supported but
* you must store the {@link com.android.volley.Request} in your ViewHolder type class and pass it
* into loadImage to ensure the request is canceled as views are recycled.
*/
public class ImageLoader extends com.android.volley.toolbox.ImageLoader {
private static final ColorDrawable transparentDrawable = new ColorDrawable(
android.R.color.transparent);
private static final int HALF_FADE_IN_TIME = UIUtils.ANIMATION_FADE_IN_TIME / 2;
private static final String CACHE_DIR = "images";
private Resources mResources;
private ArrayList<Drawable> mPlaceHolderDrawables;
private boolean mFadeInImage = true;
private int mMaxImageHeight = 0;
private int mMaxImageWidth = 0;
/**
* Creates an ImageLoader with Bitmap memory cache. No default placeholder image will be shown
* while the image is being fetched and loaded.
*/
public ImageLoader(FragmentActivity activity) {
super(newRequestQueue(activity),
BitmapCache.getInstance(activity.getSupportFragmentManager()));
mResources = activity.getResources();
}
/**
* Creates an ImageLoader with Bitmap memory cache and a default placeholder image while the
* image is being fetched and loaded.
*/
public ImageLoader(FragmentActivity activity, int defaultPlaceHolderResId) {
super(newRequestQueue(activity),
BitmapCache.getInstance(activity.getSupportFragmentManager()));
mResources = activity.getResources();
mPlaceHolderDrawables = new ArrayList<Drawable>(1);
mPlaceHolderDrawables.add(defaultPlaceHolderResId == -1 ?
null : mResources.getDrawable(defaultPlaceHolderResId));
}
/**
* Creates an ImageLoader with Bitmap memory cache and a list of default placeholder drawables.
*/
public ImageLoader(FragmentActivity activity, ArrayList<Drawable> placeHolderDrawables) {
super(newRequestQueue(activity),
BitmapCache.getInstance(activity.getSupportFragmentManager()));
mResources = activity.getResources();
mPlaceHolderDrawables = placeHolderDrawables;
}
/**
* Starts processing requests on the {@link RequestQueue}.
*/
public void startProcessingQueue() {
getRequestQueue().start();
}
/**
* Stops processing requests on the {@link RequestQueue}.
*/
public void stopProcessingQueue() {
getRequestQueue().stop();
}
public ImageLoader setFadeInImage(boolean fadeInImage) {
mFadeInImage = fadeInImage;
return this;
}
public ImageLoader setMaxImageSize(int maxImageWidth, int maxImageHeight) {
mMaxImageWidth = maxImageWidth;
mMaxImageHeight = maxImageHeight;
return this;
}
public ImageLoader setMaxImageSize(int maxImageSize) {
return setMaxImageSize(maxImageSize, maxImageSize);
}
public ImageContainer get(String requestUrl, ImageView imageView) {
return get(requestUrl, imageView, 0);
}
public ImageContainer get(String requestUrl, ImageView imageView, int placeHolderIndex) {
return get(requestUrl, imageView, mPlaceHolderDrawables.get(placeHolderIndex),
mMaxImageWidth, mMaxImageHeight);
}
public ImageContainer get(String requestUrl, ImageView imageView, Drawable placeHolder) {
return get(requestUrl, imageView, placeHolder, mMaxImageWidth, mMaxImageHeight);
}
public ImageContainer get(String requestUrl, ImageView imageView, Drawable placeHolder,
int maxWidth, int maxHeight) {
// Find any old image load request pending on this ImageView (in case this view was
// recycled)
ImageContainer imageContainer = imageView.getTag() != null &&
imageView.getTag() instanceof ImageContainer ?
(ImageContainer) imageView.getTag() : null;
// Find image url from prior request
String recycledImageUrl = imageContainer != null ? imageContainer.getRequestUrl() : null;
// If the new requestUrl is null or the new requestUrl is different to the previous
// recycled requestUrl
if (requestUrl == null || !requestUrl.equals(recycledImageUrl)) {
if (imageContainer != null) {
// Cancel previous image request
imageContainer.cancelRequest();
imageView.setTag(null);
}
if (requestUrl != null) {
// Queue new request to fetch image
imageContainer = get(requestUrl,
getImageListener(mResources, imageView, placeHolder, mFadeInImage),
maxWidth, maxHeight);
// Store request in ImageView tag
imageView.setTag(imageContainer);
} else {
imageView.setImageDrawable(placeHolder);
imageView.setTag(null);
}
}
return imageContainer;
}
private static ImageListener getImageListener(final Resources resources,
final ImageView imageView, final Drawable placeHolder, final boolean fadeInImage) {
return new ImageListener() {
@Override
public void onResponse(ImageContainer response, boolean isImmediate) {
imageView.setTag(null);
if (response.getBitmap() != null) {
setImageBitmap(imageView, response.getBitmap(), resources,
fadeInImage && !isImmediate);
} else {
imageView.setImageDrawable(placeHolder);
}
}
@Override
public void onErrorResponse(VolleyError volleyError) {
}
};
}
private static RequestQueue newRequestQueue(Context context) {
// On HC+ use HurlStack which is based on HttpURLConnection. Otherwise fall back on
// AndroidHttpClient (based on Apache DefaultHttpClient) which should no longer be used
// on newer platform versions where HttpURLConnection is simply better.
Network network = new BasicNetwork(
UIUtils.hasHoneycomb() ?
new HurlStack() :
new HttpClientStack(AndroidHttpClient.newInstance(
NetUtils.getUserAgent(context))));
Cache cache = new DiskBasedCache(getDiskCacheDir(context, CACHE_DIR));
RequestQueue queue = new RequestQueue(cache, network);
queue.start();
return queue;
}
/**
* Sets a {@link android.graphics.Bitmap} to an {@link android.widget.ImageView} using a
* fade-in animation. If there is a {@link android.graphics.drawable.Drawable} already set on
* the ImageView then use that as the image to fade from. Otherwise fade in from a transparent
* Drawable.
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
private static void setImageBitmap(final ImageView imageView, final Bitmap bitmap,
Resources resources, boolean fadeIn) {
// If we're fading in and on HC MR1+
if (fadeIn && UIUtils.hasHoneycombMR1()) {
// Use ViewPropertyAnimator to run a simple fade in + fade out animation to update the
// ImageView
imageView.animate()
.scaleY(0.95f)
.scaleX(0.95f)
.alpha(0f)
.setDuration(imageView.getDrawable() == null ? 0 : HALF_FADE_IN_TIME)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
imageView.setImageBitmap(bitmap);
imageView.animate()
.alpha(1f)
.scaleY(1f)
.scaleX(1f)
.setDuration(HALF_FADE_IN_TIME)
.setListener(null);
}
});
} else if (fadeIn) {
// Otherwise use a TransitionDrawable to fade in
Drawable initialDrawable;
if (imageView.getDrawable() != null) {
initialDrawable = imageView.getDrawable();
} else {
initialDrawable = transparentDrawable;
}
BitmapDrawable bitmapDrawable = new BitmapDrawable(resources, bitmap);
// Use TransitionDrawable to fade in
final TransitionDrawable td =
new TransitionDrawable(new Drawable[] {
initialDrawable,
bitmapDrawable
});
imageView.setImageDrawable(td);
td.startTransition(UIUtils.ANIMATION_FADE_IN_TIME);
} else {
// No fade in, just set bitmap directly
imageView.setImageBitmap(bitmap);
}
}
/**
* Get a usable cache directory (external if available, internal otherwise).
*
* @param context The context to use
* @param uniqueName A unique directory name to append to the cache dir
* @return The cache dir
*/
public static File getDiskCacheDir(Context context, String uniqueName) {
// Check if media is mounted or storage is built-in, if so, try and use external cache dir
// otherwise use internal cache dir
// TODO: getCacheDir() should be moved to a background thread as it attempts to create the
// directory if it does not exist (no disk access should happen on the main/UI thread).
final String cachePath =
Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||
!Environment.isExternalStorageRemovable()
? getExternalCacheDir(context).getPath()
: context.getCacheDir().getPath();
return new File(cachePath + File.separator + uniqueName);
}
/**
* Get the external app cache directory.
*
* @param context The context to use
* @return The external cache dir
*/
private static File getExternalCacheDir(Context context) {
// TODO: This needs to be moved to a background thread to ensure no disk access on the
// main/UI thread as unfortunately getExternalCacheDir() calls mkdirs() for us (even
// though the Volley library will later try and call mkdirs() as well from a background
// thread).
return context.getExternalCacheDir();
}
/**
* Interface an activity can implement to provide an ImageLoader to its children fragments.
*/
public interface ImageLoaderProvider {
public ImageLoader getImageLoaderInstance();
}
}
现在在您的活动中创建ImageLoader并在创建适配器时将其传递给适配器。
@Override
public void onCreate(Bundle savedInstanceState){
...
ImageLoader mImageLoader = new ImageLoader(this, R.drawable.default_image);
MoviesAdapter mMoviesAdapter = new MoviesAdapter(this, R.layout.your_list_layout, moviesList, mImageLoader);
...
}
调整Movie Adapter构造函数。
package com.example.jdmb;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class MoviesAdapter extends ArrayAdapter<Movies> {
private ArrayList<Movies> movieData;
private Activity context;
private ImageLoader mImageLoader;
public MoviesAdapter(Context context, int resource,
ArrayList<Movies> objects, ImageLoader loader) {
super(context, resource, objects);
this.context = (Activity) context;
this.movieData = objects;
this.mImageLoader = loader;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
ViewHolder holder;
if (view == null) {
LayoutInflater vi = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = vi.inflate(R.layout.list_row, parent, false);
holder = new ViewHolder();
holder.id = (TextView) view.findViewById(R.id.movieId);
holder.title = (TextView) view.findViewById(R.id.movieTitle);
holder.vote_avg = (TextView) view.findViewById(R.id.movieAvg);
holder.backdrop_path = (ImageView) view
.findViewById(R.id.movieBackdrop);
holder.release_date = (TextView) view
.findViewById(R.id.movieRelease);
holder.original_title = (TextView) view
.findViewById(R.id.movieTitle2);
holder.vote_count = (TextView) view.findViewById(R.id.movieCount);
holder.adult = (TextView) view.findViewById(R.id.movieAdult);
holder.poster = (ImageView) view.findViewById(R.id.moviePoster);
holder.popularity = (TextView) view
.findViewById(R.id.moviePopularity);
view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
}
Movies movie = movieData.get(position);
if (movie != null) {
// check to see if each individual textview is null.
// if not, assign some text!
holder.id.setText("id: " + movie.getId());
holder.title.setText("title: " + movie.getTitle());
holder.vote_avg.setText("vote_avg: " + movie.getVote_average());
mImageLoader.get(movie.getBackdrop_path(), holder.backdrop_path);
holder.release_date.setText("release_date: "
+ movie.getRelease_date());
holder.original_title.setText("original_title: "
+ movie.getOriginal_title());
holder.vote_count.setText("vote_count: " + movie.getVote_count());
holder.adult.setText("adult: " + movie.isAdult());
mImageLoader.get(movie.getPoster_path(), holder.poster);
holder.popularity.setText("popularity: " + movie.getPopularity());
}
return view;
}
private static class ViewHolder {
TextView id;
TextView title;
TextView vote_avg;
ImageView backdrop_path;
TextView release_date;
TextView original_title;
TextView vote_count;
TextView adult;
ImageView poster;
TextView popularity;
}
}
我可能错过了某些内容或输入错误,因为我没有直接测试这个,但我在自己的应用程序中使用了类似的实现。