来自: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
时间: 2024-10-14 06:56:26