ListView的优化

ListView的优化问题可以说是面试的必考题。我之前看过一遍视频 Android必学-异步加载,感觉里面讲解的知识都是ListView优化常用的,这里我就通过里面的示例来做一个总结。本篇准备通过这个示例来谈一谈ListView的优化,通过分析在写code的过程中遇到的问题,来谈一下解决办法。

示例是这样的:
http://www.imooc.com/api/teacher?type=4&num=30 中加载json格式的数据,解析出来,然后用ListView显示出来。下面是完成后的图片(没有找到有效设置图片大小的方法,大家有知道的话,留言给我)
先看我的示例listView的item的布局:

item.png

com.mecury.javamai.png
1.对于这样一个需求,我们首先应该是解析得到的数据,然后将得到的数据提供给ListView的Adapter,有关于这部分的代码,写在了MainActivity.java,我放到文章后面,毕竟重点不在这。
2.当获得到数据之后,接下来就是编写Adapter了。
  • 使用ViewHolder模式来提高效率

Viewholder模式充分了ListView的视图缓存机制,避免了每次在调用getView的时候都去通过findViewById实例化数据。《Android群英传》中说:据测试,使用ViewHolder将提高50%的效率。对应代码中就是这样,先在NewsAdapter(本示例中的Adapter)新建一个内部类:(详细代码见文章后的NewsAdapter.java

//使用ViewHolder
    private static class ViewHolder {
        private TextView tvTitle, tvContent;
        private ImageView ivIcon;
    }

在重写的getView方法中这样写:

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder;
        //判断是否有缓存
        if (convertView == null) {
            //通过LayoutInflate实例化布局
            viewHolder = new ViewHolder();
            convertView = mInflater.inflate(R.layout.item_layout, parent, false);
            viewHolder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon);
            viewHolder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title);
            viewHolder.tvContent = (TextView) convertView.findViewById(R.id.tv_content);
            convertView.setTag(viewHolder);
        } else {
           //通过tag找到缓存的布局
            viewHolder = (ViewHolder) convertView.getTag();
        }
        NewsBean newsBean = newsBeanList.get(position);

        String urlString = newsBean.newsIconUrl;
        viewHolder.ivIcon.setTag(urlString); // 将ImageView与url绑定
        //普通异步加载
       // mImageLoader.showImageByThread(viewHolder.ivIcon,urlString);
        mImageLoader.showImageByAsyncTask(viewHolder.ivIcon,urlString);
        viewHolder.tvTitle.setText(newsBean.newsTitle);
        viewHolder.tvContent.setText(newsBean.newsContent);
        return convertView;
    }
  • 异步加载:耗时的操作放在异步线程中

如果在adapter中的某些操作需要耗费大量的时间,这个时候就要用到异步线程来进行异步就在数据。比如:现在要加载图片,此时我们需要根据url访问网络得到数据,然后将数据解析为Bitmap设置给View。这些操作如果不进行异步处理而直接放入adapter,可想而知,我们的ListView会有多卡。
这里向大家提供两种异步加载线程的方式:(代码查看下面的ImageLoader.java)
向网络获得数据,异步中需要进行的操作:

    public Bitmap getBitmapFormURL(String urlString) {
        Bitmap bitmap;
        InputStream inputStream = null;

        try {
            URL url = new URL(urlString);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            inputStream = new BufferedInputStream(conn.getInputStream());  //得到图片的数据流
            bitmap = BitmapFactory.decodeStream(inputStream);  //根据数据流来解析出图片的bitmap
            conn.disconnect();
            return bitmap;
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        return null;
    }
一种是通过多线程方式通过Handler+Message进行异步加载
private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //这个getTag是下面的问题要讲的优化,大家可以先看看
            if (mImageView.getTag().equals(mUrl)) { //当url标记和原先设置的一样时,才设置ImageView
                mImageView.setImageBitmap((Bitmap) msg.obj);
            }

        }
    };
    public void showImageByThread(ImageView imageView, final String url) {

        this.mImageView = imageView;
        this.mUrl = url;
        new Thread() {
            @Override
            public void run() {
                super.run();
                Bitmap bitmap = getBitmapFormURL(url);
                Message message = Message.obtain();
                message.obj = bitmap;
                handler.sendMessage(message);
            }
        }.start();
    }
另一种是通过AsyncTask来进行异步操作

AsyncTask的使用方法,不熟悉的可以学习下,这里不讲了,毕竟不是重点。

public void showImageByAsyncTask(ImageView imageView,String url){  
        new NewsAsyncTask(imageView,url).execute(url);  
    } 

构建的AsyncTask的内部类

