來自:http://www.imooc.com/video/7871
推薦大家去學習這個視頻,講解的很不錯。
慕課網提供了一個json網址可以用來學習:http://www.imooc.com/api/teacher?type=4&num=30。我們的任務就是建立一個listview,將json提供的一些參數,主要是name,picSmall,description顯示出來,效果圖如下:
主要思路如下:listview中圖片的加載,程序中使用了兩種方式,一種是使用Thread類,一種是使用AsyncTask,為了提高listview滑動的效率,並節省流量,使用了LruCache類,更改加載圖片的處理邏輯為:當滑動listview時不加載圖片,停止滑動listview時加載界面可視范圍內的圖片。具體程序如下:
1 NewsBean.Java listview每一項的封裝類
1 package com.example.imoocnews; 2 3 public class NewsBean { 4 private String newsIconUrl;//圖片的網址即picSmall 5 private String newsTitle;//圖片的標題即json中的name屬性 6 private String newsContent;//圖片的內容即json中的description 7 8 public NewsBean(String newsIconUrl, String newsTitle, String newsContent) 9 { 10 this.newsIconUrl = newsIconUrl; 11 this.newsTitle = newsTitle; 12 this.newsContent = newsContent; 13 } 14 public String getNewsIconUrl() { 15 return newsIconUrl; 16 } 17 public void setNewsIconUrl(String newsIconUrl) { 18 this.newsIconUrl = newsIconUrl; 19 } 20 public String getNewsTitle() { 21 return newsTitle; 22 } 23 public void setNewsTitle(String newsTitle) { 24 this.newsTitle = newsTitle; 25 } 26 public String getNewsContent() { 27 return newsContent; 28 } 29 public void setNewsContent(String newsContent) { 30 this.newsContent = newsContent; 31 } 32 33 34 }
2 適配器NewsAdapter.java
1 package com.example.imoocnews; 2 3 import java.util.List; 4 5 import android.content.Context; 6 import android.view.LayoutInflater; 7 import android.view.View; 8 import android.view.ViewGroup; 9 import android.widget.BaseAdapter; 10 import android.widget.ImageView; 11 import android.widget.ListView; 12 import android.widget.TextView; 13 import android.widget.AbsListView; 14 15 /** 16 * listview的適配器,包括上下文對象和數據源 17 * 提高listview的效率:當listview滾動時不去加載可見項圖片,停止滾動后再開始加載 18 */ 19 public class NewsAdapter extends BaseAdapter implements AbsListView.OnScrollListener{ 20 21 private LayoutInflater mInflater; 22 private List<NewsBean> mList; 23 private ImageLoader mImageLoader; 24 private int mStart, mEnd;//listview屏幕可視范圍內的第一條item和最后一個item 25 public static String URLS[];//設置一個數組,用來保存所有圖片的URL 26 private boolean mFirstIn;//判斷是否是第一次啟動程序 27 28 public NewsAdapter(Context context, List<NewsBean> data, ListView listView) { 29 mInflater = LayoutInflater.from(context); 30 this.mList = data; 31 mImageLoader = new ImageLoader(listView);//在這里初始化,能夠保證只有一個imageloader的實例,即只有一個LruCache的實例 32 URLS = new String[data.size()]; 33 for (int i = 0; i < data.size(); i++) { 34 URLS[i] = data.get(i).getNewsIconUrl();//將data中的每一個URL賦值給數組 35 } 36 listView.setOnScrollListener(this); 37 mFirstIn = true;//寫在構造函數中,第一次調用newsAdapter時表示第一次啟動程序,顯示listview 38 } 39 40 @Override 41 public int getCount() { 42 return mList.size(); 43 } 44 45 @Override 46 public Object getItem(int position) { 47 // TODO Auto-generated method stub 48 return mList.get(position); 49 } 50 51 @Override 52 public long getItemId(int position) { 53 // TODO Auto-generated method stub 54 return position; 55 } 56 57 @Override 58 public View getView(int position, View convertView, ViewGroup parent) { 59 ViewHolder holder = null; 60 if (convertView == null) { 61 convertView = mInflater.inflate(R.layout.item, null); 62 holder = new ViewHolder(); 63 holder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon); 64 holder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title); 65 holder.tvContent = (TextView) convertView.findViewById(R.id.tv_content); 66 convertView.setTag(holder); 67 }else { 68 holder = (ViewHolder) convertView.getTag(); 69 } 70 //holder.ivIcon.setImageResource(R.drawable.ic_launcher); 71 String url = mList.get(position).getNewsIconUrl(); 72 holder.ivIcon.setTag(url);//給ImageView設置標志,即對應的圖片網址 73 //利用thread類實現異步加載圖片,我們這里將其注釋,使用AsyncTask的方式 74 //new ImageLoader().showImageByThread(holder.ivIcon, mList.get(position).getNewsIconUrl()); 75 mImageLoader.showImages(holder.ivIcon, url); 76 77 holder.tvTitle.setText(mList.get(position).getNewsTitle()); 78 holder.tvContent.setText(mList.get(position).getNewsContent()); 79 return convertView; 80 } 81 class ViewHolder 82 { 83 public ImageView ivIcon; 84 public TextView tvTitle, tvContent; 85 } 86 /* 87 * 當listview滑動的時候調用 88 */ 89 @Override 90 public void onScroll(AbsListView view, int firstVisibleItem, 91 int visibleItemCount, int totalItemCount) { 92 mStart = firstVisibleItem; 93 mEnd = mStart + visibleItemCount; 94 //只在第一次加載的時候調用 95 if (mFirstIn && visibleItemCount >0) {//表示第一次加載listview並且已經繪制了可見范圍內的item 96 mImageLoader.loadImages(mStart, mEnd); 97 mFirstIn = false;//加載圖片后即設為false 98 } 99 } 100 101 /* 102 * 當listview滑動狀態變化時調用 103 */ 104 @Override 105 public void onScrollStateChanged(AbsListView view, int scrollState) { 106 if (scrollState == SCROLL_STATE_IDLE) {//listview停止滾動 107 //加載可見項 108 mImageLoader.loadImages(mStart, mEnd); 109 110 }else { 111 //停止加載任務 112 mImageLoader.cancelAllTasks(); 113 } 114 115 } 116 117 }
3 訪問圖片的實現ImageLoader.java
1 package com.example.imoocnews; 2 3 import java.io.BufferedInputStream; 4 import java.io.IOException; 5 import java.io.InputStream; 6 import java.net.HttpURLConnection; 7 import java.net.MalformedURLException; 8 import java.net.URL; 9 import java.util.HashSet; 10 import java.util.Set; 11 12 import android.graphics.Bitmap; 13 import android.graphics.BitmapFactory; 14 import android.os.AsyncTask; 15 import android.os.Handler; 16 import android.os.Message; 17 import android.util.LruCache; 18 import android.widget.ImageView; 19 import android.widget.ListView; 20 21 public class ImageLoader { 22 private ImageView mImageView; 23 private String url; 24 //當圖片加載過后就將圖片緩存到本地,下次便不用重新聯網獲取,直接從本地緩存獲取即可,一個圖片即string url --> bitmap 25 private LruCache<String, Bitmap> mCache; 26 private ListView mListView; 27 private Set<NewsAsyncTask> mTasks;//從start到end范圍每次執行加載圖片任務的集合 28 29 public ImageLoader(ListView listView) 30 { 31 mListView = listView; 32 mTasks = new HashSet<ImageLoader.NewsAsyncTask>(); 33 int maxMemory = (int) Runtime.getRuntime().maxMemory();//獲取最大可用內存 34 int cacheSize = maxMemory/4;//設置緩存的大小 35 mCache = new LruCache<String, Bitmap>(cacheSize){ 36 @Override 37 protected int sizeOf(String key, Bitmap value) { 38 // 每次將圖片存入緩存時返回圖片的大小 39 return value.getByteCount(); 40 } 41 }; 42 } 43 /** 44 * 將已聯網獲取成功的圖片加入到緩存中 45 * @param bitmap 46 */ 47 public void addBitmapToCache(String url, Bitmap bitmap) 48 { 49 //在將圖片緩存到本地之前要判斷這個圖片是否已經緩存過了 50 if (getBitmapFromCache(url) == null) { 51 mCache.put(url, bitmap); 52 } 53 } 54 /** 55 * 通過URL從緩存中取出相應的圖片 56 */ 57 public Bitmap getBitmapFromCache(String url) 58 { 59 return mCache.get(url); 60 } 61 62 private Handler mHandler = new Handler(){ 63 public void handleMessage(Message msg) { 64 super.handleMessage(msg); 65 //通過tag使得ImageView和它對應的URL綁定,這樣在上下滑動listview時ImageView顯示的圖片就始終是正確的 66 //否則,由於listview的緩存機制,ImageView會先顯示出上次加載成功時的圖片,然后再顯示正確的圖片 67 if (mImageView.getTag().equals(url)) { 68 mImageView.setImageBitmap((Bitmap) msg.obj);//使用handler在主線程中更新UI,並將URL對應的圖片設置給控件imageview 69 } 70 71 } 72 }; 73 74 /** 75 * 通過使用Thread的方式從網絡上獲取圖片 76 */ 77 public void showImageByThread(ImageView imageView, final String iconUrl) 78 { 79 mImageView = imageView; 80 url = iconUrl; 81 new Thread(){ 82 @Override 83 public void run() { 84 // 在新的進程中實現圖片的加載 85 super.run(); 86 //從url中獲得bitmap,將bitmap發送給主線程 87 Bitmap bitmap = getBitmapFromUrl(iconUrl); 88 Message message = Message.obtain(); 89 message.obj = bitmap; 90 mHandler.sendMessage(message); 91 } 92 }.start(); 93 } 94 public Bitmap getBitmapFromUrl(String urlString) 95 { 96 InputStream is = null; 97 Bitmap bitmap; 98 try { 99 URL url = new URL(urlString); 100 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 101 is = new BufferedInputStream(connection.getInputStream()); 102 bitmap = BitmapFactory.decodeStream(is); 103 connection.disconnect(); 104 //Thread.sleep(1000); 105 return bitmap; 106 } catch (MalformedURLException e) { 107 // TODO Auto-generated catch block 108 e.printStackTrace(); 109 } catch (IOException e) { 110 // TODO Auto-generated catch block 111 e.printStackTrace(); 112 } finally 113 { 114 try { 115 is.close(); 116 } catch (IOException e) { 117 // TODO Auto-generated catch block 118 e.printStackTrace(); 119 } 120 } 121 return null; 122 } 123 124 /** 125 * 加載listview可見范圍內的所有圖片 126 * 127 */ 128 public void loadImages(int start, int end) 129 { 130 for (int i = start; i < end; i++) { 131 String url = NewsAdapter.URLS[i]; 132 //看是否能從緩存中取出對應的圖片 133 Bitmap bitmap = getBitmapFromCache(url); 134 //如果緩存中沒有,就要對每個url執行異步加載任務去獲取圖片 135 if (bitmap == null) { 136 NewsAsyncTask task = new NewsAsyncTask(url); 137 task.execute(url); 138 mTasks.add(task); 139 140 }else { 141 //如果緩存中存在此圖片,直接將其設置給對應的imageview即可 142 //因為我們之前給imageview設置的tag就是URL,可以利用findViewWithTag直接在這里獲取到 143 ImageView imageView = (ImageView) mListView.findViewWithTag(url); 144 imageView.setImageBitmap(bitmap); 145 } 146 } 147 } 148 /** 149 * 取消所有正在進行的圖片加載任務 150 */ 151 public void cancelAllTasks() 152 { 153 if (mTasks != null) { 154 for(NewsAsyncTask task : mTasks) 155 { 156 task.cancel(false); 157 } 158 } 159 } 160 161 public void showImages(ImageView imageView, String iconUrl) 162 { 163 //是否能從緩存中取出對應的圖片 164 Bitmap bitmap = getBitmapFromCache(iconUrl); 165 if (bitmap == null) { 166 imageView.setImageResource(R.drawable.ic_launcher);//顯示默認圖片 167 }else { 168 //如果緩存中存在此圖片,直接將其設置給對應的imageview即可 169 imageView.setImageBitmap(bitmap); 170 } 171 172 } 173 /** 174 * 使用AsyncTask實現圖片的異步加載 175 */ 176 class NewsAsyncTask extends AsyncTask<String, Void, Bitmap> 177 { 178 //private ImageView mImageView; 179 private String mUrl; 180 public NewsAsyncTask(String url) { 181 // mImageView = image; 182 mUrl = url; 183 } 184 @Override 185 protected Bitmap doInBackground(String... params) { 186 String url = params[0]; 187 Bitmap bitmap = getBitmapFromUrl(url);//從網絡上得到圖片 188 if (bitmap != null) { 189 addBitmapToCache(url, bitmap);//獲取圖片成功將圖片存入緩存中 190 } 191 return bitmap; 192 } 193 @Override 194 protected void onPostExecute(Bitmap bitmap) { 195 // 后台獲取圖片的任務完成時調用此方法 196 super.onPostExecute(bitmap); 197 //給imageview設置圖片 198 ImageView imageView = (ImageView) mListView.findViewWithTag(mUrl); 199 if (imageView != null && bitmap != null) { 200 imageView.setImageBitmap(bitmap); 201 } 202 mTasks.remove(this); 203 } 204 } 205 206 }
4. MainActivity.java
1 package com.example.imoocnews; 2 3 import java.io.BufferedReader; 4 import java.net.MalformedURLException; 5 import java.net.URL; 6 import java.io.IOException; 7 import java.io.InputStream; 8 import java.io.InputStreamReader; 9 import java.io.UnsupportedEncodingException; 10 import java.util.ArrayList; 11 import java.util.List; 12 13 import org.json.JSONArray; 14 import org.json.JSONException; 15 import org.json.JSONObject; 16 17 import android.app.Activity; 18 import android.os.AsyncTask; 19 import android.os.Bundle; 20 import android.util.Log; 21 import android.view.Menu; 22 import android.view.MenuItem; 23 import android.widget.ListView; 24 25 public class MainActivity extends Activity { 26 27 private ListView mListView; 28 private static String jsonURL = "http://www.imooc.com/api/teacher?type=4&num=30";//json數據網址 29 30 @Override 31 protected void onCreate(Bundle savedInstanceState) { 32 super.onCreate(savedInstanceState); 33 setContentView(R.layout.activity_main); 34 mListView = (ListView) findViewById(R.id.lv_main); 35 new NewsAsyncTask().execute(jsonURL); 36 } 37 /** 38 * 將URL網址上的json數據轉化為我們所需的newsbean對象 39 * @return 40 */ 41 private List<NewsBean> getJsonData(String url) 42 { 43 List<NewsBean> newsBeanList = new ArrayList<NewsBean>();//保存解析出來的所有的數據 44 try { 45 //獲取到json字符串 46 String jsonString = readStream(new URL(url).openStream());//和url.openConnection().getInputStream()一樣 47 //Log.d("MainActivity", jsonString); 48 //將獲取到的json字符串變為jsonObject對象,打開網址可以看出data節點是一個jsonArray,array里面存放了一個個的jsonObject 49 NewsBean newsBean; 50 JSONObject jsonObject; 51 String newsUrl = null; 52 String newsTitle = null; 53 String newsContent = null; 54 jsonObject = new JSONObject(jsonString); 55 JSONArray jsonArray = jsonObject.getJSONArray("data"); 56 for (int i = 0; i < jsonArray.length(); i++) { 57 jsonObject = jsonArray.getJSONObject(i); 58 newsUrl = jsonObject.getString("picSmall");//圖片網址 59 newsTitle = jsonObject.getString("name");//title 60 newsContent = jsonObject.getString("description");//content 61 newsBean = new NewsBean(newsUrl, newsTitle, newsContent); 62 newsBeanList.add(newsBean); 63 } 64 } catch (MalformedURLException e) { 65 e.printStackTrace(); 66 } catch (IOException e) { 67 e.printStackTrace(); 68 } catch (JSONException e) { 69 // TODO Auto-generated catch block 70 e.printStackTrace(); 71 } 72 return newsBeanList; 73 } 74 75 /** 76 * 解析網絡返回的數據 77 */ 78 private String readStream(InputStream is) 79 { 80 InputStreamReader isReader; 81 String result = ""; 82 String line = ""; 83 try { 84 isReader = new InputStreamReader(is, "utf-8");//將字節流轉化為字符流 85 BufferedReader buffReader = new BufferedReader(isReader);//從字符輸入流中讀取文本,緩沖各個字符,從而實現字符、數組和行的高效讀取 86 while ((line = buffReader.readLine()) != null) { 87 result += line; 88 } 89 } catch (UnsupportedEncodingException e) { 90 e.printStackTrace(); 91 } catch (IOException e) { 92 // TODO Auto-generated catch block 93 e.printStackTrace(); 94 } 95 96 return result; 97 } 98 /** 99 * 構造一個AsyncTask,傳入String類型的URL,返回一個NewsBean對象,每一個對象就是 100 * listview中的每一行數據,包括一個icon,title,content 101 */ 102 class NewsAsyncTask extends AsyncTask<String, Void, List<NewsBean>> 103 { 104 105 @Override 106 protected List<NewsBean> doInBackground(String... params) { 107 return getJsonData(params[0]); 108 } 109 110 @Override 111 protected void onPostExecute(List<NewsBean> result) { 112 super.onPostExecute(result); 113 // 訪問網絡並解析json成功后返回結果,即我們設置的List<NewsBean> 114 NewsAdapter adapter = new NewsAdapter(MainActivity.this, result, mListView); 115 mListView.setAdapter(adapter); 116 } 117 118 } 119 120 }
源碼在這里:http://download.csdn.net/detail/hnyzwtf/9418993