Android解析JSON數據異步加載新聞圖片

參考文章:解析Json並異步加載數據新聞信息(包括新聞圖片)
在這裏插入圖片描述
1.先寫出普通的ListView所實現的效果
首先從簡單的開始吧,咱們先寫item項,也就是列表中的一條信息,也就是至關於之後每條模板的做用。實現的效果圖是這樣的。在這裏插入圖片描述
設計圖是這樣的:
在這裏插入圖片描述
在這裏插入圖片描述
下面直接上代碼:
item_layout.xmljava

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="4dp">

    <ImageView
        android:id="@+id/iv_icon"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:src="@mipmap/ic_launcher" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical"
        android:paddingLeft="4dp">

        <TextView
            android:maxLines="1"
            android:id="@+id/tv_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Title"
            android:textSize="15sp" />

        <TextView
            android:maxLines="3"
            android:id="@+id/tv_content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Content"
            android:textSize="10sp" />
    </LinearLayout>
</LinearLayout>

主佈局也很簡單,就是加一個充滿父容器的ListView控件。
翠花,上效果圖。
在這裏插入圖片描述
翠花,再給客官上上等的代碼:
activity_main.xmlandroid

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ListView
        android:id="@+id/lv_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

是的,就這麼簡單,以爲太容易了嗎?那我下面就按部就班了~~坐穩扶好。web

接下來咱們就分析一下JSON數據:
下面是API:http://www.imooc.com/api/teacher?type=4&num=30
湊夠下面的圖中,咱們能夠看到,最外層是status,data裏是一個列表,列表的子項,分別有id,name,picSmall,picBig, description,還有learner。而咱們只須要新聞的圖片(picSmall),標題(name),和內容(description)就能夠了。
在這裏插入圖片描述
根據提供的json數據咱們就能夠先建立咱們本身的實體類來存放這些信息,而咱們僅須要三條信息。
因而咱們建立的實體類就寫三個屬性就能夠,分別是newsTitle,newsContent,newsUrl。
信息以下:
NewsBean.class算法

package com.iyuba.iyubanews;

public class NewsBean {
    public String newsTitle; //新聞標題
    public String newsUrl; //圖片地址
    public String newsIcon; 
}

而後咱們還有建立咱們本身的適配器,繼承自BaseAdapter,同時,並在適配器中建立List存放新聞消息NewsBean,並在構造方法中初始化該List。建立一個ViewHolder類,存放要顯示的內容,建立LayoutInflater對象,映射要顯示的佈局文件。
NewsAdapter .classsql

package com.iyuba.iyubanews;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;

public class NewsAdapter extends BaseAdapter {
    List<NewsBean> mList;
    LayoutInflater mInflater;

    public NewsAdapter(Context context,List<NewsBean> mList) {
        mInflater = LayoutInflater.from(context);
        this.mList = mList;
    }

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

    @Override
    public Object getItem(int i) {
        return mList.get(i);
    }

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

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {

        ViewHolder holder = null;
        if (view==null){ //若是爲空,從新建立
            holder = new ViewHolder();
            view = mInflater.inflate(R.layout.item_layout,null);
            holder.tv_title = view.findViewById(R.id.tv_title);
            holder.tv_content = view.findViewById(R.id.tv_content);
            holder.iv_icon = view.findViewById(R.id.iv_icon);
            view.setTag(holder);
        }else { //若是不爲空,從以前建立中獲取
            holder = (ViewHolder) view.getTag();
        }
        holder.tv_content.setText(mList.get(i).newsContent);
        holder.tv_title.setText(mList.get(i).newsTitle);
        holder.iv_icon.setImageResource(R.mipmap.ic_launcher);
        return view;
    }
    class ViewHolder{
        public TextView tv_title; //標題
        public TextView tv_content;//內容
        public ImageView iv_icon; //圖片
    }
}

重點來了!
異步類
接下來咱們須要在主佈局中建立一個異步類來加載解析Json數據。建立一個NewsAsyncTask類,繼承AsyncTask類,並實現他的doInBackground方法。填寫參數,第一個是String 表示傳入的url是這個類型,第二個Void表示不關心過程,第三個是List,表示要返回List的數據。
首先,在MainActivity類中建立一個方法readStream(),用來解析網頁返回的數據。json

