我有一个可以容纳图像的ListView。这取决于SDCARD中是否存在图像。
这是我的示例代码:
public class MainActivity extends Activity {
ListView mListView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mListView = new ListView(this);
setContentView(mListView);
String[] arr = new String[] {
"/example/images/1.jpg", "/example/images/2.jpg",
"/example/images/3.jpg", "/example/images/4.jpg",
"/example/images/5.jpg", "/example/images/6.jpg",
"/example/images/7.jpg", "/example/images/8.jpg",
"/example/images/9.jpg", "/example/images/1.jpg",
"/example/images/2.jpg", "/example/images/3.jpg",
"/example/images/4.jpg", "/example/images/5.jpg",
"/example/images/6.jpg", "/example/images/7.jpg",
"/example/images/8.jpg", "/example/images/9.jpg",
"/example/images/1.jpg", "/example/images/2.jpg",
"/example/images/3.jpg", "/example/images/4.jpg",
"/example/images/5.jpg", "/example/images/6.jpg",
"/example/images/7.jpg", "/example/images/8.jpg",
"/example/images/9.jpg", "/example/images/1.jpg",
"/example/images/2.jpg", "/example/images/3.jpg",
"/example/images/4.jpg", "/example/images/5.jpg",
"/example/images/6.jpg", "/example/images/7.jpg",
"/example/images/8.jpg", "/example/images/9.jpg"};
List<String> list = Arrays.asList(arr);
MyAdapter adapter = new MyAdapter(this, R.layout.listitem_imv, list);
mListView.setAdapter(adapter);
}
class MyAdapter extends ArrayAdapter<String>{
List<String> mList;
LayoutInflater mInflater;
int mResource;
public MyAdapter(Context context, int resource,
List<String> objects) {
super(context, resource, objects);
mResource = resource;
mInflater = getLayoutInflater();
mList = objects;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
if(convertView == null){
view = mInflater.inflate(mResource, null);
}else{
view = convertView;
}
ImageView imageView = (ImageView) view.findViewById(R.id.imv);
TextView textView = (TextView) view.findViewById(R.id.txv);
imageView.setTag(mList.get(position));//tag of imageView == path to image
new LoadImage().execute(imageView);
textView.setText(mList.get(position).toString());
return view;
}
}
class LoadImage extends AsyncTask<Object, Void, Bitmap>{
private ImageView imv;
private String path;
@Override
protected Bitmap doInBackground(Object... params) {
imv = (ImageView) params[0];
path = imv.getTag().toString();
Bitmap bitmap = null;
File file = new File(
Environment.getExternalStorageDirectory().getAbsolutePath() + path);
if(file.exists()){
bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap result) {
if(result != null && imv != null){
imv.setVisibility(View.VISIBLE);
imv.setImageBitmap(result);
}else{
imv.setVisibility(View.GONE);
}
}
}
}
'sdcard / example / images'目录包含图像:1.jpg,2.jpg,3.jpg,4.jpg,6.jpg,7.jpg和9.jpg。 预期的结果是:
但是,如果我快速滚动列表,则会在错误的项目中插入一些图像。 它是由于在getView()方法中使用convertView而发生的。
如果我使用以下代码,代码可以正常工作:
//if(convertView == null){
// view = mInflater.inflate(mResource, null);
//}else{
// view = convertView;
//}
view = mInflater.inflate(mResource, null);
当列表快速滚动时,由于使用了convertView,两个asyncTasks可以引用同一个View。 当视图不再可见时,如何取消AsyncTask?(并且由ListView的另一项使用)
修改
@Override
protected void onPostExecute(Bitmap result) {
if(result != null && imv != null){
if(imv.getTag().equals(path)){
imv.setVisibility(View.VISIBLE);
imv.setImageBitmap(result);
}else{
imv.setVisibility(View.GONE);
}
}else{
imv.setVisibility(View.GONE);
}
}
答案 0 :(得分:41)
您可以将ImageView发送到任务构造函数,并在那里保留对图像路径的引用。现在在onPostExecute中,检查ImageView的当前标记是否与您开始使用的标记相同。如果是,则设置图像。如果不是,请不要做任何事情。
但是,这意味着无论如何都会下载图像。你不会在视图上设置错误的图像。
修改强> 首先将ImageView传递给任务构造函数:
new LoadImage(imageView).execute()
然后在LoadImage构造函数中保存对ImageView和图像路径的引用。在构造函数中而不是在doInBackground中保存路径非常重要,以确保我们不会遇到多线程问题。然后在onPostExecute我们检查当前路径。
class LoadImage extends AsyncTask<Object, Void, Bitmap>{
private ImageView imv;
private String path;
public LoadImage(ImageView imv) {
this.imv = imv;
this.path = imv.getTag().toString();
}
@Override
protected Bitmap doInBackground(Object... params) {
Bitmap bitmap = null;
File file = new File(
Environment.getExternalStorageDirectory().getAbsolutePath() + path);
if(file.exists()){
bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap result) {
if (!imv.getTag().toString().equals(path)) {
/* The path is not same. This means that this
image view is handled by some other async task.
We don't do anything and return. */
return;
}
if(result != null && imv != null){
imv.setVisibility(View.VISIBLE);
imv.setImageBitmap(result);
}else{
imv.setVisibility(View.GONE);
}
}
}
答案 1 :(得分:8)
这个Android Developers Blog post将为您提供完整的参考项目,包括缓存。只需用SD卡文件读取替换Http访问代码。
答案 2 :(得分:2)
我希望这会有所帮助。 经过大量的搜索,我有了这个有效的解决方案。
public class CustomAdapter extends ArrayAdapter<String>{
/*
public CustomAdapter(Context context , String[] video) {
super(context,R.layout.custom_row, video);
}
*/
private final Activity context;
private final String[] video;
static class ViewHolder {
public TextView videoTitle;
public ImageView videoThumbnail;
public int position;
public String path;
}
public CustomAdapter(Activity context, String[] video) {
super(context, R.layout.custom_row, video);
this.context = context;
this.video = video;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
LayoutInflater videoInflator = LayoutInflater.from(getContext());
View customView = videoInflator.inflate(R.layout.custom_row, parent, false);
ViewHolder viewHolder = new ViewHolder();
viewHolder.position = position;
viewHolder.path = video[position];
viewHolder.videoTitle = (TextView) customView.findViewById(R.id.videoTitle);
viewHolder.videoThumbnail = (ImageView) customView.findViewById(R.id.videoThumbnail);
//rowView.setTag(viewHolder);
//}
customView.setTag(viewHolder);
final String videoItem = video[position];
int index=videoItem.lastIndexOf('/');
String lastString=(videoItem.substring(index +1));
index = lastString.indexOf(".mp4");
lastString=(lastString.substring(0,index));
viewHolder.videoTitle.setText(lastString);
new AsyncTask<ViewHolder, Void, Bitmap>() {
private ViewHolder v;
@Override
protected Bitmap doInBackground(ViewHolder... params) {
v = params[0];
Bitmap thumb = ThumbnailUtils.createVideoThumbnail(videoItem, MediaStore.Images.Thumbnails.MINI_KIND);
return thumb;
}
@Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
if (v.position == position) {
// If this item hasn't been recycled already, hide the
// progress and set and show the image
v.videoThumbnail.setImageBitmap(result);
}
}
}.execute(viewHolder);
return customView;
}
}
答案 3 :(得分:1)
也许你应该尝试:
view = mInflater.inflate(mResource,parent,null);
查看此博客,它解释了类似的问题:
http://www.doubleencore.com/2013/05/layout-inflation-as-intended/
答案 4 :(得分:0)
我会做什么(除非你有数千张图片): 1.创建一个数据结构 - 一个包含要显示的String名称和位图的简单类 2.为它创建一个适配器 3.在getView方法中将正确的位图分配给正确的ImageView。
在你的情况下,虽然你可以创建一个类似的数据结构,但不是一个位图而是一个AsyncTask。无论如何,您需要将asynctask与字符串绑定到一个项目中。这些项目的数组(或arraylist)将被送到您的适配器。显示的是图像视图和文本视图。
可以使用cancel()取消AsyncTask。
答案 5 :(得分:0)
嘿,我发现这个问题的解决方案只是使用以下函数而不是函数
@Override
protected void onPostExecute(Bitmap result) {
if (!imv.getTag().toString().equals(rec_id)) {
return;
}
if(result != null && imv != null){
int index = id.indexOf(imv.getTag().toString());
if(list.getFirstVisiblePosition()<=index && index<=list.getLastVisiblePosition())
{
imv.setVisibility(View.VISIBLE);
imv.setImageBitmap(result);
}
}else{
imv.setImageBitmap(icon);
imv.setVisibility(View.GONE);
}
}
此处列表是listview的对象。只需将列表视图对象传递给适配器并粘贴此函数,而不是onPostExecute函数。