由於移動端軟件開發思惟模式或者說是開發的架構實際上是不分平臺和編程語言的,就拿安卓和IOS來講,他們都是移動前端app開發展現數據和用戶交互數據的數據終端,移動架構的幾個大模塊:UI界面展現、本地數據可持續化存儲、網絡數據請求、性能優化等等,安卓和IOS開發都要考慮這些架構的模塊。因此,熟悉IOS的開發的人,再去學習一下安卓的開發以及安卓的開發模式,你會發現不少技術和思想安卓和IOS是同樣的,只是可能說法不同,因爲編程語言好比OC和Java略微的差別性,編碼習慣和細節不同以外,其餘都是同樣的。前端
本人開始對安卓略有興趣,開始對安卓粗淺的學習,一方面也會拿IOS和安卓進行對比闡述,若是你會IOS,再學習安卓的,閱讀本人的博客也許會對你有很大的幫助。java
(可是對於安卓開發的語言基礎Java、以及android studio的使用,本人不會詳細闡述,做爲有情懷有獨立能力的程序員,這些基礎應該不會難道大家的吧,更況且本人對android studio的使用一直經過google和百度來學習相關的setting和快捷鍵)android
在Android中,異步加載最經常使用的兩種方式:git
一、多線程\線程池程序員
二、AsyncTaskgithub
固然,AsyncTask底層是基於線程池實現的。因此以上兩種方法是殊途同歸。算法
1、首先,按照在IOS中用UITableView加載數據的思路同樣,咱們先要建立出自定義的Cell以及Cell的佈局:編程
新建item_layout.xml文件:json
源碼:api
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="wrap_content" 5 android:padding="4dp" 6 android:orientation="horizontal" 7 > 8 <ImageView 9 android:id="@+id/iv_icon" 10 android:layout_width="64dp" 11 android:layout_height="64dp" 12 android:src="@mipmap/ic_launcher"/> 13 14 15 <LinearLayout 16 android:layout_width="match_parent" 17 android:layout_height="match_parent" 18 android:padding="4dp" 19 android:gravity="center" 20 android:orientation="vertical"> 21 22 <TextView 23 android:id="@+id/tv_title" 24 android:layout_width="match_parent" 25 android:layout_height="wrap_content" 26 android:textSize="15sp" 27 android:maxLines="1" 28 android:text="標題標題標題"/> 29 30 31 <TextView 32 android:id="@+id/tv_content" 33 android:layout_width="match_parent" 34 android:layout_height="wrap_content" 35 android:textSize="10sp" 36 android:maxLines="3" 37 android:text="內容內容內容"/> 38 39 </LinearLayout> 40 41 42 </LinearLayout>
就這樣,一個自定義的item就建立好了,就比如咱們IOS中xib或者storyboard上的UITableViewCell建立好了。
而後接着在activity_main.xml中建立一個ListView,這個ListView就比如咱們IOS的UITableView。
源碼:
1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:tools="http://schemas.android.com/tools" 4 android:id="@+id/activity_main" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent" 7 android:paddingBottom="@dimen/activity_vertical_margin" 8 android:paddingLeft="@dimen/activity_horizontal_margin" 9 android:paddingRight="@dimen/activity_horizontal_margin" 10 android:paddingTop="@dimen/activity_vertical_margin" 11 tools:context="com.example.heyang.myapplication.MainActivity"> 12 13 <ListView 14 android:id="@+id/lv_main" 15 android:layout_width="match_parent" 16 android:layout_height="match_parent" 17 /> 18 19 20 </RelativeLayout>
2、由於每個item或者類比IOS的每個Cell都須要一個圖片地址、標題Title、內容Content三個數據,因此咱們就須要一個模型對象來一一映射對應到item,在安卓或者Java中的說法叫建立一個Bean對象,其實能夠類比理解爲IOS的model模型對象。
源碼:
1 package com.example.heyang.myapplication; 2 3 /** 4 * Created by HeYang on 16/10/5. 5 */ 6 7 public class NewsBean { 8 // 包含三個屬性:一、標題二、內容三、圖片的網址 9 public String newsIconURL; 10 public String newsTitle; 11 public String newsContent; 12 }
3、接着就是要數據源了,http://www.imooc.com/api/teacher?type=4&num=30,點擊這個URL打開網頁會看到一大堆數據,而後你能夠經過json格式轉換工具就能夠看到json的數據格式。
那麼接下來就很容易明白了,模型Bean對象中的三個屬性就能夠來自這個URL下的json數據中的name、picSmall、discription。
那麼接下來就是IOS中所謂的字典轉模型的步驟。只不過在這以前,先要經過網絡請求獲取到這些數據才行,這裏網絡請求獲取json數據的作法有點和IOS的不一樣了。
感受IOS的網絡請求,無論是蘋果的NSURLSession仍是第三方的AFN框架都已是封裝好的網絡請求框架。而安卓的獲取json數據的作法好像更接近底層,採用了輸入輸出流來獲取URL的網頁數據:
1 package com.example.heyang.myapplication; 2 3 import android.os.AsyncTask; 4 import android.support.v7.app.AppCompatActivity; 5 import android.os.Bundle; 6 import android.util.Log; 7 import android.widget.ListView; 8 9 import org.json.JSONArray; 10 import org.json.JSONException; 11 import org.json.JSONObject; 12 13 import java.io.BufferedReader; 14 import java.io.IOException; 15 import java.io.InputStream; 16 import java.io.InputStreamReader; 17 import java.io.UnsupportedEncodingException; 18 import java.net.URL; 19 import java.util.ArrayList; 20 import java.util.List; 21 22 public class MainActivity extends AppCompatActivity { 23 24 private ListView mListView; 25 26 private static String URL = "http://www.imooc.com/api/teacher?type=4&num=30"; 27 28 @Override 29 protected void onCreate(Bundle savedInstanceState) { 30 super.onCreate(savedInstanceState); 31 setContentView(R.layout.activity_main); 32 33 // 獲取xml上的ListView對象 34 mListView = (ListView) findViewById(R.id.lv_main); 35 36 new NewsAsyncTask().execute(URL); 37 } 38 39 // 經過輸入輸出流獲取整個網頁格式的字符串數據 40 private String readStream(InputStream is){ 41 InputStreamReader isr; 42 String result = ""; 43 44 try { 45 String line = ""; 46 // 一、輸入流對象 二、輸入流讀取對象 三、字節讀取對象 47 isr = new InputStreamReader(is,"utf-8"); 48 BufferedReader br = new BufferedReader(isr); 49 while ((line = br.readLine()) != null){ 50 result += line; 51 } 52 } catch(UnsupportedEncodingException e){ 53 e.printStackTrace(); 54 } catch (IOException e) { 55 e.printStackTrace(); 56 } 57 return result; 58 } 59 60 61 private List<NewsBean> getJsonData(String url){ 62 // 建立存儲NewsBean的集合對象 63 List<NewsBean> newsBeanList = new ArrayList<>(); 64 try { 65 // 取出網絡的json字符串的格式以後 66 String jsonStr = readStream(new URL(url).openStream()); 67 // 就要用JSONObject對象進行解析 68 JSONObject jsonObject; 69 // 而後須要NewsBean,其實至關於IOS的模型對象 70 NewsBean newsBean; 71 try { 72 // jsonObject的對象,建立該對象的同時傳入json字符串格式的對象 73 jsonObject = new JSONObject(jsonStr); 74 // 拿到jsonObject對象以後,就須要經過key值來拿到數組 75 JSONArray jsonArray = jsonObject.getJSONArray("data"); 76 // 而後開始遍歷數組,獲取模型數組 77 for (int i = 0;i<jsonArray.length();i++){ 78 // 數組裏每個元素又是jsonObject 79 jsonObject = jsonArray.getJSONObject(i); 80 81 // 開始建立模型對象 82 newsBean = new NewsBean(); 83 newsBean.newsIconURL = jsonObject.getString("picSmall"); 84 newsBean.newsTitle = jsonObject.getString("name"); 85 newsBean.newsContent = jsonObject.getString("description"); 86 87 // 建立的一個模型對象,就要添加到集合當中去 88 newsBeanList.add(newsBean); 89 } 90 91 92 } catch (JSONException e) { 93 e.printStackTrace(); 94 } 95 96 Log.d("heyang",jsonStr); 97 } catch (IOException e) { 98 e.printStackTrace(); 99 } 100 101 return newsBeanList; 102 } 103 104 // 建立一個內部類來實現 ,在實現下面內部類以前,須要自定義的Bean對象來封裝處理Josn格式的數據 105 class NewsAsyncTask extends AsyncTask<String,Void,List<NewsBean>>{ 106 @Override 107 protected List<NewsBean> doInBackground(String... strings) { 108 return getJsonData(strings[0]); 109 } 110 } 111 }
按照以上的源碼,就能順利的獲取到了ListView的數據源數組。
可是在網絡請求方面,別忘了要給項目增長Internet訪問權限:
<!--增長 網絡訪問權限 -->
<uses-permission android:name="android.permission.INTERNET"/>
4、建立繼承BaseAdapter自定義的Adapter,注意其中利用了匿名內部類來優化處理ListView的每個view的循環利用:
這裏要補充一下,安卓加載ListView用到了適配器模式,因此須要下面自定義的Adapter對象,這個和咱們IOS開發的加載UITableView用到的一些列代理方法的代理模式仍是有區別的。不過,若是讀者學習了適配器模式,將會對安卓底層用到的適配器模式就會有所理解了。
1 package com.example.heyang.myapplication; 2 3 import android.content.Context; 4 import android.view.LayoutInflater; 5 import android.view.View; 6 import android.view.ViewGroup; 7 import android.widget.BaseAdapter; 8 import android.widget.ImageView; 9 import android.widget.TextView; 10 11 import java.util.List; 12 13 /** 14 * Created by HeYang on 16/10/6. 15 */ 16 17 public class NewsAdapter extends BaseAdapter { 18 19 // 適配器對象須要傳入Bean數據集合對象,相似IOS的模型數組集合 20 private List<NewsBean> beanList; 21 // 而後要傳入LayoutInflater對象,用來獲取xml文件的視圖控件 22 private LayoutInflater layoutInflater; 23 24 // 建立構造方法 25 public NewsAdapter(MainActivity context, List<NewsBean> data){ 26 beanList = data; 27 layoutInflater = LayoutInflater.from(context);// 這個context對象就是Activity對象 28 } 29 30 @Override 31 public int getCount() { 32 return beanList.size(); 33 } 34 35 @Override 36 public Object getItem(int i) { 37 // 由於beanList是數組,經過get訪問對應index的元素 38 return beanList.get(i); 39 } 40 41 @Override 42 public long getItemId(int i) { 43 return i; 44 } 45 46 @Override 47 public View getView(int i, View view, ViewGroup viewGroup) { 48 ViewHolder viewHolder = null; 49 if (view == null){ 50 viewHolder = new ViewHolder(); 51 // 每個View都要和layout關聯 52 view = layoutInflater.inflate(R.layout.item_layout,null); 53 // 在R.layout.item_layout中有三個控件對象 54 // 如今所有傳遞給view對象了 55 viewHolder.tvTitle = (TextView) view.findViewById(R.id.tv_title); 56 viewHolder.tvContent = (TextView) view.findViewById(R.id.tv_content); 57 viewHolder.ivIcon = (ImageView) view.findViewById(R.id.iv_icon); 58 view.setTag(viewHolder); 59 60 }else { 61 62 // 重複利用,可是因爲裏面的View展現的數據顯然須要從新賦值 63 viewHolder = (ViewHolder) view.getTag(); 64 } 65 viewHolder.tvTitle.setText(beanList.get(i).newsTitle); 66 viewHolder.tvContent.setText(beanList.get(i).newsContent); 67 // 先默認加載系統圖片 68 viewHolder.ivIcon.setImageResource(R.mipmap.ic_launcher); 69 70 return view; 71 } 72 73 // 最後須要一個匿名內部類來建立一個臨時緩存View的對象 74 class ViewHolder{ 75 public TextView tvContent,tvTitle; 76 public ImageView ivIcon; 77 } 78 }
5、最後優化和完善MainActivity對象
1 package com.example.heyang.myapplication; 2 3 import android.os.AsyncTask; 4 import android.support.v7.app.AppCompatActivity; 5 import android.os.Bundle; 6 import android.util.Log; 7 import android.widget.ListView; 8 9 import org.json.JSONArray; 10 import org.json.JSONException; 11 import org.json.JSONObject; 12 13 import java.io.BufferedReader; 14 import java.io.IOException; 15 import java.io.InputStream; 16 import java.io.InputStreamReader; 17 import java.io.UnsupportedEncodingException; 18 import java.net.URL; 19 import java.util.ArrayList; 20 import java.util.List; 21 22 public class MainActivity extends AppCompatActivity { 23 24 private ListView mListView; 25 26 private static String URL = "http://www.imooc.com/api/teacher?type=4&num=30"; 27 28 @Override 29 protected void onCreate(Bundle savedInstanceState) { 30 super.onCreate(savedInstanceState); 31 setContentView(R.layout.activity_main); 32 33 // 獲取xml上的ListView對象 34 mListView = (ListView) findViewById(R.id.lv_main); 35 36 new NewsAsyncTask().execute(URL); 37 } 38 39 // 經過輸入輸出流獲取整個網頁格式的字符串數據 40 private String readStream(InputStream is){ 41 InputStreamReader isr; 42 String result = ""; 43 44 try { 45 String line = ""; 46 // 一、輸入流對象 二、輸入流讀取對象 三、字節讀取對象 47 isr = new InputStreamReader(is,"utf-8"); 48 BufferedReader br = new BufferedReader(isr); 49 while ((line = br.readLine()) != null){ 50 result += line; 51 } 52 } catch(UnsupportedEncodingException e){ 53 e.printStackTrace(); 54 } catch (IOException e) { 55 e.printStackTrace(); 56 } 57 return result; 58 } 59 60 61 private List<NewsBean> getJsonData(String url){ 62 // 建立存儲NewsBean的集合對象 63 List<NewsBean> newsBeanList = new ArrayList<>(); 64 try { 65 // 取出網絡的json字符串的格式以後 66 String jsonStr = readStream(new URL(url).openStream()); 67 // 就要用JSONObject對象進行解析 68 JSONObject jsonObject; 69 // 而後須要NewsBean,其實至關於IOS的模型對象 70 NewsBean newsBean; 71 try { 72 // jsonObject的對象,建立該對象的同時傳入json字符串格式的對象 73 jsonObject = new JSONObject(jsonStr); 74 // 拿到jsonObject對象以後,就須要經過key值來拿到數組 75 JSONArray jsonArray = jsonObject.getJSONArray("data"); 76 // 而後開始遍歷數組,獲取模型數組 77 for (int i = 0;i<jsonArray.length();i++){ 78 // 數組裏每個元素又是jsonObject 79 jsonObject = jsonArray.getJSONObject(i); 80 81 // 開始建立模型對象 82 newsBean = new NewsBean(); 83 newsBean.newsIconURL = jsonObject.getString("picSmall"); 84 newsBean.newsTitle = jsonObject.getString("name"); 85 newsBean.newsContent = jsonObject.getString("description"); 86 87 // 建立的一個模型對象,就要添加到集合當中去 88 newsBeanList.add(newsBean); 89 } 90 91 92 } catch (JSONException e) { 93 e.printStackTrace(); 94 } 95 96 Log.d("heyang",jsonStr); 97 } catch (IOException e) { 98 e.printStackTrace(); 99 } 100 101 return newsBeanList; 102 } 103 104 // 建立一個內部類來實現 ,在實現下面內部類以前,須要自定義的Bean對象來封裝處理Josn格式的數據 105 class NewsAsyncTask extends AsyncTask<String,Void,List<NewsBean>>{ 106 @Override 107 protected List<NewsBean> doInBackground(String... strings) { 108 return getJsonData(strings[0]); 109 } 110 111 @Override 112 protected void onPostExecute(List<NewsBean> newsBeen) { 113 super.onPostExecute(newsBeen); 114 NewsAdapter newsAdapter = new NewsAdapter(MainActivity.this,newsBeen); 115 mListView.setAdapter(newsAdapter); 116 117 } 118 } 119 }
好,運行一下模擬器看看結果:
6、下面採用兩種方法進行加載圖片:①多線程加載圖片 ②AsyncTask
①多線程加載圖片:
建立一個普通的Class類:ImageLoader,在內部使用線程run運行執行ImageView加載網絡圖片的邏輯
1 package com.example.heyang.myapplication; 2 3 import android.graphics.Bitmap; 4 import android.graphics.BitmapFactory; 5 import android.os.Handler; 6 import android.os.Message; 7 import android.widget.ImageView; 8 9 import java.io.BufferedInputStream; 10 import java.io.IOException; 11 import java.io.InputStream; 12 import java.net.HttpURLConnection; 13 import java.net.MalformedURLException; 14 import java.net.URL; 15 16 /** 17 * Created by HeYang on 16/10/6. 18 */ 19 20 public class ImageLoader { 21 22 private ImageView mImageView; 23 private String mURLStr; 24 25 private Handler handler = new Handler(){ 26 @Override 27 public void handleMessage(Message msg) { 28 super.handleMessage(msg); 29 30 if (mImageView.getTag().equals(mURLStr)){ 31 mImageView.setImageBitmap((Bitmap) msg.obj); 32 } 33 34 35 } 36 }; 37 38 public void showImageByThread(ImageView imageView, final String urlString){ 39 40 mImageView = imageView; 41 mURLStr = urlString; 42 43 new Thread(){ 44 @Override 45 public void run() { 46 super.run(); 47 Bitmap bitmap = getBitmapFromURL(urlString); 48 // 當前線程是子線程,並非UI主線程 49 // 不是Message message = new Message(); 50 Message message = Message.obtain(); 51 message.obj = bitmap; 52 handler.sendMessage(message); 53 54 } 55 }.start(); 56 } 57 58 public Bitmap getBitmapFromURL(String urlString){ 59 Bitmap bitmap = null; 60 InputStream is = null; 61 62 try { 63 URL url = new URL(urlString); 64 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 65 is = new BufferedInputStream(connection.getInputStream()); 66 bitmap = BitmapFactory.decodeStream(is); 67 // 最後要關閉http鏈接 68 connection.disconnect(); 69 Thread.sleep(1000);// 睡眠1秒 70 } catch (IOException e) { 71 e.printStackTrace(); 72 } catch (InterruptedException e) { 73 e.printStackTrace(); 74 } finally { 75 76 try { 77 is.close(); 78 } catch (IOException e) { 79 e.printStackTrace(); 80 } 81 } 82 83 return bitmap; 84 } 85 }
而後修改一下以前的NewsAdapter的代碼:
1 package com.example.heyang.myapplication; 2 3 import android.content.Context; 4 import android.view.LayoutInflater; 5 import android.view.View; 6 import android.view.ViewGroup; 7 import android.widget.BaseAdapter; 8 import android.widget.ImageView; 9 import android.widget.TextView; 10 11 import java.util.List; 12 13 /** 14 * Created by HeYang on 16/10/6. 15 */ 16 17 public class NewsAdapter extends BaseAdapter { 18 19 // 適配器對象須要傳入Bean數據集合對象,相似IOS的模型數組集合 20 private List<NewsBean> beanList; 21 // 而後要傳入LayoutInflater對象,用來獲取xml文件的視圖控件 22 private LayoutInflater layoutInflater; 23 24 // 建立構造方法 25 public NewsAdapter(MainActivity context, List<NewsBean> data){ 26 beanList = data; 27 layoutInflater = LayoutInflater.from(context);// 這個context對象就是Activity對象 28 } 29 30 @Override 31 public int getCount() { 32 return beanList.size(); 33 } 34 35 @Override 36 public Object getItem(int i) { 37 // 由於beanList是數組,經過get訪問對應index的元素 38 return beanList.get(i); 39 } 40 41 @Override 42 public long getItemId(int i) { 43 return i; 44 } 45 46 @Override 47 public View getView(int i, View view, ViewGroup viewGroup) { 48 ViewHolder viewHolder = null; 49 if (view == null){ 50 viewHolder = new ViewHolder(); 51 // 每個View都要和layout關聯 52 view = layoutInflater.inflate(R.layout.item_layout,null); 53 // 在R.layout.item_layout中有三個控件對象 54 // 如今所有傳遞給view對象了 55 viewHolder.tvTitle = (TextView) view.findViewById(R.id.tv_title); 56 viewHolder.tvContent = (TextView) view.findViewById(R.id.tv_content); 57 viewHolder.ivIcon = (ImageView) view.findViewById(R.id.iv_icon); 58 59 view.setTag(viewHolder); 60 61 }else { 62 63 // 重複利用,可是因爲裏面的View展現的數據顯然須要從新賦值 64 viewHolder = (ViewHolder) view.getTag(); 65 } 66 viewHolder.tvTitle.setText(beanList.get(i).newsTitle); 67 viewHolder.tvContent.setText(beanList.get(i).newsContent); 68 // 先默認加載系統圖片 69 viewHolder.ivIcon.setImageResource(R.mipmap.ic_launcher); // 相似加載佔位圖片 70 viewHolder.ivIcon.setTag(beanList.get(i).newsIconURL); 71 // 將ImageView對象和URLSting對象傳入進去 72 new ImageLoader().showImageByThread(viewHolder.ivIcon,beanList.get(i).newsIconURL); 73 74 return view; 75 } 76 77 // 最後須要一個匿名內部類來建立一個臨時緩存View的對象 78 class ViewHolder{ 79 public TextView tvContent,tvTitle; 80 public ImageView ivIcon; 81 } 82 }
注意,其中使用了viewHolder.ivIcon.setTag(beanList.get(i).newsIconURL);做爲惟一標示傳遞給ImageLoader內部作判斷,這樣加載ListView不會出現圖片加載和緩存衝突了現象。(源碼中保留了Thread.sleep(1000)能夠查看到效果)。
②AsyncTask
接着,使用AsyncTask異步加載,在imageLoader對象的基礎上繼續:
完整源碼:
1 package com.example.heyang.myapplication; 2 3 import android.graphics.Bitmap; 4 import android.graphics.BitmapFactory; 5 import android.os.AsyncTask; 6 import android.os.Handler; 7 import android.os.Message; 8 import android.widget.ImageView; 9 10 import java.io.BufferedInputStream; 11 import java.io.IOException; 12 import java.io.InputStream; 13 import java.net.HttpURLConnection; 14 import java.net.MalformedURLException; 15 import java.net.URL; 16 17 /** 18 * Created by HeYang on 16/10/6. 19 */ 20 21 public class ImageLoader { 22 23 private ImageView mImageView; 24 private String mURLStr; 25 26 private Handler handler = new Handler(){ 27 @Override 28 public void handleMessage(Message msg) { 29 super.handleMessage(msg); 30 31 if (mImageView.getTag().equals(mURLStr)){ 32 mImageView.setImageBitmap((Bitmap) msg.obj); 33 } 34 35 36 } 37 }; 38 39 public void showImageByThread(ImageView imageView, final String urlString){ 40 41 mImageView = imageView; 42 mURLStr = urlString; 43 44 new Thread(){ 45 @Override 46 public void run() { 47 super.run(); 48 Bitmap bitmap = getBitmapFromURL(urlString); 49 // 當前線程是子線程,並非UI主線程 50 // 不是Message message = new Message(); 51 Message message = Message.obtain(); 52 message.obj = bitmap; 53 handler.sendMessage(message); 54 55 } 56 }.start(); 57 } 58 59 public Bitmap getBitmapFromURL(String urlString){ 60 Bitmap bitmap = null; 61 InputStream is = null; 62 63 try { 64 URL url = new URL(urlString); 65 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 66 is = new BufferedInputStream(connection.getInputStream()); 67 bitmap = BitmapFactory.decodeStream(is); 68 // 最後要關閉http鏈接 69 connection.disconnect(); 70 Thread.sleep(1000);// 睡眠1秒 71 } catch (IOException e) { 72 e.printStackTrace(); 73 } catch (InterruptedException e) { 74 e.printStackTrace(); 75 } finally { 76 77 try { 78 is.close(); 79 } catch (IOException e) { 80 e.printStackTrace(); 81 } 82 } 83 84 return bitmap; 85 } 86 87 88 // ==================使用AsyncTask==================== 89 public void showImageByAsyncTask(ImageView imageView, final String urlString){ 90 new NewsAsyncTask(imageView, (String) imageView.getTag()).execute(urlString);// 這兩個參數分別傳遞的目的地能夠理解一下 91 } 92 93 private class NewsAsyncTask extends AsyncTask<String,Void,Bitmap>{ 94 95 // 須要私有的ImageView對象和構造方法來傳遞ImageView對象 96 private ImageView mImageView; 97 private String urlString; 98 99 public NewsAsyncTask(ImageView imageView,String urlString){ 100 mImageView = imageView; 101 urlString = urlString; 102 } 103 104 @Override 105 protected Bitmap doInBackground(String... strings) { 106 // 在這個方法中,完成異步下載的任務 107 getBitmapFromURL(strings[0]); 108 return null; 109 } 110 111 112 @Override 113 protected void onPostExecute(Bitmap bitmap) { 114 super.onPostExecute(bitmap); 115 // 而後在這個方法中設置imageViewd 116 if (mImageView.getTag().equals(urlString)){ 117 mImageView.setImageBitmap(bitmap); 118 } 119 120 } 121 } 122 123 }
而後在NewsAdapter裏修改一下:
而後運行效果和上面多線程的效果同樣,這裏就再也不重複展現了。
7、LruCache緩存處理
爲了提升用戶體驗,因此須要使用緩存機制。這裏安卓提供了Lru算法處理緩存。
Lru:Least Recently Used 近期最少使用算法。
因此,咱們須要在ImageLoader中建立LruCache對象:
完整源碼:
1 package com.example.heyang.myapplication; 2 3 import android.annotation.TargetApi; 4 import android.graphics.Bitmap; 5 import android.graphics.BitmapFactory; 6 import android.os.AsyncTask; 7 import android.os.Build; 8 import android.os.Handler; 9 import android.os.Message; 10 import android.support.annotation.RequiresApi; 11 import android.util.Log; 12 import android.util.LruCache; 13 import android.widget.ImageView; 14 15 import java.io.BufferedInputStream; 16 import java.io.IOException; 17 import java.io.InputStream; 18 import java.net.HttpURLConnection; 19 import java.net.MalformedURLException; 20 import java.net.URL; 21 22 /** 23 * Created by HeYang on 16/10/6. 24 */ 25 26 public class ImageLoader { 27 28 private ImageView mImageView; 29 private String mURLStr; 30 // 建立緩存對象 31 // 第一個參數是須要緩存對象的名字或者ID,這裏咱們傳輸url做爲惟一名字便可 32 // 第二個參數是Bitmap對象 33 private LruCache<String,Bitmap> lruCache; 34 35 // 而後咱們須要在構造方法中初始化這個緩存對象 36 // 另外,咱們不可能把全部的緩存空間拿來用 37 @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB_MR1) 38 public ImageLoader(){ 39 40 // 獲取最大可用內存 41 int maxMemory = (int) Runtime.getRuntime().maxMemory(); 42 int cachaSize = maxMemory / 4; 43 // 建立LruCache對象,同時用匿名內部類的方式重寫方法 44 lruCache = new LruCache<String,Bitmap>(cachaSize){ 45 @Override 46 protected int sizeOf(String key, Bitmap value) { 47 // 咱們須要直接返回Bitmap value的實際大小 48 //return super.sizeOf(key, value); 49 // 在每次存入緩存的時候調用 50 return value.getByteCount(); 51 } 52 }; 53 } 54 55 // 而後咱們要寫倆個方法:一、將bitmap存入緩存中 二、從緩存中取出bitmap 56 57 @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) 58 public void addBitmapToCache(String url, Bitmap bitmap){ 59 if (getBitMapFromCache(url) == null){ 60 lruCache.put(url,bitmap); 61 } 62 } 63 64 @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) 65 public Bitmap getBitMapFromCache(String url){ 66 return lruCache.get(url); 67 } 68 69 70 private Handler handler = new Handler(){ 71 @Override 72 public void handleMessage(Message msg) { 73 super.handleMessage(msg); 74 75 if (mImageView.getTag().equals(mURLStr)){ 76 mImageView.setImageBitmap((Bitmap) msg.obj); 77 } 78 79 80 } 81 }; 82 83 public void showImageByThread(ImageView imageView, final String urlString){ 84 85 mImageView = imageView; 86 mURLStr = urlString; 87 88 new Thread(){ 89 @Override 90 public void run() { 91 super.run(); 92 Bitmap bitmap = getBitmapFromURL(urlString); 93 // 當前線程是子線程,並非UI主線程 94 // 不是Message message = new Message(); 95 Message message = Message.obtain(); 96 message.obj = bitmap; 97 handler.sendMessage(message); 98 99 } 100 }.start(); 101 } 102 103 public Bitmap getBitmapFromURL(String urlString){ 104 Bitmap bitmap = null; 105 InputStream is = null; 106 107 try { 108 URL url = new URL(urlString); 109 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 110 is = new BufferedInputStream(connection.getInputStream()); 111 bitmap = BitmapFactory.decodeStream(is); 112 // 最後要關閉http鏈接 113 connection.disconnect(); 114 // Thread.sleep(1000);// 睡眠1秒 115 } catch (IOException e) { 116 e.printStackTrace(); 117 } 118 // catch (InterruptedException e) { 119 // e.printStackTrace(); 120 // } 121 finally { 122 123 try { 124 is.close(); 125 } catch (IOException e) { 126 e.printStackTrace(); 127 } 128 } 129 130 return bitmap; 131 } 132 133 134 // ==================使用AsyncTask==================== 135 public void showImageByAsyncTask(ImageView imageView, final String urlString){ 136 // 在異步請求以前,先判斷緩存中是否有,有的話就取出直接加載 137 Bitmap bitmap = getBitMapFromCache(urlString); 138 if (bitmap == null){ 139 // 若是沒有,就異步任務加劇 140 new NewsAsyncTask(imageView, (String) imageView.getTag()).execute(urlString);// 這兩個參數分別傳遞的目的地能夠理解一下 141 }else{ 142 imageView.setImageBitmap(bitmap); 143 } 144 145 } 146 147 private class NewsAsyncTask extends AsyncTask<String,Void,Bitmap>{ 148 149 // 須要私有的ImageView對象和構造方法來傳遞ImageView對象 150 private ImageView mImageView; 151 private String mURlString; 152 153 public NewsAsyncTask(ImageView imageView,String urlString){ 154 mImageView = imageView; 155 mURlString = urlString; 156 } 157 158 @Override 159 protected Bitmap doInBackground(String... strings) { 160 // 在這個方法中,完成異步下載的任務 161 String url = strings[0]; 162 // 從網絡中獲取圖片 163 Bitmap bitmap = getBitmapFromURL(strings[0]); 164 if (bitmap != null){ 165 // 將網絡加載出來的圖片存儲緩存 166 addBitmapToCache(url,bitmap); 167 } 168 return bitmap; 169 } 170 171 172 @Override 173 protected void onPostExecute(Bitmap bitmap) { 174 super.onPostExecute(bitmap); 175 // 而後在這個方法中設置imageViewd 176 177 if (mImageView.getTag().equals(mURlString)){ 178 mImageView.setImageBitmap(bitmap); 179 } 180 181 } 182 } 183 }
還須要在NewsAdapter類裏作一個小小的優化,由於適配器的getView是個不斷被調用的方法,就比如UITableView建立Cell的代理方法,會被不斷的被調用。而在getView方法中,ImageLoader的構造方法會被不斷的被調用,這樣的話,會形成ImageLoader構造方法中的LruCache對象不斷的被建立,這樣顯然是很差的。因此咱們須要用一個引用,指向一開始就建立好的ImageLoader對象,而後屢次使用。
而後
完整源碼:
1 package com.example.heyang.myapplication; 2 3 import android.annotation.TargetApi; 4 import android.content.Context; 5 import android.os.Build; 6 import android.support.annotation.RequiresApi; 7 import android.view.LayoutInflater; 8 import android.view.View; 9 import android.view.ViewGroup; 10 import android.widget.BaseAdapter; 11 import android.widget.ImageView; 12 import android.widget.TextView; 13 14 import java.util.List; 15 16 /** 17 * Created by HeYang on 16/10/6. 18 */ 19 20 public class NewsAdapter extends BaseAdapter { 21 22 // 適配器對象須要傳入Bean數據集合對象,相似IOS的模型數組集合 23 private List<NewsBean> beanList; 24 // 而後要傳入LayoutInflater對象,用來獲取xml文件的視圖控件 25 private LayoutInflater layoutInflater; 26 27 private ImageLoader imageLoader; 28 29 // 建立構造方法 30 31 @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) 32 public NewsAdapter(MainActivity context, List<NewsBean> data){ 33 beanList = data; 34 layoutInflater = LayoutInflater.from(context);// 這個context對象就是Activity對象 35 imageLoader = new ImageLoader(); 36 } 37 38 @Override 39 public int getCount() { 40 return beanList.size(); 41 } 42 43 @Override 44 public Object getItem(int i) { 45 // 由於beanList是數組,經過get訪問對應index的元素 46 return beanList.get(i); 47 } 48 49 @Override 50 public long getItemId(int i) { 51 return i; 52 } 53 54 55 @Override 56 public View getView(int i, View view, ViewGroup viewGroup) { 57 ViewHolder viewHolder = null; 58 if (view == null){ 59 viewHolder = new ViewHolder(); 60 // 每個View都要和layout關聯 61 view = layoutInflater.inflate(R.layout.item_layout,null); 62 // 在R.layout.item_layout中有三個控件對象 63 // 如今所有傳遞給view對象了 64 viewHolder.tvTitle = (TextView) view.findViewById(R.id.tv_title); 65 viewHolder.tvContent = (TextView) view.findViewById(R.id.tv_content); 66 viewHolder.ivIcon = (ImageView) view.findViewById(R.id.iv_icon); 67 68 view.setTag(viewHolder); 69 70 }else { 71 72 // 重複利用,可是因爲裏面的View展現的數據顯然須要從新賦值 73 viewHolder = (ViewHolder) view.getTag(); 74 } 75 viewHolder.tvTitle.setText(beanList.get(i).newsTitle); 76 viewHolder.tvContent.setText(beanList.get(i).newsContent); 77 // 先默認加載系統圖片 78 viewHolder.ivIcon.setImageResource(R.mipmap.ic_launcher); // 相似加載佔位圖片 79 viewHolder.ivIcon.setTag(beanList.get(i).newsIconURL); 80 // 將ImageView對象和URLSting對象傳入進去 81 // new ImageLoader().showImageByThread(viewHolder.ivIcon,beanList.get(i).newsIconURL); 82 imageLoader.showImageByAsyncTask(viewHolder.ivIcon,beanList.get(i).newsIconURL); 83 return view; 84 } 85 86 // 最後須要一個匿名內部類來建立一個臨時緩存View的對象 87 class ViewHolder{ 88 public TextView tvContent,tvTitle; 89 public ImageView ivIcon; 90 } 91 }
8、滾動時的高效優化
前面的代碼,雖然順利的實現了異步加載的過程,可是在實際項目開發中,ListView的每個item項多是很複雜的,若是按照前面的代碼實現,用戶在滾動的過程就可能會出現卡頓的現象。
雖然從網絡加載的數據是在子線程中完成,可是更新UI的過程只能在主線程中更新,若是item項很複雜的話,滾動ListView出現卡頓現象也是能夠理解的。
可是咱們能夠優化這個卡頓的問題:
① ListView滑動中止後才加載可見項
② ListView滑動時,取消全部加載項
優化的緣由是:用戶在滑動的時候,由於ListView在滑動,因此不必作更新主線程UI的操做,而更新主線程UI的操做能夠放在滑動結束的時候執行,這樣也是符合用戶交互習慣的,同時也優化了卡頓的問題。
既然咱們須要監聽滾動的事件,能夠直接使用監聽滾動的接口:
並實現接口的方法:
由於咱們要實如今結束滾動的時候加載可見項,說的再明白點就是好比有100項須要加載,手機屏幕最多顯示8項,用戶滾動到第20項範圍內結束,這時候就要加載這地20項附近的8項便可。
因此要實現這樣的邏輯,就須要滾動結束以前,就要獲取可見項的起止位置,還要獲取這起止位置之間對應的全部數據。
接下來在前面代碼進行修改的細節就比較多了,這裏就直接上修改的源碼,整體思路就是以前的一邊滾動一邊執行適配器對象的getView對象去加載每個item,而接下來就是改爲
在滾動結束的時候,才加載可見項的圖片,固然以前的getView方法里加載String字符串的邏輯仍舊能夠保留,由於這個和經過網絡加載圖片的業務邏輯本質是不一樣的,讀者本身領會,不難。
在NewsAdapter類中:
1 package com.example.heyang.myapplication; 2 3 import android.annotation.TargetApi; 4 import android.content.Context; 5 import android.os.Build; 6 import android.support.annotation.RequiresApi; 7 import android.view.LayoutInflater; 8 import android.view.View; 9 import android.view.ViewGroup; 10 import android.widget.AbsListView; 11 import android.widget.BaseAdapter; 12 import android.widget.ImageView; 13 import android.widget.ListView; 14 import android.widget.TextView; 15 16 import java.util.List; 17 18 /** 19 * Created by HeYang on 16/10/6. 20 */ 21 22 public class NewsAdapter extends BaseAdapter implements AbsListView.OnScrollListener{ 23 24 // 適配器對象須要傳入Bean數據集合對象,相似IOS的模型數組集合 25 private List<NewsBean> beanList; 26 // 而後要傳入LayoutInflater對象,用來獲取xml文件的視圖控件 27 private LayoutInflater layoutInflater; 28 29 private ImageLoader imageLoader; 30 31 // 可見項的起止index 32 private int mStart,mEnd; 33 // 由於咱們須要存儲起止項全部的url地址 34 public static String[] URLS; 35 36 // 建立構造方法 37 @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) 38 public NewsAdapter(MainActivity context, List<NewsBean> data, ListView listView){ 39 beanList = data; 40 layoutInflater = LayoutInflater.from(context);// 這個context對象就是Activity對象 41 imageLoader = new ImageLoader(listView); 42 43 // 將模型數組中的url字符串單獨存儲在靜態數組中 44 int dataSize = data.size(); 45 URLS = new String[dataSize]; 46 for (int i = 0; i < dataSize; i++) { 47 URLS[i] = data.get(i).newsIconURL; 48 } 49 50 // 已經要記得註冊 51 listView.setOnScrollListener(this); 52 } 53 54 @Override 55 public int getCount() { 56 return beanList.size(); 57 } 58 59 @Override 60 public Object getItem(int i) { 61 // 由於beanList是數組,經過get訪問對應index的元素 62 return beanList.get(i); 63 } 64 65 @Override 66 public long getItemId(int i) { 67 return i; 68 } 69 70 71 @Override 72 public View getView(int i, View view, ViewGroup viewGroup) { 73 ViewHolder viewHolder = null; 74 if (view == null){ 75 viewHolder = new ViewHolder(); 76 // 每個View都要和layout關聯 77 view = layoutInflater.inflate(R.layout.item_layout,null); 78 // 在R.layout.item_layout中有三個控件對象 79 // 如今所有傳遞給view對象了 80 viewHolder.tvTitle = (TextView) view.findViewById(R.id.tv_title); 81 viewHolder.tvContent = (TextView) view.findViewById(R.id.tv_content); 82 viewHolder.ivIcon = (ImageView) view.findViewById(R.id.iv_icon); 83 84 view.setTag(viewHolder); 85 86 }else { 87 88 // 重複利用,可是因爲裏面的View展現的數據顯然須要從新賦值 89 viewHolder = (ViewHolder) view.getTag(); 90 } 91 viewHolder.tvTitle.setText(beanList.get(i).newsTitle); 92 viewHolder.tvContent.setText(beanList.get(i).newsContent); 93 // 先默認加載系統圖片 94 viewHolder.ivIcon.setImageResource(R.mipmap.ic_launcher); // 相似加載佔位圖片 95 viewHolder.ivIcon.setTag(beanList.get(i).newsIconURL); 96 // 將ImageView對象和URLSting對象傳入進去 97 // new ImageLoader().showImageByThread(viewHolder.ivIcon,beanList.get(i).newsIconURL); 98 imageLoader.showImageByAsyncTask(viewHolder.ivIcon,beanList.get(i).newsIconURL); 99 return view; 100 } 101 102 @Override 103 public void onScrollStateChanged(AbsListView absListView, int i) { 104 // SCROLL_STATE_IDLE : 滾動結束 105 if (i == SCROLL_STATE_IDLE){ 106 // 加載可見項 107 imageLoader.loadImages(mStart,mEnd); 108 }else{ 109 // 中止全部任務 110 imageLoader.cancelAllTask(); 111 } 112 } 113 114 @Override 115 public void onScroll(AbsListView absListView, int i, int i1, int i2) { 116 // i是第一個可見元素 i1是當前可見元素的長度 117 mStart = i; 118 mEnd = i + i1; 119 } 120 121 // 最後須要一個匿名內部類來建立一個臨時緩存View的對象 122 class ViewHolder{ 123 public TextView tvContent,tvTitle; 124 public ImageView ivIcon; 125 } 126 }
在ImageLoader類中:
1 package com.example.heyang.myapplication; 2 3 import android.annotation.TargetApi; 4 import android.graphics.Bitmap; 5 import android.graphics.BitmapFactory; 6 import android.os.AsyncTask; 7 import android.os.Build; 8 import android.os.Handler; 9 import android.os.Message; 10 import android.support.annotation.RequiresApi; 11 import android.util.Log; 12 import android.util.LruCache; 13 import android.widget.ImageView; 14 import android.widget.ListView; 15 16 import java.io.BufferedInputStream; 17 import java.io.IOException; 18 import java.io.InputStream; 19 import java.net.HttpURLConnection; 20 import java.net.MalformedURLException; 21 import java.net.URL; 22 import java.util.HashSet; 23 import java.util.Set; 24 25 /** 26 * Created by HeYang on 16/10/6. 27 */ 28 29 public class ImageLoader { 30 31 private ImageView mImageView; 32 private String mURLStr; 33 // 建立緩存對象 34 // 第一個參數是須要緩存對象的名字或者ID,這裏咱們傳輸url做爲惟一名字便可 35 // 第二個參數是Bitmap對象 36 private LruCache<String,Bitmap> lruCache; 37 38 private ListView mListView; 39 private Set<NewsAsyncTask> mTasks; 40 41 // 而後咱們須要在構造方法中初始化這個緩存對象 42 // 另外,咱們不可能把全部的緩存空間拿來用 43 @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB_MR1) 44 public ImageLoader(ListView listView){ 45 46 mListView = listView; 47 mTasks = new HashSet<>(); 48 49 // 獲取最大可用內存 50 int maxMemory = (int) Runtime.getRuntime().maxMemory(); 51 int cachaSize = maxMemory / 4; 52 // 建立LruCache對象,同時用匿名內部類的方式重寫方法 53 lruCache = new LruCache<String,Bitmap>(cachaSize){ 54 @Override 55 protected int sizeOf(String key, Bitmap value) { 56 // 咱們須要直接返回Bitmap value的實際大小 57 //return super.sizeOf(key, value); 58 // 在每次存入緩存的時候調用 59 return value.getByteCount(); 60 } 61 }; 62 } 63 64 // 而後咱們要寫倆個方法:一、將bitmap存入緩存中 二、從緩存中取出bitmap 65 66 @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) 67 public void addBitmapToCache(String url, Bitmap bitmap){ 68 if (getBitMapFromCache(url) == null){ 69 lruCache.put(url,bitmap); 70 } 71 } 72 73 @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) 74 public Bitmap getBitMapFromCache(String url){ 75 return lruCache.get(url); 76 } 77 78 79 private Handler handler = new Handler(){ 80 @Override 81 public void handleMessage(Message msg) { 82 super.handleMessage(msg); 83 84 if (mImageView.getTag().equals(mURLStr)){ 85 mImageView.setImageBitmap((Bitmap) msg.obj); 86 } 87 88 89 } 90 }; 91 92 // 根據可見項起止位置加載可見項 93 public void loadImages(int startIndex,int endIndex){ 94 for (int i = startIndex; i < endIndex; i++) { 95 String url = NewsAdapter.URLS[i]; 96 // 在異步請求以前,先判斷緩存中是否有,有的話就取出直接加載 97 Bitmap bitmap = getBitMapFromCache(url); 98 if (bitmap == null){ 99 // 若是沒有,就異步任務加劇 100 // new NewsAsyncTask(imageView, (String) imageView.getTag()).execute(urlString);// 這兩個參數分別傳遞的目的地能夠理解一下 101 NewsAsyncTask task = new NewsAsyncTask(url); 102 task.execute(url); 103 mTasks.add(task); 104 105 106 107 }else{ 108 ImageView loadImageView = (ImageView) mListView.findViewWithTag(url); 109 loadImageView.setImageBitmap(bitmap); 110 } 111 } 112 } 113 114 public void cancelAllTask(){ 115 if (mTasks != null){ 116 for (NewsAsyncTask newsTask: mTasks) { 117 newsTask.cancel(false); 118 } 119 } 120 } 121 122 123 124 125 126 public Bitmap getBitmapFromURL(String urlString){ 127 Bitmap bitmap = null; 128 InputStream is = null; 129 130 try { 131 URL url = new URL(urlString); 132 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 133 is = new BufferedInputStream(connection.getInputStream()); 134 bitmap = BitmapFactory.decodeStream(is); 135 // 最後要關閉http鏈接 136 connection.disconnect(); 137 // Thread.sleep(1000);// 睡眠1秒 138 } catch (IOException e) { 139 e.printStackTrace(); 140 } 141 // catch (InterruptedException e) { 142 // e.printStackTrace(); 143 // } 144 finally { 145 146 try { 147 is.close(); 148 } catch (IOException e) { 149 e.printStackTrace(); 150 } 151 } 152 153 return bitmap; 154 } 155 156 157 // ==================使用AsyncTask==================== 158 public void showImageByAsyncTask(ImageView imageView, final String urlString){ 159 // 在異步請求以前,先判斷緩存中是否有,有的話就取出直接加載 160 Bitmap bitmap = getBitMapFromCache(urlString); 161 if (bitmap == null){ 162 // 若是沒有,就異步任務加劇 163 // new NewsAsyncTask( urlString).execute(urlString);// 這兩個參數分別傳遞的目的地能夠理解一下 164 imageView.setImageResource(R.mipmap.ic_launcher); 165 }else{ 166 imageView.setImageBitmap(bitmap); 167 } 168 169 } 170 171 private class NewsAsyncTask extends AsyncTask<String,Void,Bitmap>{ 172 173 // 須要私有的ImageView對象和構造方法來傳遞ImageView對象 174 // private ImageView mImageView; 175 private String mURlString; 176 177 // public NewsAsyncTask(ImageView imageView,String urlString){ 178 // mImageView = imageView; 179 // mURlString = urlString; 180 // } 181 182 public NewsAsyncTask(String urlString){ 183 mURlString = urlString; 184 } 185 186 @Override 187 protected Bitmap doInBackground(String... strings) { 188 // 在這個方法中,完成異步下載的任務 189 String url = strings[0]; 190 // 從網絡中獲取圖片 191 Bitmap bitmap = getBitmapFromURL(strings[0]); 192 if (bitmap != null){ 193 // 將網絡加載出來的圖片存儲緩存 194 addBitmapToCache(url,bitmap); 195 } 196 return bitmap; 197 } 198 199 200 @Override 201 protected void onPostExecute(Bitmap bitmap) { 202 super.onPostExecute(bitmap); 203 // 而後在這個方法中設置imageViewd 204 205 ImageView executeImageView = (ImageView) mListView.findViewWithTag(mURlString); 206 if (bitmap != null && executeImageView != null){ 207 executeImageView.setImageBitmap(bitmap); 208 } 209 // 執行完當前任務,天然要把當前Task任務remove 210 mTasks.remove(this); 211 212 // if (mImageView.getTag().equals(mURlString)){ 213 // mImageView.setImageBitmap(bitmap); 214 // } 215 216 } 217 } 218 219 // ==================使用多線程==================== 220 public void showImageByThread(ImageView imageView, final String urlString){ 221 222 mImageView = imageView; 223 mURLStr = urlString; 224 225 new Thread(){ 226 @Override 227 public void run() { 228 super.run(); 229 Bitmap bitmap = getBitmapFromURL(urlString); 230 // 當前線程是子線程,並非UI主線程 231 // 不是Message message = new Message(); 232 Message message = Message.obtain(); 233 message.obj = bitmap; 234 handler.sendMessage(message); 235 236 } 237 }.start(); 238 } 239 }
可是,以上的代碼還有一個不足之處,就是最開始啓動的時候,由於沒有滾動ScorllView,因此默認是沒有加載圖片的,可是咱們能夠作一個預加載:
完整源碼:
1 package com.example.heyang.myapplication; 2 3 import android.annotation.TargetApi; 4 import android.content.Context; 5 import android.os.Build; 6 import android.support.annotation.RequiresApi; 7 import android.view.LayoutInflater; 8 import android.view.View; 9 import android.view.ViewGroup; 10 import android.widget.AbsListView; 11 import android.widget.BaseAdapter; 12 import android.widget.ImageView; 13 import android.widget.ListView; 14 import android.widget.TextView; 15 16 import java.util.List; 17 18 /** 19 * Created by HeYang on 16/10/6. 20 */ 21 22 public class NewsAdapter extends BaseAdapter implements AbsListView.OnScrollListener{ 23 24 // 適配器對象須要傳入Bean數據集合對象,相似IOS的模型數組集合 25 private List<NewsBean> beanList; 26 // 而後要傳入LayoutInflater對象,用來獲取xml文件的視圖控件 27 private LayoutInflater layoutInflater; 28 29 private ImageLoader imageLoader; 30 31 private boolean isFirstLoadImage; 32 33 // 可見項的起止index 34 private int mStart,mEnd; 35 // 由於咱們須要存儲起止項全部的url地址 36 public static String[] URLS; 37 38 // 建立構造方法 39 @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) 40 public NewsAdapter(MainActivity context, List<NewsBean> data, ListView listView){ 41 42 isFirstLoadImage = true; 43 44 beanList = data; 45 layoutInflater = LayoutInflater.from(context);// 這個context對象就是Activity對象 46 imageLoader = new ImageLoader(listView); 47 48 // 將模型數組中的url字符串單獨存儲在靜態數組中 49 int dataSize = data.size(); 50 URLS = new String[dataSize]; 51 for (int i = 0; i < dataSize; i++) { 52 URLS[i] = data.get(i).newsIconURL; 53 } 54 55 // 已經要記得註冊 56 listView.setOnScrollListener(this); 57 } 58 59 @Override 60 public int getCount() { 61 return beanList.size(); 62 } 63 64 @Override 65 public Object getItem(int i) { 66 // 由於beanList是數組,經過get訪問對應index的元素 67 return beanList.get(i); 68 } 69 70 @Override 71 public long getItemId(int i) { 72 return i; 73 } 74 75 76 @Override 77 public View getView(int i, View view, ViewGroup viewGroup) { 78 ViewHolder viewHolder = null; 79 if (view == null){ 80 viewHolder = new ViewHolder(); 81 // 每個View都要和layout關聯 82 view = layoutInflater.inflate(R.layout.item_layout,null); 83 // 在R.layout.item_layout中有三個控件對象 84 // 如今所有傳遞給view對象了 85 viewHolder.tvTitle = (TextView) view.findViewById(R.id.tv_title); 86 viewHolder.tvContent = (TextView) view.findViewById(R.id.tv_content); 87 viewHolder.ivIcon = (ImageView) view.findViewById(R.id.iv_icon); 88 89 view.setTag(viewHolder); 90 91 }else { 92 93 // 重複利用,可是因爲裏面的View展現的數據顯然須要從新賦值 94 viewHolder = (ViewHolder) view.getTag(); 95 } 96 viewHolder.tvTitle.setText(beanList.get(i).newsTitle); 97 viewHolder.tvContent.setText(beanList.get(i).newsContent); 98 // 先默認加載系統圖片 99 viewHolder.ivIcon.setImageResource(R.mipmap.ic_launcher); // 相似加載佔位圖片 100 viewHolder.ivIcon.setTag(beanList.get(i).newsIconURL); 101 // 將ImageView對象和URLSting對象傳入進去 102 // new ImageLoader().showImageByThread(viewHolder.ivIcon,beanList.get(i).newsIconURL); 103 imageLoader.showImageByAsyncTask(viewHolder.ivIcon,beanList.get(i).newsIconURL); 104 return view; 105 } 106 107 @Override 108 public void onScrollStateChanged(AbsListView absListView, int i) { 109 // SCROLL_STATE_IDLE : 滾動結束 110 if (i == SCROLL_STATE_IDLE){ 111 // 加載可見項 112 imageLoader.loadImages(mStart,mEnd); 113 }else{ 114 // 中止全部任務 115 imageLoader.cancelAllTask(); 116 } 117 } 118 119 @Override 120 public void onScroll(AbsListView absListView, int i, int i1, int i2) { 121 // i是第一個可見元素 i1是當前可見元素的長度 122 mStart = i; 123 mEnd = i + i1; 124 // 第一次預加載可見項 125 if (isFirstLoadImage && i1 > 0){ 126 imageLoader.loadImages(mStart,mEnd); 127 isFirstLoadImage = false; 128 } 129 } 130 131 // 最後須要一個匿名內部類來建立一個臨時緩存View的對象 132 class ViewHolder{ 133 public TextView tvContent,tvTitle; 134 public ImageView ivIcon; 135 } 136 }
就這樣完成了異步加載的完整最優化的功能:
完整源碼下載地址:
https://github.com/HeYang123456789/Android-ListView-AsyncLoader-Demo