/**
         * 解析網頁返回數據
         * @param is
         * @return
         */
        public String readStream(InputStream is){
            InputStreamReader isr;
            String result ="";
            try {
                isr = new InputStreamReader(is,"utf-8");

            BufferedReader br = new BufferedReader(isr);
            String line = "";
            while ((line=br.readLine())!=null){
                result += line;
            }
            return result;
            } catch (IOException e) {
                e.printStackTrace();
            }
            return  null;
        }

其次,在MainActivity類中建立一個getJsonData()方法, 將url對應的json數據轉化爲咱們所封裝的NewsBeanapi

private List<NewsBean> getJsonData(String url) {
            List<NewsBean> newsBeans = new ArrayList<>();
            NewsBean newsBean;
            try {
                String objectString = readStream(new URL(url).openStream());
                JSONObject jsonObject = new JSONObject(objectString);
                JSONArray jsonArray = jsonObject.getJSONArray("data");
                for (int i = 0; i < jsonArray.length(); i++) {
                    newsBean = new NewsBean();
                    jsonObject = (JSONObject) jsonArray.get(i);
                    newsBean.newsTitle = jsonObject.getString("name");
                    newsBean.newsContent = jsonObject.getString("description");
                    newsBean.newsUrl = jsonObject.getString("picSmall");
                    newsBeans.add(newsBean);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (JSONException e) {
                e.printStackTrace();
            }
            return newsBeans;
        }

而後在NewsAsyncTask類中onPostExecute()中將適配器與ListView關聯數組

@Override
        protected void onPostExecute(List<NewsBean> newsBeans) {
            super.onPostExecute(newsBeans);
            NewsAdapter adapter = new NewsAdapter(MainActivity.this,list);
            mListView.setAdapter(adapter);
        }

千萬不要忘記增長INTERNET訪問權限緩存

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.iyuba.iyubanews">

    <uses-permission android:name="android.permission.INTERNET" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

先到這裏爲止,先上傳一下項目的運行效果還有MainActivity.java文件
在這裏插入圖片描述
MainActivity.class網絡

package com.iyuba.iyubanews;

import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.LinearLayout;
import android.widget.ListView;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private ListView mListView;
    private List<NewsBean> list;
    private static 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);
        mListView = findViewById(R.id.lv_main);
        new NewsAsyncTask().execute(URL);
    }
    /**
     * 解析網頁返回數據
     * @param is
     * @return
     */
    private String readStream(InputStream is) {
        InputStreamReader isr;
        String result = "";
        try {
            String line = "";
            isr = new InputStreamReader(is, "utf-8"); //字節流轉化爲字符流
            BufferedReader br = new BufferedReader(isr);
            while ((line = br.readLine()) != null) {
                result += line;
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    private List<NewsBean> getJsonData(String url) {
        List<NewsBean> newsBeanList = new ArrayList<>();
        try {
            String jsonString = readStream(new URL(url).openStream());
            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.newsUrl = jsonObject.getString("picSmall");
                    newsBean.newsTitle = jsonObject.getString("name");
                    newsBean.newsContent = jsonObject.getString("description");
                    newsBeanList.add(newsBean);
                    Log.d("Tag","newsBeanList.size() is the "+newsBeanList.size());
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }

            Log.d("xys", "jsonString");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return newsBeanList;

    }
    class NewsAsyncTask extends AsyncTask<String,Void,List<NewsBean>>{
        @Override
        protected List<NewsBean> doInBackground(String... strings) {
            list = getJsonData(strings[0]);
            return list;
        }

        @Override
        protected void onPostExecute(List<NewsBean> newsBeans) {
            super.onPostExecute(newsBeans);
            NewsAdapter adapter = new NewsAdapter(MainActivity.this,list);
            mListView.setAdapter(adapter);
        }
        
    }
}

-----------------------------------------以上爲通常適配器展現界面-------------------------------------------------------
下面開始加入對圖片的加載,以前是制訂默認的圖片。
如今建立一個圖片工具類ImageLoader,專門用於下載圖片信息
在類中首先建立一個方法getBitmapFromUrl,專門下載圖片

/**
     * 下載圖片
     * @param url 圖片地址
     */
    public Bitmap getBitmapFromUrl(String urlstring){
        Bitmap bitmap;
        InputStream is = null;
        try {
            URL url = new URL(urlstring);
            HttpURLConnection con = (HttpURLConnection) url.openConnection();
            is = new BufferedInputStream(con.getInputStream());
            bitmap = BitmapFactory.decodeStream(is);
            con.disconnect();
            return bitmap;
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

而後寫異步方法showImageByAsyncTask(),因爲非主線程不能直接在線程中更新UI,因此咱們要先建立一個Handler做爲一個消息的傳遞來更新UI,也須要建立一個全局變量ImageView來傳遞。在showImageByAsyncTask()中創建一個Message發送更新UI請求。那麼咱們接下來先寫主線程

private ImageView mImageView; 
private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            mImageView = (ImageView) msg.obj;
        }
    };

而後展現多線程方法showImageByAsyncTask()

/**
     * 多線程 加載圖片
     * @param imageView
     * @param url
     */
   public void showImageByThread(final ImageView imageView, final String url){
       mImageView = imageView;
       new Thread(new Runnable() {
           @Override
           public void run() {
               Bitmap bitmap = getBitmapFromUrl(url);
               Message message = Message.obtain();
               message.obj = bitmap;
               handler.sendMessage(message);
           }
       }).start();
   }

咱們還須要一個經過URL下載BitMap的方法

/**
     * 從url中獲取bitmap
     */
    public Bitmap getBitmapFromUrl(String urlstring){
        Bitmap bitmap;
        InputStream is = null;
        try {
            URL url = new URL(urlstring);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            is = new BufferedInputStream(connection.getInputStream());
            bitmap = BitmapFactory.decodeStream(is);
            connection.disconnect();
            return bitmap;
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                is.close();
            }catch (Exception e){

            }
        }
        return null;
    }

又是到了翠花上酸菜的時間:
ImageLoader.java

package com.iyuba.mytablayout.utils;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.widget.ImageView;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class ImageLoader {

    private ImageView mImageView;
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            mImageView = (ImageView) msg.obj;
        }
    };
    /**
     * 多線程 加載圖片
     * @param imageView
     * @param url
     */
   public void showImageByThread(final ImageView imageView, final String url){
       mImageView = imageView;
       new Thread(new Runnable() {
           @Override
           public void run() {
               Bitmap bitmap = getBitmapFromUrl(url);
               Message message = Message.obtain();
               message.obj = bitmap;
               handler.sendMessage(message);
           }
       }).start();
   }
    /**
     * 從url中獲取bitmap
     */
    public Bitmap getBitmapFromUrl(String urlstring){
        Bitmap bitmap;
        InputStream is = null;
        try {
            URL url = new URL(urlstring);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            is = new BufferedInputStream(connection.getInputStream());
            bitmap = BitmapFactory.decodeStream(is);
            connection.disconnect();
            return bitmap;
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                is.close();
            }catch (Exception e){

            }
        }
        return null;
    }
}

