一.简介
代码就不全贴了,Demo在本文的最下方可以下载。在写这个代码的时候,有个地方没注意,readStream方法中把放回结果的字符串:String strData = null 这样写会让返回的数据前面有个null,还有注意一点就是string类型数据默认值是null;
package com.liangdianshui.asynchronouslyload;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
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.util.ArrayList;
import java.util.List;
public class MainActivity extends Activity {
private MyAdapter mAdapter;
private ListView mLv;
private String url = "http://www.imooc.com/api/teacher?type=4&num=30";
private final String TAG = MainActivity.class.getSimpleName();
private MyAsyncTask mAsyncTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
mAsyncTask = new MyAsyncTask();
mAsyncTask.execute(url);
}
private void initView() {
mLv = (ListView) findViewById(R.id.lv_main);
}
/**
* 异步加载数据
*/
class MyAsyncTask extends AsyncTask> {
/**
* 异步加载数据
*
* @param params
* @return
*/
@Override
protected List doInBackground(String... params) {
return getJsonData(params[0]);
}
@Override
protected void onPostExecute(List dataBeans) {
super.onPostExecute(dataBeans);
mAdapter = new MyAdapter(MainActivity.this, dataBeans);
mLv.setAdapter(mAdapter);
}
}
/**
* 获取网络数据
*
* @param url
* @return
*/
private List getJsonData(String url) {
List list = new ArrayList<>();
JSONObject jsonObject;
DataBean dataBean;
try {
String strData = readStream(new URL(url).openStream());
if (strData != null) {
JSONObject allJsonObject = new JSONObject(strData);
JSONArray jsonArray = allJsonObject.getJSONArray("data");
for (int i = 0; i < jsonArray.length(); i++) {
jsonObject = jsonArray.getJSONObject(i);
Log.i(TAG, "jsonObject:" + jsonObject.toString());
dataBean = new DataBean();
dataBean.setDataIconUrl(jsonObject.getString("picSmall"));
dataBean.setDataTitle(jsonObject.getString("name"));
dataBean.setDataContent(jsonObject.getString("description"));
list.add(dataBean);
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return list;
}
/**
* 读取数据流的信息
*
* @param inputStream
* @return
*/
private String readStream(InputStream inputStream) {
// String strData = null; //不能这样写
String strData = "";
InputStreamReader isReader = null;
BufferedReader bfReader = null;
try {
isReader = new InputStreamReader(inputStream, "utf-8");
bfReader = new BufferedReader(isReader);
// String line = null;
String line = "";
while ((line = bfReader.readLine()) != null) {
strData += line;
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
isReader.close();
bfReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return strData;
}
}
Bean对象:
package com.liangdianshui.asynchronouslyload;
/**
* Created by 两点水 on 2016/5/12.
*/
public class DataBean {
public String dataIconUrl;
public String dataTitle;
public String getDataContent() {
return dataContent;
}
public void setDataContent(String dataContent) {
this.dataContent = dataContent;
}
public String getDataTitle() {
return dataTitle;
}
public void setDataTitle(String dataTitle) {
this.dataTitle = dataTitle;
}
public String getDataIconUrl() {
return dataIconUrl;
}
public void setDataIconUrl(String dataIconUrl) {
this.dataIconUrl = dataIconUrl;
}
public String dataContent;
}
Adapter:
package com.liangdianshui.asynchronouslyload;
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;
/**
* Created by 两点水 on 2016/5/12.
*/
public class MyAdapter extends BaseAdapter {
private Context context;
private List list;
private ViewHolder mHolder;
public MyAdapter(Context context, List list) {
this.context = context;
this.list = list;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.item_layout, null);
mHolder = new ViewHolder();
mHolder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title);
mHolder.tvContent = (TextView) convertView.findViewById(R.id.tv_content);
mHolder.ivIcon= (ImageView) convertView.findViewById(R.id.iv_icon);
convertView.setTag(mHolder);
}else{
mHolder = (ViewHolder) convertView.getTag();
}
mHolder.tvTitle.setText(list.get(position).getDataTitle());
mHolder.tvContent.setText(list.get(position).getDataContent());
return convertView;
}
class ViewHolder {
private TextView tvTitle;
private TextView tvContent;
private ImageView ivIcon;
}
}
上面的实现之后,我们要把网络图片也显示到对应的item中的ImageView里!这里写了个类ImgeLoader用来异步加载网络图片,并显示到Imageview中!
package com.liangdianshui.asynchronouslyload;
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.MalformedURLException;
import java.net.URL;
/**
* Created by 两点水 on 2016/5/22.
*/
public class ImageLoader {
private ImageView mImageView;
private final int URL_BITMAP = 0;
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case URL_BITMAP:
mImageView.setImageBitmap((Bitmap) msg.obj);
break;
}
}
};
/**
* 多线程加载图片(Thread+Handler)
* 根据url获取的itmap显示在ImageView中
*
* @param imageView
* @param url
*/
public void showImageByThread(ImageView imageView, final String url) {
mImageView = imageView;
new Thread(new Runnable() {
@Override
public void run() {
Bitmap bitmap = getBitmapFromURL(url);
Message message = new Message();
message.obj = bitmap;
message.what = URL_BITMAP;
mHandler.sendMessage(message);
}
}).start(); //记得start();
}
/**
* 根据rul获取bitmap
*
* @param url
* @return
*/
private Bitmap getBitmapFromURL(String url) {
InputStream inputStream = null;
try {
URL urlBitmap = new URL(url);
HttpURLConnection urlConnection = (HttpURLConnection) urlBitmap.openConnection();
inputStream = new BufferedInputStream(urlConnection.getInputStream());
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
Thread.sleep(1000); //模拟网速慢的情况,为了更好的显示ImageView刷新多次的情况
urlConnection.disconnect(); //关闭http连接
return bitmap;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
try {
inputStream.close(); //关闭流
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
也在Adapter中调用这个方法并显示在ImageView中:
package com.liangdianshui.asynchronouslyload;
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;
/**
* Created by 两点水 on 2016/5/12.
*/
public class MyAdapter extends BaseAdapter {
private Context context;
private List list;
private ViewHolder mHolder;
public MyAdapter(Context context, List list) {
this.context = context;
this.list = list;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.item_layout, null);
mHolder = new ViewHolder();
mHolder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title);
mHolder.tvContent = (TextView) convertView.findViewById(R.id.tv_content);
mHolder.ivIcon= (ImageView) convertView.findViewById(R.id.iv_icon);
convertView.setTag(mHolder);
}else{
mHolder = (ViewHolder) convertView.getTag();
}
String url=list.get(position).getDataIconUrl();
new ImageLoader().showImageByThread(mHolder.ivIcon,url);
mHolder.tvTitle.setText(list.get(position).getDataTitle());
mHolder.tvContent.setText(list.get(position).getDataContent());
return convertView;
}
class ViewHolder {
private TextView tvTitle;
private TextView tvContent;
private ImageView ivIcon;
}
}
看下效果:
认真看下这个效果图,你会发现ImageView 会刷新多次,为什么呢?
package com.liangdianshui.asynchronouslyload;
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;
/**
* Created by 两点水 on 2016/5/12.
*/
public class MyAdapter extends BaseAdapter {
private Context context;
private List list;
private ViewHolder mHolder;
public MyAdapter(Context context, List list) {
this.context = context;
this.list = list;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.item_layout, null);
mHolder = new ViewHolder();
mHolder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title);
mHolder.tvContent = (TextView) convertView.findViewById(R.id.tv_content);
mHolder.ivIcon= (ImageView) convertView.findViewById(R.id.iv_icon);
convertView.setTag(mHolder);
}else{
mHolder = (ViewHolder) convertView.getTag();
}
String url=list.get(position).getDataIconUrl();
mHolder.ivIcon.setTag(url); //为防止listview显示的图片错乱,重复,闪烁
new ImageLoader().showImageByThread(mHolder.ivIcon,url);
mHolder.tvTitle.setText(list.get(position).getDataTitle());
mHolder.tvContent.setText(list.get(position).getDataContent());
return convertView;
}
class ViewHolder {
private TextView tvTitle;
private TextView tvContent;
private ImageView ivIcon;
}
}
ImageLoader中增加判断:
package com.liangdianshui.asynchronouslyload;
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.MalformedURLException;
import java.net.URL;
/**
* Created by 两点水 on 2016/5/22.
*/
public class ImageLoader {
private ImageView mImageView;
private String mUrl;
private final int URL_BITMAP = 0;
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case URL_BITMAP:
if (mImageView.getTag().equals(mUrl)){
mImageView.setImageBitmap((Bitmap) msg.obj);
}
break;
}
}
};
/**
* 多线程加载图片(Thread+Handler)
* 根据url获取的itmap显示在ImageView中
*
* @param imageView
* @param url
*/
public void showImageByThread(ImageView imageView, final String url) {
mImageView = imageView;
mUrl = url;
new Thread(new Runnable() {
@Override
public void run() {
Bitmap bitmap = getBitmapFromURL(url);
Message message = new Message();
message.obj = bitmap;
message.what = URL_BITMAP;
mHandler.sendMessage(message);
}
}).start(); //记得start();
}
/**
* 根据rul获取bitmap
*
* @param url
* @return
*/
private Bitmap getBitmapFromURL(String url) {
InputStream inputStream = null;
try {
URL urlBitmap = new URL(url);
HttpURLConnection urlConnection = (HttpURLConnection) urlBitmap.openConnection();
inputStream = new BufferedInputStream(urlConnection.getInputStream());
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
Thread.sleep(1000); //模拟网速慢的情况,为了更好的显示ImageView刷新多次的情况
urlConnection.disconnect(); //关闭http连接
return bitmap;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
try {
inputStream.close(); //关闭流
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
改完之后,看下效果图:
看效果图,你可能会怀疑是不是放错效果图了?怎么问题没有解决,跟上面的效果图差不多啊!其实不是的,上面的代码已经实现了ImageView显示不对和重复加载图片的问题,会出现上面的情况还是因为没有解决listView缓存的问题,我们在在Adapter里面Imageview显示异步加载图片的前面加上Imageview默认加载的图片的代码:mHolder.ivIcon.setImageResource(R.mipmap.ic_launcher);然后分别运行给ImageView加上标志(setTag)和不加标志的效果:
加上默认加载的图片和去掉tag的效果图
加上默认加载的图片和加上tag的效果图
细心的你会发现是有不同的!加上默认加载的图片和加上tag的效果图明显没有加载两次的效果!
加上默认加载的图片后,我们看到每次下拉或上拉都会先出现默认的图片(android机器人),这样也不好看,因此我们得加上缓存,给已经加载过的图片缓存起来,再次出现时,直接从缓存里拿,不用去下载!把已经显示过的图片缓存起来,可以增加用户的体验,减少流量!
加上缓存后的代码,主要是在Imageloader类上增加LruCache以及修改Adapter调用ImageLoader显示图片的方法切且在构造方法中初始化ImageLoader,目的是为了只创建一次缓存避免多次创建!
MyAdapter代码:
package com.liangdianshui.asynchronouslyload;
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;
/**
* Created by 两点水 on 2016/5/12.
*/
public class MyAdapter extends BaseAdapter {
private Context context;
private List list;
private ViewHolder mHolder;
private ImageLoader mImaeLoader;
public MyAdapter(Context context, List list) {
this.context = context;
this.list = list;
mImaeLoader = new ImageLoader(); //因为创建了缓存空间,所以放在构造方法,只创建一次
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.item_layout, null);
mHolder = new ViewHolder();
mHolder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title);
mHolder.tvContent = (TextView) convertView.findViewById(R.id.tv_content);
mHolder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon);
convertView.setTag(mHolder);
} else {
mHolder = (ViewHolder) convertView.getTag();
}
String url = list.get(position).getDataIconUrl();
mHolder.ivIcon.setImageResource(R.mipmap.ic_launcher); //默认加载的图片
mHolder.ivIcon.setTag(url);//为防止listview显示的图片错乱,重复,闪烁
// new ImageLoader().showImageByThread(mHolder.ivIcon, url);
mImaeLoader.showImageByThread(mHolder.ivIcon, url);
mHolder.tvTitle.setText(list.get(position).getDataTitle());
mHolder.tvContent.setText(list.get(position).getDataContent());
return convertView;
}
class ViewHolder {
private TextView tvTitle;
private TextView tvContent;
private ImageView ivIcon;
}
}
ImageLoader的代码:
package com.liangdianshui.asynchronouslyload;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.util.LruCache;
import android.widget.ImageView;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
/**
* Created by 两点水 on 2016/5/22.
*/
public class ImageLoader {
private ImageView mImageView;
private String mUrl;
private final int URL_BITMAP = 0;
private LruCache mCache; //使用Lru缓存(使用的Lru算法:近期最少使用算法)
/**
* 使用缓存肯定要先声明缓存空间,因此在构造方法中声明缓存空间
*/
public ImageLoader() {
//获取程序最大使用的内存
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 url
* @param bitmap
*/
public void addBitmapToCache(String url, Bitmap bitmap) {
//LruCache好比是一个map,采用键值对的形式去保存图片
if (getBitmapFromCache(url) == null) {
//如果缓存中没有这个Bitmap,就把bitmap放进缓存
mCache.put(url, bitmap);
}
}
/**
* 从缓存中获取图片
*
* @param url
*/
public Bitmap getBitmapFromCache(String url) {
return mCache.get(url); //根据url获取图片
}
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case URL_BITMAP:
if (mImageView.getTag().equals(mUrl)) {
mImageView.setImageBitmap((Bitmap) msg.obj);
}
break;
}
}
};
/**
* 多线程加载图片(Thread+Handler)
* 根据url获取的itmap显示在ImageView中
*
* @param imageView
* @param url
*/
public void showImageByThread(ImageView imageView, final String url) {
mImageView = imageView;
mUrl = url;
//从缓存中取出图片
Bitmap bitmap = getBitmapFromCache(url);
//判断缓存中是否含有这张图片,没有的话去网络下载图片
if (bitmap == null) {
new Thread(new Runnable() {
@Override
public void run() {
Bitmap bitmap = getBitmapFromURL(url);
//把下载下来的图片加到缓存中
if (bitmap != null) {
addBitmapToCache(url, bitmap);
}
Message message = new Message();
message.obj = bitmap;
message.what = URL_BITMAP;
mHandler.sendMessage(message);
}
}).start(); //记得start();
} else {
Message message = new Message();
message.obj = bitmap;
message.what = URL_BITMAP;
mHandler.sendMessage(message);
}
}
/**
* 根据rul获取bitmap
*
* @param url
* @return
*/
private Bitmap getBitmapFromURL(String url) {
InputStream inputStream = null;
try {
URL urlBitmap = new URL(url);
HttpURLConnection urlConnection = (HttpURLConnection) urlBitmap.openConnection();
inputStream = new BufferedInputStream(urlConnection.getInputStream());
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
Thread.sleep(1000); //模拟网速慢的情况,为了更好的显示ImageView刷新多次的情况
urlConnection.disconnect(); //关闭http连接
return bitmap;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
try {
inputStream.close(); //关闭流
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
好了,我们来看下效果图!
看效果图后,我们发现效果并不是我们想象中的那样的!为什么呢?
因为一遍博客的字数有限制,Demo的下载地址和接下来问题的解决就在下一篇博客记录了,下一篇博客还会记录AsyncTask实现异步加载图片,还有ListView滚动时的加载优化和图片的加载优化!
博客地址:http://blog.csdn.net/Two_Water/article/details/51550489