Android ListView-图像在滚动时洗牌

Android ListView-图像在滚动时洗牌,android,listview,asynchronous,imageview,Android,Listview,Asynchronous,Imageview,我有一个包含两个文本视图和一个图像视图的列表视图。图像从Internet加载,并由LruCache缓存。当在ListView中滚动时,图像会被洗牌几秒钟。在正确的图像完全加载之前,不应该有任何图像。我发现了几个同样问题的问题,但没有人帮我:/。这是我的密码: public class NewsAdapter extends BaseAdapter { private static LayoutInflater inflater; private List<Item>

我有一个包含两个文本视图和一个图像视图的列表视图。图像从Internet加载,并由LruCache缓存。当在ListView中滚动时,图像会被洗牌几秒钟。在正确的图像完全加载之前,不应该有任何图像。我发现了几个同样问题的问题,但没有人帮我:/。这是我的密码:

public class NewsAdapter extends BaseAdapter {
    private static LayoutInflater inflater;
    private List<Item> items = new LinkedList<Item>();
    private LruCache<String, Bitmap> mMemoryCache;

    public NewsAdapter(Context context) {
        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        // Bitmap Cache
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        final int cacheSize = maxMemory / 8;
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return getSizeInBytes(bitmap) / 1024;
            }
        };
    }

    public void add(Item item) {
        items.add(item);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder = new ViewHolder();
        if(convertView == null) {
            convertView = inflater.inflate(R.layout.list_item_item, null);
            viewHolder.ivPic = (ImageView) convertView.findViewById(R.id.ivPic);
            viewHolder.tvTitle = (TextView) convertView.findViewById(R.id.tvTitle);
            viewHolder.tvShortDesc = (TextView) convertView.findViewById(R.id.tvShortDesc);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }

        final Item item = items.get(position);
        viewHolder.tvTitle.setText(item.getTitle());
        viewHolder.tvShortDesc.setText(Html.fromHtml(item.getShortDesc()));

        Bitmap bitmap = mMemoryCache.get(item.getPicUrl());
        if (bitmap != null) {
            viewHolder.ivPic.setImageBitmap(bitmap);
        } else {
            GetBitmap gb = new GetBitmap(item.getPicUrl(), viewHolder.ivPic);
            gb.execute();
        }

        return convertView;
    }

    static class ViewHolder {
        ImageView ivPic;
        TextView tvTitle;
        TextView tvShortDesc;
    }

    @Override
    public int getCount() {
        return items.size();
    }

    @Override
    public Item getItem(int position) {
        return items.get(position);
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @SuppressLint("NewApi")
    public static int getSizeInBytes(Bitmap bitmap) {
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            return bitmap.getByteCount();
        } else {
            return bitmap.getRowBytes() * bitmap.getHeight();
        }
    }

    private class GetBitmap extends AsyncTask<Void, Bitmap, Bitmap> {
        private String url;
        private ImageView ivPic;

        public GetBitmap(String url, ImageView ivPic) {
            this.url = url;
            this.ivPic = ivPic;
        }

        @Override
        protected Bitmap doInBackground(Void... params) {
            Bitmap bitmap = null;
            try {
                URL url = new URL(this.url);
                bitmap = BitmapFactory.decodeStream(url.openConnection().getInputStream());
            } catch (Exception e) {
                e.printStackTrace();
            }

            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            if (bitmap != null) ivPic.setImageBitmap(bitmap); 
        }

    }

}
公共类NewsAdapter扩展了BaseAdapter{
私人静电充气机;
私有列表项=新建LinkedList();
私人LruCache-mMemoryCache;
公共新闻适配器(上下文){
充气器=(LayoutFlater)context.getSystemService(context.LAYOUT\u充气器\u服务);
//位图缓存
final int maxMemory=(int)(Runtime.getRuntime().maxMemory()/1024);
最终int cacheSize=maxMemory/8;
mMemoryCache=新的LruCache(缓存大小){
@凌驾
受保护的int-sizeOf(字符串键、位图){
返回getSizeInBytes(位图)/1024;
}
};
}
公共作废添加(项目){
项目。添加(项目);
}
@凌驾
公共视图getView(int位置、视图转换视图、视图组父视图){
ViewHolder ViewHolder=新ViewHolder();
if(convertView==null){
convertView=充气机。充气(R.layout.list\u item\u item,null);
viewHolder.ivPic=(ImageView)convertView.findViewById(R.id.ivPic);
viewHolder.tvTitle=(TextView)convertView.findViewById(R.id.tvTitle);
viewHolder.tvShortDesc=(TextView)convertView.findViewById(R.id.tvShortDesc);
convertView.setTag(viewHolder);
}否则{
viewHolder=(viewHolder)convertView.getTag();
}
最终项目=项目。获取(位置);
viewHolder.tvTitle.setText(item.getTitle());
viewHolder.tvShortDesc.setText(Html.fromHtml(item.getShortDesc());
位图Bitmap=mMemoryCache.get(item.getPicUrl());
if(位图!=null){
viewHolder.ivPic.setImageBitmap(位图);
}否则{
GetBitmap gb=新的GetBitmap(item.getPicUrl(),viewHolder.ivPic);
gb.execute();
}
返回视图;
}
静态类视窗夹{
ImageView-ivPic;
文本视图标题;
文本视图tvShortDesc;
}
@凌驾
public int getCount(){
返回items.size();
}
@凌驾
公共项getItem(int位置){
返回项目。获取(位置);
}
@凌驾
公共长getItemId(int位置){
返回0;
}
@SuppressLint(“新API”)
公共静态整型getSizeInBytes(位图){
if(Build.VERSION.SDK\u INT>=Build.VERSION\u code.HONEYCOMB){
返回bitmap.getByteCount();
}否则{
返回bitmap.getRowBytes()*bitmap.getHeight();
}
}
私有类GetBitmap扩展异步任务{
私有字符串url;
私有ImageView ivPic;
公共GetBitmap(字符串url,ImageView ivPic){
this.url=url;
this.ivPic=ivPic;
}
@凌驾
受保护位图doInBackground(无效…参数){
位图=空;
试一试{
URL=新URL(this.URL);
位图=BitmapFactory.decodeStream(url.openConnection().getInputStream());
}捕获(例外e){
e、 printStackTrace();
}
返回位图;
}
@凌驾
受保护的void onPostExecute(位图){
onPostExecute(位图);
如果(位图!=null)ivPic.setImageBitmap(位图);
}
}
}
如果有人有主意就好了。。。提前谢谢


注:我忘了一些东西,请不要建议使用任何库,我想在没有任何外部库的情况下使用。

可能就是这个代码片段

 ViewHolder viewHolder = new ViewHolder();
也就是说,每次调用getView时,都会创建一个新的viewHolder实例

请尝试以下方法:

   final ViewHolder viewHolder;
然后在if结构中

    viewHolder = new ViewHolder();
在您的代码中实现:

  @Override
public View getView(int position, View convertView, ViewGroup parent) {
   final ViewHolder viewHolder; 

    if(convertView == null) {
        convertView = inflater.inflate(R.layout.list_item_item, null);
        viewHolder = new ViewHolder();
        viewHolder.ivPic = (ImageView) convertView.findViewById(R.id.ivPic);
        viewHolder.tvTitle = (TextView) convertView.findViewById(R.id.tvTitle);
        viewHolder.tvShortDesc = (TextView) convertView.findViewById(R.id.tvShortDesc);
        convertView.setTag(viewHolder);
    } else {
        viewHolder = (ViewHolder) convertView.getTag();
    }

    final Item item = items.get(position);
    viewHolder.tvTitle.setText(item.getTitle());
    viewHolder.tvShortDesc.setText(Html.fromHtml(item.getShortDesc()));

    Bitmap bitmap = mMemoryCache.get(item.getPicUrl());
    if (bitmap != null) {
        viewHolder.ivPic.setImageBitmap(bitmap);
    } else {
        GetBitmap gb = new GetBitmap(item.getPicUrl(), viewHolder.ivPic);
        gb.execute();
    }

    return convertView;
}

问题是您没有检查图像是否处于相同位置。请尝试上面的代码,希望对您有所帮助

这与视图回收和图像放置在错误的位置有关。看看这里,尝试删除final-here:final-Item=items.get(position);你需要在箱子里放一个占位符图像(位图==null)@tyczj好的,我会调查的。@LIlya Demidov我认为这样做不行。谢谢,你说得对,你的解决方案更好。但这不是问题所在。这个问题也发生在我实现整个ViewHolder之前。每次调用getView时都创建一个新的ViewHolder实例是对内存的浪费非常好!如果我滚动很快,仍然有一个错误的图像在半秒左右,但它比以前好多了!完美的非常感谢你!现在一切都正常了:)。我真的不明白(整数)getTag。。。。这里有什么?它不应该与像iv.getTag()这样的ImageView关联吗?@Vidor是的,它是针对图像的视图标记,需要加载。
public GetBitmap(String url, ImageView ivPic, int position) {
    this.url = url;
    this.ivPic = ivPic;
    this.position = position;
    ivPic.setTag(position);
    ivPic.setImageBitmap(null);
}

@Override
protected void onPostExecute(Bitmap bitmap) {
     super.onPostExecute(bitmap);
     if(bitmap != null && ((Integer)getTag) == this.position)
         ivPic.setImageBitmap(bitmap); 
}