運行效果如圖:
在這裏插入圖片描述
而後呢,咱們再實現一下異步下載圖片:
首先建立一個NewsAsyncTask類,繼承AsyncTask,參數分別是String,表明傳入url爲string類型,第二個參數Void表明不關心過程,Bitmap表示返回的是這個類型。最後再實現他的方法。
最開始是這個樣子。

private class NewsAsyncTask extends AsyncTask<String,Void,Bitmap>{

        @Override
        protected Bitmap doInBackground(String... strings) {
            return null;
        }
    }

咱們須要在doInBackground()方法中將url(strings[0])傳給getBitmapFromUrl()來返回Bitmap對象。代碼以下:

@Override
        protected Bitmap doInBackground(String... strings) {
            return getBitmapFromUrl(strings[0]);
        }

咱們還須要一個ImageView對象來與指定的imageview綁定,對imageview設置他的Bitmap對象。在構造方法中將imageview將他綁定。代碼以下:

private ImageView mImageView;
        public NewsAsyncTask(ImageView imageView){
            mImageView = imageView;
        }

而後咱們再重載他的onPostExecute()方法,在這個方法裏面對imageview設置他的Bitmap對象,代碼以下:

@Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            mImageView.setImageBitmap(bitmap);
        }

翠花,再來展現一下所有的代碼:
ImageLoader.class

