Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> ListView異步加載與緩存

ListView異步加載與緩存

編輯:關於Android編程

該例子實現的是從網絡上異步獲取數據,包括圖片與文字,然後顯示在listView中,並對圖片進行內存緩存。

程序截圖

這裡寫圖片描述

第一次錄制gif。。。效果很差

這裡寫圖片描述

首先定義布局

主界面就一個listView




   

然後定義一個item_layout用於設置子項的布局也很簡單



    
    
        
        
    

定義Bean

根據對json數據的分析,我新建了一個實體類,三個屬性,一個是Tilte,一個是Content,一個是imageUrl

public class NewsBean {
    private String mImageUrl;
    private String mTitle;
    private String mContent;

    public String getmContent() {
        return mContent;
    }

    public String getmTitle() {
        return mTitle;
    }

    public String getmImageUrl() {
        return mImageUrl;
    }

    public void setmImageUrl(String mImageUrl) {
        this.mImageUrl = mImageUrl;
    }

    public void setmTitle(String mTitle) {
        this.mTitle = mTitle;
    }

    public void setmContent(String mContent) {
        this.mContent = mContent;
    }
}

獲取JSON數據

這裡用AsyncTask訪問網絡獲取到json,並解析json成NewsBean類型,在doInbackground方法裡面進行訪問,在onPostExecute方法裡面對ListView設置Adapter

 class MyAsyncTask extends AsyncTask> {


        @Override
        protected List doInBackground(String... params) {
            List newsBeans = getJsonData(params[0]);
            return newsBeans;
        }

        @Override
        protected void onPostExecute(List newsBeen) {
            super.onPostExecute(newsBeen);
            mAdapter = new MyAdapter(newsBeen, MainActivity.this);
            mListView.setAdapter(mAdapter);
        }

        //根據url獲取json數據,返回集合
        public List getJsonData(String url) {
            List newsBeens = new ArrayList<>();
            try {
                String jsonString = readStream(new URL(url).openStream());
                JSONObject object;
                NewsBean newsBean;
                object = new JSONObject(jsonString);
                JSONArray jsonArray = object.getJSONArray("data");
                for (int i = 0; i < jsonArray.length(); i++) {
                    newsBean = new NewsBean();
                    object = jsonArray.getJSONObject(i);
                    newsBean.setmTitle(object.getString("name"));
                    newsBean.setmContent(object.getString("description"));
                    newsBean.setmImageUrl(object.getString("picSmall"));
                    newsBeens.add(newsBean);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return newsBeens;
        }

        public String readStream(InputStream in) {
            InputStreamReader isb;
            String result = "";
            try {
                String line = "";
                isb = new InputStreamReader(in, "UTF-8");
                BufferedReader br = new BufferedReader(isb);
                while ((line = br.readLine()) != null) {
                    result += line;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
    }

自定義的ImageLoader方法

通過調用ImageLoader提供的displayImageByAsyncTask(ImageView imageview,String url)方法,來加載圖片。定義NewsAsyncTask類來實現網絡加載圖片,定義一個LruCache(Least recently used 最近最少使用算法)來緩存圖片到內存中。如果第一次加載就通過網絡加載圖片,並添加到緩存中,再次加載如果緩存中存在就直接從緩存中獲取。這裡只做了內存緩存,當然也可以加上磁盤緩存。進行二級緩存。

public class ImageLoader {
    ;

    private ImageView mImageView;
    private String mUrl;
    private LruCache mCache;

    public ImageLoader() {
        //新建緩存,緩存大小為系統內存的1/4
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        int cacheMemory = maxMemory / 4;
        mCache = new LruCache(cacheMemory) {
            @Override
            //獲取每個緩存的大小,這裡為圖片的大小
            protected int sizeOf(String key, Bitmap value) {
                return value.getByteCount();
            }
        };
    }
//添加圖片到緩存中,LruCache的實質就是Map
    public void addBitmapToCache(String url, Bitmap bitmap) {
        if (getBitmapFromCache(url) == null) {
            mCache.put(url, bitmap);
        }
    }
//從緩存中通過url獲取到緩存的圖片
    public Bitmap getBitmapFromCache(String url) {
        return mCache.get(url);
    }


    public void displayImageByAsyncTask(final ImageView imageView, final String url) {
        Bitmap bitmap = getBitmapFromCache(url);
        if (bitmap == null) {
            //如果緩存中沒有,就從網絡加載
            new NewsAsyncTask(imageView, url).execute(url);
        } else {
            //緩存中有的話直接使用緩存中的圖片
                imageView.setImageBitmap(bitmap);
        }

    }
//通過url從網絡加載圖片的方法
    public Bitmap getBitmapFromUrl(String url) {
        Bitmap bitmap;
        InputStream in = null;
        try {
            HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
            in = connection.getInputStream();
            //通過BitmapFactory的decodeStream方法可直接獲取到bitmap對象
            bitmap = BitmapFactory.decodeStream(in);
            //注意釋放資源
            connection.disconnect();
            return bitmap;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    private class NewsAsyncTask extends AsyncTask {
        private ImageView mImageView;
        private String mUrl;

        public NewsAsyncTask(ImageView imageView, String url) {
            //接收傳遞過來的image和url
            mImageView = imageView;
            mUrl = url;
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            String url = params[0];
            //從網絡加載圖片,如果加載到了就放到緩存中
            Bitmap bitmap = getBitmapFromUrl(url);
            if (bitmap != null) {
                addBitmapToCache(url, bitmap);
            }
            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            //通過為imageView設置的Tag來與url比較,防止網絡不好時出現異步加載圖片錯位的問題
            if (mImageView.getTag().equals(mUrl)) {
                mImageView.setImageBitmap(bitmap);
            }
        }
    }
}

定義listView的適配器MyAdapter

關鍵點在於為imageView設置TAG,我們知道listView都是復用的,因此有時候網絡不好的時候,當一個View的圖片還沒加載完成時,他就被劃出了屏幕被再次復用,雖然位置不同,但是他們使用的都是同一個View,這時候可能就會先顯示之前沒加載好的圖片,然後再顯示當前需要顯示的圖片,這就出現了錯位。解決方法是為ImageView設置唯一標識比如加載的url。當被復用的時候,TAG會改變,這時候判斷一個TAG值,當前的與之前的TAG不一樣,因此就不加載之前的image,只需要加載目前需要現實的image。這樣在加載的時候就不會錯位了。

public class MyAdapter extends BaseAdapter{
    private Context mContext;
    private List mNews;
    private LayoutInflater mInflater;
    private ImageLoader mImageLoader;
    public MyAdapter(List news,Context context){
        mContext=context;
        mNews=news;
        mInflater=LayoutInflater.from(mContext);
        //初始化ImageLoader,確保緩存只有一個
        mImageLoader=new ImageLoader();
    }
    @Override
    public int getCount() {
        return mNews.size();
    }

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

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder mHolder;
        if(convertView==null){
            mHolder=new ViewHolder();
            convertView=mInflater.inflate(R.layout.item_layout,null);
            mHolder.mIconImageView= (ImageView) convertView.findViewById(R.id.item_iv_icon);
            mHolder.mTitleTextView= (TextView) convertView.findViewById(R.id.item_tv_title);
            mHolder.mContentTextView= (TextView) convertView.findViewById(R.id.item_tv_content);
            convertView.setTag(mHolder);
        }else {
            mHolder= (ViewHolder) convertView.getTag();
        }
        mHolder.mIconImageView.setImageResource(R.mipmap.ic_launcher);
        mImageLoader.displayImageByAsyncTask(mHolder.mIconImageView,mNews.get(position).getmImageUrl());
        mHolder.mTitleTextView.setText(mNews.get(position).getmTitle());
        mHolder.mContentTextView.setText(mNews.get(position).getmContent());
        //為ImageView設置TAG為對應的url,防止圖片加載錯位
        mHolder.mIconImageView.setTag(mNews.get(position).getmImageUrl());
        return convertView;
    }

    class ViewHolder{
        public TextView mContentTextView;
        public TextView mTitleTextView;
        public ImageView mIconImageView;
    }
}
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved