參考文章:解析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,任何控件均可以使用異步加載