package com.iyuba.iyubanews;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.widget.ImageView;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class ImageLoader {
    private ImageView mImageView;
    private String mUrl;
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            mImageView.setImageBitmap((Bitmap) msg.obj);
        }
    };

    /**
     * 經過多線程 下載圖片
     * @param imageView
     * @param url
     */
    public void showImageByThread(final ImageView imageView, final String url){
        new Thread(new Runnable() {
            @Override
            public void run() {
                mImageView = imageView;
                Bitmap bitmap = getBitmapFromUrl(url);
                Message message = Message.obtain();
                message.obj = bitmap;
                handler.sendMessage(message);
            }
        }).start();
    }


    /**
     * 下載圖片
     *
     * @param urlstring 圖片地址
     */
    public Bitmap getBitmapFromUrl(String urlstring) {
        Bitmap bitmap;
        InputStream is = null;
        try {
            URL url = new URL(urlstring);
            HttpURLConnection con = (HttpURLConnection) url.openConnection();
            is = new BufferedInputStream(con.getInputStream());
            bitmap = BitmapFactory.decodeStream(is);
            con.disconnect();
            return bitmap;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     *
     * @param imageView
     * @param mUrl
     */
    public void showImageByAsyncTask(ImageView imageView,String mUrl){
        new NewsAsyncTask(imageView).execute(mUrl);
    }

    /**
     * 異步加載圖片
     */
    private class NewsAsyncTask extends AsyncTask<String,Void,Bitmap>{
        private ImageView mImageView;
        public NewsAsyncTask(ImageView imageView){
            mImageView = imageView;
        }
        @Override
        protected Bitmap doInBackground(String... strings) {
            return getBitmapFromUrl(strings[0]);
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            mImageView.setImageBitmap(bitmap);
        }
    }
}

效果圖和上圖同樣,這裏我就不上傳了,只不過呢,有一些小瑕疵,好比圖片錯位,浪費網絡流量,佔用大量存儲空間,因此,下面咱們要對它進行優化。
方法一:設置tag方法
在NewsAdapter的getView()方法中,在調用ImageLoader方法以前,爲圖片設置setTag()方法,代碼以下:

holder.iv_icon.setTag(mList.get(i).newsUrl);
        new ImageLoader().showImageByAsyncTask(holder.iv_icon,mList.get(i).newsUrl);

在ImageLoader類中的NewsAsyncTask中的構造方法中傳入url,並設置全局的String類型的url對象,在onPostExecute方法中調用圖片imageView的getTag方法,代碼以下:

private ImageView mImageView;
        private String mUrl;
        public NewsAsyncTask(ImageView imageView,String url){
            mImageView = imageView;
            mUrl = url;
        }
@Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            if (mImageView.getTag().equals(mUrl)){
                mImageView.setImageBitmap(bitmap);
            }
        }

以上,就是圖片防止錯亂的代碼。


下面咱們再來引進關於Cache的LRU算法:

LRU算法:Least Recently Used近期最少使用算法
爲Android提供了LruCache類來實現這個緩存方法

使用緩存,將下載圖片緩存下來,一方面,讓ListView滑動更加流暢。
首先呢,須要的就是翠花,給這位看博客的客官上一盤酸菜:
在ImageLoader 中定義一個全局變量

private LruCache<String, Bitmap> mCaches;//須要保存緩存對象的名字,保存對象  本質是map

String類型的key傳入的是圖片的地址,Bitmap不用多說,你懂的~

在ImageLoader的構造方法中初始化緩存大小,翠花,上酸菜~
值得一說的是要注意從新加載sizeOf()方法,不然默認返回元素的個數
調用 value.getByteCount()方法,將Bitmap實際大小返回

public ImageLoader(ListView listView) {
          ......
          ......
          ......
        //獲取最大可用內存
        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的實際大小傳入
            }
        }; //初始化緩存大小
    }

在NewsAdapter中的getView()方法中將new ImageLoader()方法去掉,換成咱們定義的全局變量imageLoader,這樣每次調用該方法時,就不會從新建立緩存區。

public class NewsAdapter extends BaseAdapter {
    ......
    ......
    private ImageLoader imageLoader;
     @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
    .......
     holder.iv_icon.setTag(mList.get(i).newsUrl);
     imageLoader.showImageByAsyncTask(holder.iv_icon,mList.get(i).newsUrl);
    .......
    }
    }