class NewsAsyncTask extends AsyncTask<String,Void,Bitmap>{  
          
        private ImageView myImageView;  
        private String mUrl;  
          
        public NewsAsyncTask(ImageView imageView,String url){  
            myImageView = imageView;  
            mUrl = url;  
        }  
              //String...params是可变参数接受execute中传过来的参数  
        @Override  
        protected Bitmap doInBackground(String... params) {  
              
            String url=params[0];  
            //这里同样调用我们的getBitmapFromeUrl  
            Bitmap bitmap = getBitmapFromUrl(params[0]);  
            return bitmap;  
        }  
        //这里的bitmap是从doInBackgroud中方法中返回过来的  
        @Override  
        protected void onPostExecute(Bitmap bitmap) {  
            super.onPostExecute(bitmap);  
                imageView.setImageBitmap(bitmap);  
        }  
    } 

listView错位加载问题

当上面的代码写完时,如果先不用让大家先看看的mImageView.getTag().equals(mUrl)判断一下(上面的代码并不完整,下面会讲),你会发现如下图的情况,在Listview中的各个item加载的过程中,出现了item错位的情况。一张图片在多个位置显示,过了一段时间才正常。

GIF.gif

对于上面的情况,我们先来看看产生这种情况的原因(下面内容主要引用自android listview 异步加载图片并防止错位):

231543494598347.jpg

网上找了一张图, listview 异步加载图片之所以错位的根本原因是重用了 convertView 且有异步操作.
如果不重用 convertView 不会出现错位现象, 重用 convertView 但没有异步操作也不会有问题。
我简单分析一下:
当重用 convertView 时,最初一屏显示 7 条记录, getView 被调用 7 次,创建了 7 个 convertView.
当 Item1 划出屏幕, Item8 进入屏幕时,这时没有为 Item8 创建新的 view 实例, Item8 复用的是
Item1 的 view 如果没有异步不会有任何问题,虽然 Item8 和 Item1 指向的是同一个 view,但滑到
Item8 时刷上了 Item8 的数据,这时 Item1 的数据和 Item8 是一样的,因为它们指向的是同一块内存,
但 Item1 已滚出了屏幕你看不见。当 Item1 再次可见时这块 view 又涮上了 Item1 的数据。
但当有异步下载时就有问题了,假设 Item1 的图片下载的比较慢,Item8 的图片下载的比较快,你滚上去
使 Item8 可见,这时 Item8 先显示它自己下载的图片没错,但等到 Item1 的图片也下载完时你发现
Item8 的图片也变成了 Item1 的图片,因为它们复用的是同一个 view。 如果 Item1 的图片下载的比
Item8 的图片快, Item1 先刷上自己下载的图片,这时你滑下去,Item8 的图片还没下载完, Item8
会先显示 Item1 的图片,因为它们是同一快内存,当 Item8 自己的图片下载完后 Item8 的图片又刷成
了自己的,你再滑上去使 Item1 可见, Item1 的图片也会和 Item8 的图片是一样的,
因为它们指向的是同一块内存。

解决的办法由很多种,这里说一下最简单的一种:使用findViewWithTag
在NewsAdapter中为ImageView设置Tag标识位:

//将url设为imagView的标识位
String urlString = newsBean.newsIconUrl; 
viewHolder.ivIcon.setTag(urlString); // 将ImageView与url绑定

然后再加载的过程中通过url来判断对应imagview位置是否一致来决定是否加载。

if (mImageView.getTag().equals(mUrl)) { 
//当url标记和原先设置的一样时,才设置ImageView    
mImageView.setImageBitmap((Bitmap) msg.obj);
}

为图片设置缓存

设置缓存的重要性不言而喻,在加载数据的过程中,你不可能每次都从网络上加载(除非数据量小)。如果即之一这样做就要面对两个问题:1.加载数据浪费的时间 2.加载数据消耗的流量 。信不信用户一言不合就把你这破app给卸载了。。。。。。
设置缓存的原理这里也不再说了,要说的话估计又成一片博文了。这里介绍用法,让你看看怎么写:(这部分代码,在文章末尾的ImageLoader.java中)
1.首先声明一个LrcCache
private LruCache<String,Bitmap> mMemoryCaches;
2.然后初始化

 //下面是建立缓存
        int maxMemory = (int) Runtime.getRuntime().maxMemory();  //运行时最大内存
        int cacheSize = maxMemory/4;  //缓存的大小
        mCaches = new LruCache<String, Bitmap>(cacheSize){
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getByteCount();
            }
        };

3.添加方法,在适当的地方调用

