Android异步加载访问网络图片-解析json
16lz
2021-01-26
来自: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每一项的封装类
package com.example.imoocnews;public class NewsBean { private String newsIconUrl;//图片的网址即picSmall private String newsTitle;//图片的标题即json中的name属性 private String newsContent;//图片的内容即json中的description public NewsBean(String newsIconUrl, String newsTitle, String newsContent) { this.newsIconUrl = newsIconUrl; this.newsTitle = newsTitle; this.newsContent = newsContent; } public String getNewsIconUrl() { return newsIconUrl; } public void setNewsIconUrl(String newsIconUrl) { this.newsIconUrl = newsIconUrl; } public String getNewsTitle() { return newsTitle; } public void setNewsTitle(String newsTitle) { this.newsTitle = newsTitle; } public String getNewsContent() { return newsContent; } public void setNewsContent(String newsContent) { this.newsContent = newsContent; }}
2 适配器NewsAdapter.java
package com.example.imoocnews;import java.util.List;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.ListView;import android.widget.TextView;import android.widget.AbsListView;/** * listview的适配器,包括上下文对象和数据源 * 提高listview的效率:当listview滚动时不去加载可见项图片,停止滚动后再开始加载 */public class NewsAdapter extends BaseAdapter implements AbsListView.OnScrollListener{ private LayoutInflater mInflater; private List mList; private ImageLoader mImageLoader; private int mStart, mEnd;//listview屏幕可视范围内的第一条item和最后一个item public static String URLS[];//设置一个数组,用来保存所有图片的URL private boolean mFirstIn;//判断是否是第一次启动程序 public NewsAdapter(Context context, List data, ListView listView) { mInflater = LayoutInflater.from(context); this.mList = data; mImageLoader = new ImageLoader(listView);//在这里初始化,能够保证只有一个imageloader的实例,即只有一个LruCache的实例 URLS = new String[data.size()]; for (int i = 0; i < data.size(); i++) { URLS[i] = data.get(i).getNewsIconUrl();//将data中的每一个URL赋值给数组 } listView.setOnScrollListener(this); mFirstIn = true;//写在构造函数中,第一次调用newsAdapter时表示第一次启动程序,显示listview } @Override public int getCount() { return mList.size(); } @Override public Object getItem(int position) { // TODO Auto-generated method stub return mList.get(position); } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { convertView = mInflater.inflate(R.layout.item, null); holder = new ViewHolder(); holder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon); holder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title); holder.tvContent = (TextView) convertView.findViewById(R.id.tv_content); convertView.setTag(holder); }else { holder = (ViewHolder) convertView.getTag(); } //holder.ivIcon.setImageResource(R.drawable.ic_launcher); String url = mList.get(position).getNewsIconUrl(); holder.ivIcon.setTag(url);//给ImageView设置标志,即对应的图片网址 //利用thread类实现异步加载图片,我们这里将其注释,使用AsyncTask的方式 //new ImageLoader().showImageByThread(holder.ivIcon, mList.get(position).getNewsIconUrl()); mImageLoader.showImages(holder.ivIcon, url); holder.tvTitle.setText(mList.get(position).getNewsTitle()); holder.tvContent.setText(mList.get(position).getNewsContent()); return convertView; } class ViewHolder { public ImageView ivIcon; public TextView tvTitle, tvContent; } /* * 当listview滑动的时候调用 */ @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { mStart = firstVisibleItem; mEnd = mStart + visibleItemCount; //只在第一次加载的时候调用 if (mFirstIn && visibleItemCount >0) {//表示第一次加载listview并且已经绘制了可见范围内的item mImageLoader.loadImages(mStart, mEnd); mFirstIn = false;//加载图片后即设为false } } /* * 当listview滑动状态变化时调用 */ @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState == SCROLL_STATE_IDLE) {//listview停止滚动 //加载可见项 mImageLoader.loadImages(mStart, mEnd); }else { //停止加载任务 mImageLoader.cancelAllTasks(); } }}
3 访问图片的实现ImageLoader.java
package com.example.imoocnews;import java.io.BufferedInputStream;import java.io.IOException;import java.io.InputStream;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL;import java.util.HashSet;import java.util.Set;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;public class ImageLoader { private ImageView mImageView; private String url; //当图片加载过后就将图片缓存到本地,下次便不用重新联网获取,直接从本地缓存获取即可,一个图片即string url --> bitmap private LruCache mCache; private ListView mListView; private Set mTasks;//从start到end范围每次执行加载图片任务的集合 public ImageLoader(ListView listView) { mListView = listView; mTasks = new HashSet(); int maxMemory = (int) Runtime.getRuntime().maxMemory();//获取最大可用内存 int cacheSize = maxMemory/4;//设置缓存的大小 mCache = new LruCache(cacheSize){ @Override protected int sizeOf(String key, Bitmap value) { // 每次将图片存入缓存时返回图片的大小 return value.getByteCount(); } }; } /** * 将已联网获取成功的图片加入到缓存中 * @param bitmap */ public void addBitmapToCache(String url, Bitmap bitmap) { //在将图片缓存到本地之前要判断这个图片是否已经缓存过了 if (getBitmapFromCache(url) == null) { mCache.put(url, bitmap); } } /** * 通过URL从缓存中取出相应的图片 */ public Bitmap getBitmapFromCache(String url) { return mCache.get(url); } private Handler mHandler = new Handler(){ public void handleMessage(Message msg) { super.handleMessage(msg); //通过tag使得ImageView和它对应的URL绑定,这样在上下滑动listview时ImageView显示的图片就始终是正确的 //否则,由于listview的缓存机制,ImageView会先显示出上次加载成功时的图片,然后再显示正确的图片 if (mImageView.getTag().equals(url)) { mImageView.setImageBitmap((Bitmap) msg.obj);//使用handler在主线程中更新UI,并将URL对应的图片设置给控件imageview } } }; /** * 通过使用Thread的方式从网络上获取图片 */ public void showImageByThread(ImageView imageView, final String iconUrl) { mImageView = imageView; url = iconUrl; new Thread(){ @Override public void run() { // 在新的进程中实现图片的加载 super.run(); //从url中获得bitmap,将bitmap发送给主线程 Bitmap bitmap = getBitmapFromUrl(iconUrl); Message message = Message.obtain(); message.obj = bitmap; mHandler.sendMessage(message); } }.start(); } public Bitmap getBitmapFromUrl(String urlString) { InputStream is = null; Bitmap bitmap; try { URL url = new URL(urlString); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); is = new BufferedInputStream(connection.getInputStream()); bitmap = BitmapFactory.decodeStream(is); connection.disconnect(); //Thread.sleep(1000); return bitmap; } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { is.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return null; } /** * 加载listview可见范围内的所有图片 * */ public void loadImages(int start, int end) { for (int i = start; i < end; i++) { String url = NewsAdapter.URLS[i]; //看是否能从缓存中取出对应的图片 Bitmap bitmap = getBitmapFromCache(url); //如果缓存中没有,就要对每个url执行异步加载任务去获取图片 if (bitmap == null) { NewsAsyncTask task = new NewsAsyncTask(url); task.execute(url); mTasks.add(task); }else { //如果缓存中存在此图片,直接将其设置给对应的imageview即可 //因为我们之前给imageview设置的tag就是URL,可以利用findViewWithTag直接在这里获取到 ImageView imageView = (ImageView) mListView.findViewWithTag(url); imageView.setImageBitmap(bitmap); } } } /** * 取消所有正在进行的图片加载任务 */ public void cancelAllTasks() { if (mTasks != null) { for(NewsAsyncTask task : mTasks) { task.cancel(false); } } } public void showImages(ImageView imageView, String iconUrl) { //是否能从缓存中取出对应的图片 Bitmap bitmap = getBitmapFromCache(iconUrl); if (bitmap == null) { imageView.setImageResource(R.drawable.ic_launcher);//显示默认图片 }else { //如果缓存中存在此图片,直接将其设置给对应的imageview即可 imageView.setImageBitmap(bitmap); } } /** * 使用AsyncTask实现图片的异步加载 */ class NewsAsyncTask extends AsyncTask { //private ImageView mImageView; private String mUrl; public NewsAsyncTask(String url) { // mImageView = image; mUrl = url; } @Override protected Bitmap doInBackground(String... params) { String url = params[0]; Bitmap bitmap = getBitmapFromUrl(url);//从网络上得到图片 if (bitmap != null) { addBitmapToCache(url, bitmap);//获取图片成功将图片存入缓存中 } return bitmap; } @Override protected void onPostExecute(Bitmap bitmap) { // 后台获取图片的任务完成时调用此方法 super.onPostExecute(bitmap); //给imageview设置图片 ImageView imageView = (ImageView) mListView.findViewWithTag(mUrl); if (imageView != null && bitmap != null) { imageView.setImageBitmap(bitmap); } mTasks.remove(this); } }}
4 MainActivity.java
package com.example.imoocnews;import java.io.BufferedReader;import java.net.MalformedURLException;import java.net.URL;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.UnsupportedEncodingException;import java.util.ArrayList;import java.util.List;import org.json.JSONArray;import org.json.JSONException;import org.json.JSONObject;import android.app.Activity;import android.os.AsyncTask;import android.os.Bundle;import android.util.Log;import android.view.Menu;import android.view.MenuItem;import android.widget.ListView;public class MainActivity extends Activity { private ListView mListView; private static String jsonURL = "http://www.imooc.com/api/teacher?type=4&num=30";//json数据网址 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mListView = (ListView) findViewById(R.id.lv_main); new NewsAsyncTask().execute(jsonURL); } /** * 将URL网址上的json数据转化为我们所需的newsbean对象 * @return */ private List getJsonData(String url) { List newsBeanList = new ArrayList();//保存解析出来的所有的数据 try { //获取到json字符串 String jsonString = readStream(new URL(url).openStream());//和url.openConnection().getInputStream()一样 //Log.d("MainActivity", jsonString); //将获取到的json字符串变为jsonObject对象,打开网址可以看出data节点是一个jsonArray,array里面存放了一个个的jsonObject NewsBean newsBean; JSONObject jsonObject; String newsUrl = null; String newsTitle = null; String newsContent = null; jsonObject = new JSONObject(jsonString); JSONArray jsonArray = jsonObject.getJSONArray("data"); for (int i = 0; i < jsonArray.length(); i++) { jsonObject = jsonArray.getJSONObject(i); newsUrl = jsonObject.getString("picSmall");//图片网址 newsTitle = jsonObject.getString("name");//title newsContent = jsonObject.getString("description");//content newsBean = new NewsBean(newsUrl, newsTitle, newsContent); newsBeanList.add(newsBean); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); } return newsBeanList; } /** * 解析网络返回的数据 */ private String readStream(InputStream is) { InputStreamReader isReader; String result = ""; String line = ""; try { isReader = new InputStreamReader(is, "utf-8");//将字节流转化为字符流 BufferedReader buffReader = new BufferedReader(isReader);//从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取 while ((line = buffReader.readLine()) != null) { result += line; } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return result; } /** * 构造一个AsyncTask,传入String类型的URL,返回一个NewsBean对象,每一个对象就是 * listview中的每一行数据,包括一个icon,title,content */ class NewsAsyncTask extends AsyncTask> { @Override protected List doInBackground(String... params) { return getJsonData(params[0]); } @Override protected void onPostExecute(List result) { super.onPostExecute(result); // 访问网络并解析json成功后返回结果,即我们设置的List NewsAdapter adapter = new NewsAdapter(MainActivity.this, result, mListView); mListView.setAdapter(adapter); } }}
源码在这里:http://download.csdn.net/detail/hnyzwtf/9418993
更多相关文章
- Picasso源码解析
- 自定义进度条,progressBar 背景为.9图片
- Android(安卓)通过字符串资源名获取资源id
- Android超好用的图片加载框架
- Android加载Class的思考
- 关于ImageView图片宽高比的问题
- Android有效解决加载大图片时内存溢出的问题[转]
- android yuv摄像
- Android(安卓)嵌入式 linux 去掉启动时闪烁的光标 和制作启动LOG