而後再建立兩個方法,一個是將圖片保存到緩存中,另外一個是將圖片從緩存中獲取到:
保存到緩存的方法是addBitmapToCache(String url, Bitmap bitmap) ,先判斷緩存是否存在,若是存在,就從對應的key(url)返回相應的bitmap

/**
     * 將內容保存的Cache
     */
    public void addBitmapToCache(String url, Bitmap bitmap) {
        if (getBitmapFromCache(url) == null) { //判斷當前緩存是否存在
            mCaches.put(url, bitmap);
        }
    }

第二個就是從緩存中讀取內容:

/**
     * 從Cache中讀取內容
     */
    public Bitmap getBitmapFromCache(String url) {
        //從緩存中獲取數據
        return mCaches.get(url);
    }

緊接着呢,咱們要修改showImageByAsyncTask(ImageView imageView, String url) 方法

public void showImageByAsyncTask(ImageView imageView, String url) {
        //先判斷緩存中是否存在數據,減小下載時間  從緩存中取出對應的圖片
        Bitmap bitmap = getBitmapFromCache(url);
        if (bitmap == null) { //若是緩存中沒有,必須從網絡下載
            //new NewsAsyncTask(url).execute(url);
            imageView.setImageResource(R.mipmap.ic_launcher);
        } else { //直接從內存中獲取 並設置
            imageView.setImageBitmap(bitmap);
        }
    }

在NewsAsyncTask 內部類中doInBackground(String… params)方法中加入以下代碼:

@Override
        protected Bitmap doInBackground(String... params) {
            String url = params[0];
            //將下載的圖片保存緩存中 從網絡獲取圖片
            Bitmap bitmap = getBitmapFromUrl(url);
            if (bitmap != null) {
                //將再也不緩存的圖片加入緩存
                addBitmapToCache(url, bitmap);
            }
            return bitmap;
        }

以上,咱們就完成了優化圖片緩存。


下面咱們進行滾動時的優化。
解決ListView滾動時卡頓問題
ListView滑動中止後纔會加載可見項
ListView滑動時,取消全部加載項

首先,在NewsAdapter中實現一個接口 AbsListView.OnScrollListener,而後實現他的兩個抽象方法onScrollStateChanged(),onScroll(),其中onScrollStateChanged()在狀態切換的時候會調用,而onScroll()會在整個調用過程當中都會被調用。

public class NewsAdapter extends BaseAdapter implements AbsListView.OnScrollListener {
    @Override
    public void onScrollStateChanged(AbsListView absListView, int i) {

    }

    @Override
    public void onScroll(AbsListView absListView, int i, int i1, int i2) {

    }
}

定義兩個全局變量mStart,mEnd,分別記錄ListView第一個開始的位置和最後一個item位置

public class NewsAdapter extends BaseAdapter implements AbsListView.OnScrollListener {
     ......
     ......
    private int mStart,mEnd;
    ......
    ......
    }

在onScroll()方法中給mStart,mEnd賦值

@Override
    public void onScroll(AbsListView absListView, int i, int i1, int i2) {
        mStart = i;
        mEnd = i+i1;
    }

在onScrollStateChanged()方法中判斷ListView的狀態,若是時中止狀態,就加載數據

@Override
    public void onScrollStateChanged(AbsListView absListView, int i) {
        if (i == SCROLL_STATE_IDLE){
            //加載可見項
        }else{
            //中止加載可見項
        }
    }

在NewsAdapter類添加全局變量存放全部圖片的連接的靜態數組

public static String[] URLS; //建立靜態數組保存全部圖片的連接

在ImageLoader類中建立全局變量

private ListView mListView;
    private Set<NewsAsyncTask> mTasks;

在構造方法中對他進行初始化,並將構造方法傳入一個ListView方法

public ImageLoader(ListView mListView){
        this.mListView = mListView;
        mTasks = new HashSet<>();
        ......
    }

在構造方法中對他進行初始化,並註冊一下listview

public NewsAdapter(Context context,List<NewsBean> mList) {
        ......
        this.mList = mList;
        ......
        URLS = new String[mList.size()];
        for (int i = 0; i < mList.size(); i++) {
            URLS[i] = mList.get(i).newsUrl;
        }
         listView.setOnScrollListener(this);
    }