//将bitmap添加到缓存
    public void addBitmapToCache(String url,Bitmap bitmap){
        if (getBitmapFormCache(url) == null){
            mCaches.put(url, bitmap);
        }
    }

    //从缓存中获取数据
    public Bitmap getBitmapFormCache(String url){
        return mCaches.get(url);
    }

ListView的滑动时停止加载和分页加载

先说分页加载,我们不用每次把ListView所有的Item都一次性加载完毕,这样做没必要也很累。我们仅仅需要加载那部分显示在屏幕部分的Item即可,这样所要加载的数据就减少了很多。另一方面,我们需要考虑另一个问题,当用户滑动时,显示在屏幕的Item会不断的变化,如果只是加载显示在屏幕的Item,这也没有必要,因此我们应该在停止滑动时再加载数据。这样说,understand?
实现步骤:
1.让NewsAdapter实现接口AbsListView.OnScrollListener,并复写里面的方法
2.在复写的方法里面进行一些操作。

@Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        switch (scrollState){
            case SCROLL_STATE_IDLE:  //滑动停止时。
                mImageLoader.loadImages(mStart, mEnd);
                break;
            case SCROLL_STATE_TOUCH_SCROLL: //正在滑动时
                mImageLoader.cancelAllTasks();
                break;
            case SCROLL_STATE_FLING: //手指抛动时,即手指用力滑动在离开后ListView由于惯性而继续滑动

                break;
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        mStart = firstVisibleItem;
        mEnd = firstVisibleItem + visibleItemCount;
        //第一次的时候预加载
        if (mFirstIn && visibleItemCount > 0){
            mImageLoader.loadImages(mStart, mEnd);
            mFirstIn = false;
        }
    }

可以看到在滑动停止时我们调用了ImageLoader类的loadIamge方法,这是一个加载图片的方法,用于加载Item对应的第mSart项t到第mEnd项之间的数据(这两个数据怎么来的,你先把这个问题放在心里),看下loadImage法的代码实现:

public void loadImages(int start, int end){
        for (int i = start; i < end; i++){
            String url = NewsAdapter.URLS[i];
            //由缓存中得到bitmap
            Bitmap bitmap = getBitmapFormCache(url);
            if (bitmap == null){
                //当bitmap为空时,由AsyncTask进行加载,并在onPostExecute()方法中setImageBitmap
                NewsAsyncTask task = new NewsAsyncTask(url);
                task.execute(url);
                mAsyncTask.add(task);
            } else {
                //当bitmap不为空时,直接进行setImageBitmap
                ImageView imageView = (ImageView) mListView.findViewWithTag(url);
                imageView.setImageBitmap(bitmap);
            }
        }
    }

由代码中可以看到我们由NewsAdatper.URLS[]中取出了start到end的url,然后加载了这些数据,我们看下这个数组,这是在NewsAdapter的构造器中,可以明显看到里面存放了所有Item的图片的url:

//将图片的url存储在数组中
        URLS = new String[data.size()];
        for (int i = 0; i < data.size(); i++) {
            URLS[i] = data.get(i).newsIconUrl;
        }

此时再来看NewsAsyncTask的代码:

  //参数1:启动任务输入的参数,参数2:后台任务执行的百分比,参数3,后台执行任务的返回方法
    private class NewsAsyncTask extends AsyncTask<String, Void, Bitmap> {

        private String mUrl;

        public NewsAsyncTask(String stringUrl) {
            mUrl = stringUrl;
        }

        //doInBackground方法的参数是上面输入的第一个参数,返回的对象会传递给onPostExecute方法
        @Override
        protected Bitmap doInBackground(String... params) {
            String url = params[0];
            Bitmap bitmap = getBitmapFormURL(url);
            if (bitmap != null){
                addBitmapToCache(url,bitmap); //将bitmap添加到缓存
            }
            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            //根据url从listView中找到对应的ImageView
            ImageView imageView = (ImageView) mListView.findViewWithTag(mUrl);
            if (imageView != null && bitmap != null){
                imageView.setImageBitmap(bitmap); //为imageView设置图片
            }
            mAsyncTask.remove(this);
        }
    }

可以看到这就是根据url为所对应ImageView设置图片。看到这就只有一个问题了,那就是mStart和mEnd是怎么得到的,再看代码我们重写的onScroll方法:

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        mStart = firstVisibleItem;
        mEnd = firstVisibleItem + visibleItemCount;
        //第一次的时候预加载
        if (mFirstIn && visibleItemCount > 0){
            mImageLoader.loadImages(mStart, mEnd);
            mFirstIn = false;
        }
    }