在ImageLoader類中建立一個加載圖片的方法loadImage()

//用來加載從start到end全部圖片
    public void loadImages(int start,int end){
        for (int i = start; i < end; i++) {
            String url  = NewsAdapter.URLS[i];
            //從緩存中取出對應的圖片
            Bitmap bitmap = getBitmapFromCache(url);
            if (bitmap == null) { //若是緩存中不存在,就下載
              NewsAsyncTask task = new NewsAsyncTask(url);
              task.execute(url);
              mTasks.add(task);
            }else { //若是存在,就從緩存中下載
                ImageView imageView = mListView.findViewWithTag(url);
                imageView.setImageBitmap(bitmap);
            }
        }

    }

在NewsAdpter類中添加全局變量mFirstIn,用來判別是否時第一次登錄

private boolean mFirstIn; //判斷是否第一次登錄

在他的構造方法中,初始化爲true

public NewsAdapter(Context context, List<NewsBean> mList, ListView listView) {
        ......
        ......
        mFirstIn = true;
    }

在他的onScroll()方法中進行判斷是否時第一次登錄

@Override
    public void onScroll(AbsListView absListView, int i, int i1, int i2) {
        mStart = i;
        mEnd = i+i1;
        if (mFirstIn && i1>0){
            imageLoader.loadImages(mStart,mEnd);
            mFirstIn = false;
        }
    }

最後了,翠花,此次別上酸菜了,把女兒紅上上來吧~~
ImageLoader.class

package com.iyuba.iyubanews;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.util.LruCache;
import android.widget.ImageView;
import android.widget.ListView;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;

public class ImageLoader {
    //全局變量
    private ImageView mImageView;

    //得到圖片地址
    private String mUrl;

    //建立Cache
    private LruCache<String,Bitmap> mCaches; //String 傳入的url

    //歷來註冊的listView
    private ListView mListView;

    //將全部下載任務放到集合中
    private Set<NewsAsyncTask> mTasks;

    /**
     * 構造方法
     */
    public ImageLoader(ListView mListView){

        this.mListView = mListView;

        mTasks = 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 Bitmap getBitmapFromCache(String url){
        return mCaches.get(url);
    }
    //增長到緩存
    public void addBitmapToCache(String url,Bitmap bitmap){
        if (getBitmapFromUrl(url) == null){ //校驗當前緩存是否存在
            mCaches.put(url,bitmap);
        }
    }

    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            mImageView.setImageBitmap((Bitmap) msg.obj);
        }
    };




    /**
     * 異步加載圖片方法
     * @param imageView
     * @param url
     */
    public void showImageByAsyncTask(ImageView imageView,String url){
        //先判斷一下當前緩存是否存在 從緩存中取出相應的圖片
        Bitmap bitmap = getBitmapFromCache(url);
        if (bitmap == null) { //若是緩存中不存在,就下載
            imageView.setImageResource(R.mipmap.ic_launcher);
        }else { //若是存在,就從緩存中下載
            imageView.setImageBitmap(bitmap);
        }
    }
    //用來加載從start到end全部圖片
    public void loadImages(int start,int end){
        for (int i = start; i < end; i++) {
            String url  = NewsAdapter.URLS[i];
            //從緩存中取出對應的圖片
            Bitmap bitmap = getBitmapFromCache(url);
            if (bitmap == null) { //若是緩存中不存在,就下載
              NewsAsyncTask task = new NewsAsyncTask(url);
              task.execute(url);
              mTasks.add(task);
            }else { //若是存在,就從緩存中下載
                ImageView imageView = mListView.findViewWithTag(url);
                imageView.setImageBitmap(bitmap);
            }
        }

    }
    /**
     * 經過多線程方法 下載圖片
     * @param imageView
     * @param url
     */
    public void showImageByThread(final ImageView imageView, final String url){
        new Thread(new Runnable() {
            @Override
            public void run() {
                mImageView = imageView;
                Bitmap bitmap = getBitmapFromUrl(url);
                Message message = Message.obtain();
                message.obj = bitmap;
                handler.sendMessage(message);
            }
        }).start();
    }



    /**
     * 下載圖片
     *
     * @param urlstring 圖片地址
     */
    public Bitmap getBitmapFromUrl(String urlstring) {
        Bitmap bitmap;
        InputStream is = null;
        mUrl = urlstring;
        try {
            URL url = new URL(urlstring);
            HttpURLConnection con = (HttpURLConnection) url.openConnection();
            is = new BufferedInputStream(con.getInputStream());
            bitmap = BitmapFactory.decodeStream(is);
            con.disconnect();
            return bitmap;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

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


    /**
     * 異步加載圖片類
     */
    private class NewsAsyncTask extends AsyncTask<String,Void,Bitmap>{
        private ImageView mImageView;
        private String mUrl;
        public NewsAsyncTask(String url){
            mUrl = url;
        }
        @Override
        protected Bitmap doInBackground(String... strings) {
            String url = strings[0];
            //從網絡獲取圖片
            Bitmap bitmap = getBitmapFromUrl(strings[0]);
            if (bitmap!=null){//確實下載到了圖片
                //將不在緩存的圖片加入緩存,從而實現緩存效果
                addBitmapToCache(url,bitmap);
            }
            return bitmap;
        }
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            ImageView imageView = mListView.findViewWithTag(mUrl);
            if (imageView != null && bitmap!=null) { //若是圖片存在,而且照片已下載
                imageView.setImageBitmap(bitmap);

            }
            mTasks.remove(this);
        }
    }
}

NewsAdaper.class

package com.iyuba.iyubanews;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import java.util.List;

public class NewsAdapter extends BaseAdapter implements AbsListView.OnScrollListener {
    private List<NewsBean> mList;
    private LayoutInflater mInflater;
    private ImageLoader imageLoader;
    private int mStart,mEnd;
    public static String[] URLS; //建立靜態數組保存全部圖片的連接
    private boolean mFirstIn; //判斷是否第一次登錄

    public NewsAdapter(Context context, List<NewsBean> mList, ListView listView) {
        mInflater = LayoutInflater.from(context);
        this.mList = mList;
        imageLoader = new ImageLoader(listView);
        URLS = new String[mList.size()];
        for (int i = 0; i < mList.size(); i++) {
            URLS[i] = mList.get(i).newsUrl;
        }
        listView.setOnScrollListener(this);
        mFirstIn = true;
    }


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

    @Override
    public Object getItem(int i) {
        return mList.get(i);
    }

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

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {

        ViewHolder holder = null;
        if (view==null){ //若是爲空,從新建立
            holder = new ViewHolder();
            view = mInflater.inflate(R.layout.item_layout,null);
            holder.tv_title = view.findViewById(R.id.tv_title);
            holder.tv_content = view.findViewById(R.id.tv_content);
            holder.iv_icon = view.findViewById(R.id.iv_icon);
            view.setTag(holder);
        }else { //若是不爲空,從以前建立中獲取
            holder = (ViewHolder) view.getTag();
        }
        holder.tv_content.setText(mList.get(i).newsContent);
        holder.tv_title.setText(mList.get(i).newsTitle);
        //holder.iv_icon.setImageResource(R.mipmap.ic_launcher);
        holder.iv_icon.setTag(mList.get(i).newsUrl);
        imageLoader.loadImages(mStart,mEnd);
        return view;
    }

    @Override
    public void onScrollStateChanged(AbsListView absListView, int i) {
        if (i == SCROLL_STATE_IDLE){
            //加載可見項
            imageLoader.loadImages(mStart,mEnd);
        }else{
            //中止加載可見項
            imageLoader.cancelAllTasks();
        }
    }

    @Override
    public void onScroll(AbsListView absListView, int i, int i1, int i2) {
        mStart = i;
        mEnd = i+i1;
        if (mFirstIn && i1>0){
            imageLoader.loadImages(mStart,mEnd);
            mFirstIn = false;
        }
    }

    class ViewHolder{
        public TextView tv_title; //標題
        public TextView tv_content;//內容
        public ImageView iv_icon; //圖片
    }
}

總結:

經過異步加載,避免堵塞UI線程 經過LruCache,將已下載圖片放到內存中 經過判斷ListView滑動狀態,決定什麼時候加載圖片 不只僅是ListView,任何控件均可以使用異步加載

相關文章
相關標籤/搜索