这个方法有三个参数,对应的为
firstVisibleItem:ListView所有当前可见的Item第一个Item
visibleItemCount:可见Item的总数
totalItemCount :所有Item的总数
这样一来大家是不是都懂了。

code

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private ListView listView;

    private static final String url = "http://www.imooc.com/api/teacher?type=4&num=30";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        listView = (ListView) findViewById(R.id.lv);

        new MainAsyncTask().execute(url);

    }


    public List<NewsBean> getJsonData(String url){
        List<NewsBean> newsBeanList = new ArrayList<>();
        try {
            String jsonString = readStream(new URL(url).openStream());//利用readStream得到String数据
            Log.e("JSON",jsonString); //打印出string数据

            //下面解析得到的json数据
            JSONObject jsonObject;
            NewsBean newsBean;
            try {
                jsonObject = new JSONObject(jsonString);
                JSONArray jsonArray = jsonObject.getJSONArray("data");
                for (int i=0; i<jsonArray.length(); i++){
                    jsonObject = jsonArray.getJSONObject(i);
                    newsBean = new NewsBean();
                    newsBean.newsIconUrl = jsonObject.getString("picSmall");
                    newsBean.newsTitle = jsonObject.getString("name");
                    newsBean.newsContent = jsonObject.getString("description");
                    newsBeanList.add(newsBean);
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return newsBeanList;
    }

    //由输入流中读取数据并将数据返回
    public String readStream(InputStream in){
        InputStreamReader reader;
        String result = "";
        String line = "";
        try {
            reader = new InputStreamReader(in, "UTF-8");
            BufferedReader br = new BufferedReader(reader);
            while((line = br.readLine())!=null){
                result += line;
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }


    //异步线程类
    class MainAsyncTask extends AsyncTask<String, Void, List<NewsBean>>{

        //该方法运行在后台线程中
        @Override
        protected List<NewsBean> doInBackground(String... params) {
            return getJsonData(params[0]);
        }

        @Override
        protected void onPostExecute(List<NewsBean> newsBeanList) {
            super.onPostExecute(newsBeanList);
            NewsAdapter newsAdapter = new NewsAdapter(MainActivity.this, newsBeanList, listView);
            listView.setAdapter(newsAdapter);
        }
    }
}

NewsAdapter.java

public class NewsAdapter extends BaseAdapter implements AbsListView.OnScrollListener {

    private List<NewsBean> newsBeanList = new ArrayList<>();
    private LayoutInflater mInflater;
    private ImageLoader mImageLoader;
    private int mStart, mEnd;
    public static String[] URLS;
    private boolean mFirstIn;

    public NewsAdapter(Context context, List<NewsBean> data, ListView listView) {
        newsBeanList = data;
        mInflater = LayoutInflater.from(context);
        mImageLoader = new ImageLoader(listView);

        //将图片的url存储在数组中
        URLS = new String[data.size()];
        for (int i = 0; i < data.size(); i++) {
            URLS[i] = data.get(i).newsIconUrl;
        }

        listView.setOnScrollListener(this);
        mFirstIn = true;
    }

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

    @Override
    public Object getItem(int position) {
        return newsBeanList.get(position);
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder;
        //判断是否有缓存
        if (convertView == null) {
            //通过LayoutInflate实例化布局
            viewHolder = new ViewHolder();
            convertView = mInflater.inflate(R.layout.item_layout, parent, false);
            viewHolder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon);
            viewHolder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title);
            viewHolder.tvContent = (TextView) convertView.findViewById(R.id.tv_content);
            convertView.setTag(viewHolder);
        } else {
           //通过tag找到缓存的布局
            viewHolder = (ViewHolder) convertView.getTag();
        }
        NewsBean newsBean = newsBeanList.get(position);

        String urlString = newsBean.newsIconUrl;
        viewHolder.ivIcon.setTag(urlString); // 将ImageView与url绑定
        //普通异步加载
       // mImageLoader.showImageByThread(viewHolder.ivIcon,urlString);
        mImageLoader.showImageByAsyncTask(viewHolder.ivIcon,urlString);
        viewHolder.tvTitle.setText(newsBean.newsTitle);
        viewHolder.tvContent.setText(newsBean.newsContent);
        return convertView;
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        switch (scrollState){
            case SCROLL_STATE_IDLE:  //滑动停止时。
                mImageLoader.loadImages(mStart, mEnd);
                break;
            case SCROLL_STATE_TOUCH_SCROLL: //正在滑动时
                mImageLoader.cancelAllTasks();
                break;
            case SCROLL_STATE_FLING: //手指抛动时,即手指用力滑动在离开后ListView由于惯性而继续滑动

                break;
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        mStart = firstVisibleItem;
        mEnd = firstVisibleItem + visibleItemCount;
        //第一次的时候预加载
        if (mFirstIn && visibleItemCount > 0){
            mImageLoader.loadImages(mStart, mEnd);
            mFirstIn = false;
        }
    }

    //使用ViewHolder
    private static class ViewHolder {
        private TextView tvTitle, tvContent;
        private ImageView ivIcon;
    }
}

ImageLoder.java

public class ImageLoader {

    private ImageView mImageView;
    private String mUrl;

    private LruCache<String, Bitmap> mCaches;
    private ListView mListView;
    private Set<NewsAsyncTask> mAsyncTask;

    public ImageLoader(ListView listView){

        mListView = listView;
        mAsyncTask = new HashSet<>();

        //下面是建立缓存
        int maxMemory = (int) Runtime.getRuntime().maxMemory();  //运行时最大内存
        int cacheSize = maxMemory/4;
        mCaches = new LruCache<String, Bitmap>(cacheSize){
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getByteCount();
            }
        };
    }

    //将bitmap添加到缓存
    public void addBitmapToCache(String url,Bitmap bitmap){
        if (getBitmapFormCache(url) == null){
            mCaches.put(url, bitmap);
        }
    }
    //从缓存中获取数据
    public Bitmap getBitmapFormCache(String url){
        return mCaches.get(url);
    }
//===================================下面为普通异步加载===========================================
private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (mImageView.getTag().equals(mUrl)) { //当url标记和原先设置的一样时,才设置ImageView
                mImageView.setImageBitmap((Bitmap) msg.obj);
            }
        }
    };

    public void showImageByThread(ImageView imageView, final String url) {

        this.mImageView = imageView;
        this.mUrl = url;
        new Thread() {
            @Override
            public void run() {
                super.run();
                Bitmap bitmap = getBitmapFormURL(url);
                Message message = Message.obtain();
                message.obj = bitmap;
                handler.sendMessage(message);
            }
        }.start();
    }

//====================上面是使用普通的异步加载,下面是使用AsyncTask进行的异步加载==================
    public Bitmap getBitmapFormURL(String urlString) {
        Bitmap bitmap;
        InputStream inputStream = null;

        try {
            URL url = new URL(urlString);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            inputStream = new BufferedInputStream(conn.getInputStream());  //得到图片的数据流
            bitmap = BitmapFactory.decodeStream(inputStream);  //根据数据流来解析出图片的bitmap
            conn.disconnect();
            return bitmap;
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        return null;
    }

   
    //加载图片
    public void showImageByAsyncTask(ImageView ImageView, String url) {
        Bitmap bitmap = getBitmapFormCache(url);
        if (bitmap == null){
            ImageView.setImageResource(R.mipmap.ic_launcher);
        }else{
            ImageView.setImageBitmap(bitmap);
        }
    }

    public void cancelAllTasks(){
        if (mAsyncTask != null){
            for (NewsAsyncTask task : mAsyncTask){
                task.cancel(false);
            }
        }
    }

    public void loadImages(int start, int end){
        for (int i = start; i < end; i++){
            String url = NewsAdapter.URLS[i];
            //由缓存中得到bitmap
            Bitmap bitmap = getBitmapFormCache(url);
            if (bitmap == null){
                //当bitmap为空时,由AsyncTask进行加载,并在onPostExecute()方法中setImageBitmap
                NewsAsyncTask task = new NewsAsyncTask(url);
                task.execute(url);
                mAsyncTask.add(task);
            } else {
                //当bitmap不为空时,直接进行setImageBitmap
                ImageView imageView = (ImageView) mListView.findViewWithTag(url);
                imageView.setImageBitmap(bitmap);
            }
        }
    }

    //参数1:启动任务输入的参数,参数2:后台任务执行的百分比,参数3,后台执行任务的返回方法
    private class NewsAsyncTask extends AsyncTask<String, Void, Bitmap> {

        private String mUrl;

        public NewsAsyncTask(String stringUrl) {
            mUrl = stringUrl;
        }

        //doInBackground方法的参数是上面输入的第一个参数,返回的对象会传递给onPostExecute方法
        @Override
        protected Bitmap doInBackground(String... params) {
            String url = params[0];
            Bitmap bitmap = getBitmapFormURL(url);
            if (bitmap != null){
                addBitmapToCache(url,bitmap); //将bitmap添加到缓存
            }
            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            //根据url从listView中找到对应的ImageView
            ImageView imageView = (ImageView) mListView.findViewWithTag(mUrl);
            if (imageView != null && bitmap != null){
                imageView.setImageBitmap(bitmap);
            }
            mAsyncTask.remove(this);
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容