利用AsyncTask給ListView異步加載圖片 實(shí)現(xiàn)圖文混排
# AsyncTask
// 無參 無進(jìn)度顯示 有返回值
JSONObjectpublic class NewsTask extends AsyncTask<Void,Void,JSONObject> {
private Activity activity;
private String url;
private OnTaskListener listener;
//構(gòu)造方法 將 act 以及 url 傳進(jìn)來 listen實(shí)例化
public NewsTask (Activity activity, String url,OnTaskListener onTaskListener) {
this.activity = activity;
this.url = url;
this.listener = onTaskListener;
}
// 將 url 對應(yīng)的json數(shù)據(jù) 轉(zhuǎn)為 JSONObject
private JSONObject getJsonData(String url){
JSONObject jsonObject = null;
try {
// new URL(url).getConnection.getInputStream 合成
String jsonStr = readStream(new URL(url).openStream());
jsonObject = new JSONObject(jsonStr); //json字符串轉(zhuǎn)為 json對象
} catch (IOException e) {
e.printStackTrace();
}catch (JSONException e) {
e.printStackTrace();
} return jsonObject;
}
// 轉(zhuǎn)成 json 字符串
private String readStream (InputStream is){
InputStreamReader isr;
String result ="";
try {
String line = "";
// 通過定義字符集格式 將字節(jié)流 轉(zhuǎn)為 字符流
isr = new InputStreamReader(is,"utf-8");
BufferedReader br = new BufferedReader(isr); // 緩存 策略 Buffer
while ((line = br.readLine()) != null){ // 逐行 讀 直到為空
result += line;
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
// 子線程 任務(wù)
@Override
protected JSONObject doInBackground(Void... params) {
return getJsonData(url);
}
// execute后 doInBackground前 初始化
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onPostExecute(JSONObject jsonObject) {
super.onPostExecute(jsonObject);
if (jsonObject == null){
listener.onFailed("數(shù)據(jù)為空");
}else {
// 成功
listener.onSucceed(jsonObject);
}
// 注意釋放
listener = null;
activity = null;
}
@Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
}
# act
new NewsTask(this, url, new OnTaskListener() {
@Override public void onSucceed(JSONObject jsonObject){
List<NewsBean> beanList = new ArrayList<NewsBean>();
JSONArray jsonArray = null;
// 操作數(shù)據(jù)
try {
jsonArray = jsonObject.getJSONArray("data");
for (int i=0;i<jsonArray.length();i++){
JSONObject object = (JSONObject) jsonArray.get(i);
NewsBean bean = new NewsBean();
bean.newsImgUrl = object.getString("picBig");
bean.newsTitle = object.getString("name");
bean.newsContent = object.getString("description");
beanList.add(bean);
}
// adapter 將 listView 與 數(shù)據(jù)綁定
listView.setAdapter(new NewsAdapter(beanList,MainActivity.this,listView));
} catch (JSONException e) {
e.printStackTrace();
}
}
@Override public void onFailed(String msg) {
}
}).execute();
# BaseAdapter
public class NewsAdapter extends BaseAdapter implements AbsListView.OnScrollListener{
private List<NewsBean> beanList;
private LayoutInflater inflater;
private Context context;
private ImageThreadLoader imgLoader;
private int startIdx, endIdx;
public static String[] imgUrls; //創(chuàng)建靜態(tài)數(shù)組
private boolean firstFlag;
public NewsAdapter(List<NewsBean> beanList, Context context) {
this.beanList = beanList;
this.context = context;
inflater = LayoutInflater.from(context);
imgLoader = new ImageThreadLoader();
// 將所有 newsImgUrl 單獨(dú)出來成為一個靜態(tài)數(shù)組
imgUrls = new String[beanList.size()];
for (int i=0;i<beanList.size();i++){
imgUrls[i] = beanList.get(i).newsImgUrl;
}
listView.setOnScrollListener(this);
firstFlag = true; // 首次加載
}
@Override
public int getCount() {
return beanList.size();
}
@Override
public Object getItem(int position) {
return beanList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
HolderView holderView;
if (convertView == null){
holderView = new HolderView();
convertView = inflater.inflate(R.layout.item_layout,null);
holderView.mImgView = (ImageView) convertView.findViewById(R.id.imgView);
holderView.mTitle = (TextView) convertView.findViewById(R.id.tv_title);
holderView.mContent = (TextView) convertView.findViewById(R.id.tv_content);
convertView.setTag(holderView); //綁定
}else{
holderView = (HolderView) convertView.getTag();
}
// 賦值
NewsBean bean = beanList.get(position);
holderView.mImgView.setImageResource(R.mipmap.ic_launcher);
// 綁定
holderView.mImgView.setTag(bean.newsImgUrl);
// 多線程
// new ImageThreadLoader().getImageByThrea(holderView.mImgView,bean.newsImgUrl);
// AsyncTask 方式 依次執(zhí)行任務(wù)
// new ImageThreadLoader().setImageByAsyncTask(holderView.mImgView,bean.newsImgUrl);
// 使用LruCache 以內(nèi)存換效率
// imgLoader.setImageByAsyncTask(holderView.mImgView,bean.newsImgUrl);
holderView.mTitle.setText(bean.newsTitle);
holderView.mContent.setText(bean.newsContent);
}
// AbsListView.OnScrollListener 滑動監(jiān)聽
@Overridepublic
void onScrollStateChanged(AbsListView view, int scrollState) {
// 狀態(tài)改變
if (scrollState == SCROLL_STATE_IDLE){
// 滾動停止 加載數(shù)據(jù)
imgLoader.loadImages(startIdx,endIdx);
}else {
// 停止后臺線程 保證滾動的流暢
imgLoader.cancleAllTasks();
}
}
// onScroll 一開始就會執(zhí)行
@Overridepublic
void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
startIdx = firstVisibleItem; // 第一個可見項(xiàng)
endIdx = firstVisibleItem + visibleItemCount; // 最后一個可見項(xiàng)
if (firstFlag && totalItemCount > 0){
// 第一次 第一屏 加載 (此時 未主動滑動 且 totalItemCount不為0而且已經(jīng)計(jì)算出來)
imgLoader.loadImages(startIdx,endIdx);
firstFlag = false;
}
}
// ViewHolder 內(nèi)部類
private class HolderView{
private ImageView mImgView;
private TextView mTitle,mContent;
}
# Thread
public class ImageThreadLoader {
private ImageView mImageView;
private String mUrl;
// Lru(least recently used近期最少使用 算法) LruCache是Android基于LRU封裝的緩存 (類型類似于map)
private LruCache<String,Bitmap> mCache;
private ListView mListView;
private Set<ImageAsycTask> mVisiableTasks;
// 匿名Handler 處理子線程和主線程的消息傳遞 更新UI
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// url 一一對應(yīng)ImgView 避免 多次賦值 以及 賦值混亂
if (mImageView.getTag() == mUrl){
mImageView.setImageBitmap((Bitmap) msg.obj);
}
}
};
public void getImageByThrea(ImageView imgView, final String imgUrl){
// 存儲 imgView 和 url
mImageView = imgView;
mUrl = imgUrl;
//
new Thread(){
@Override
public void run() {
super.run();
Bitmap bitmap = getBitmapBy(imgUrl);
Message msg = new Message();
msg.obj = bitmap;
mHandler.sendMessage(msg); //發(fā)送消息
}
}.start();
}
private Bitmap getBitmapBy(String imgUrl) {
Bitmap bitmap = null;
InputStream is = null;
URL url = null;
try {
url = new URL(imgUrl);
} catch (MalformedURLException e) {
e.printStackTrace();
}
HttpURLConnection connection = null;
try {
connection = (HttpURLConnection) url.openConnection();
} catch (IOException e) {
e.printStackTrace();
}
try {
is = connection.getInputStream();
} catch (IOException e) { e.printStackTrace(); }
BufferedInputStream bis = new BufferedInputStream(is);
bitmap = BitmapFactory.decodeStream(bis);
return bitmap;
}
// 構(gòu)造方法 外界 實(shí)例化ImageThreadLoader的成員變量 保證只有一個 LruCache對象
public ImageThreadLoader() {
mListView = listView;
// 初始化 集合
mVisiableTasks = new HashSet<>();
// 獲得當(dāng)前 最大可以內(nèi)存
int maxSize = (int) Runtime.getRuntime().maxMemory();
// 分多少內(nèi)存給緩存
int cacheSize = maxSize / 4;
// 實(shí)例化
mCache = new LruCache<String, Bitmap>(cacheSize){
// 必須實(shí)現(xiàn)的方法 返回每次存入時 key 對應(yīng)的value的大小
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
}
};
}
private void addToCache(String key,Bitmap value){
if (getFromCache(key) == null){
// 內(nèi)存中不存在
mCache.put(key,value);
}
}
private Bitmap getFromCache(String key){
return mCache.get(key);
}
// AsyncTask方式
public void setImageByAsyncTask(ImageView imgView,String url){
mImageView = imgView;
mUrl = url;
// 先從緩存中取數(shù)據(jù)
Bitmap bitmap = getFromCache(url);
if (bitmap == null){
// 沒有緩存 下載
new ImageAsycTask().execute(url);
}else {
imgView.setImageBitmap(bitmap);
}
}
// 加載序列中 指定的所有圖片
public void loadImages(int startIdx,int endIdx){
for (int i=startIdx;i<endIdx;i++){
String url = NewsAdapter.imgUrls[i];
// 先從緩存中取數(shù)據(jù)
Bitmap bitmap = getFromCache(url);
if (bitmap == null){
// 沒有緩存 下載
ImageAsycTask task = new ImageAsycTask(url);
task.execute(url);
mVisiableTasks.add(task); // 加到Set集合中
}else {
// listView 根據(jù) tag 找到url對應(yīng)的ImgView
ImageView imgView = (ImageView) mListView.findViewWithTag(url);
imgView.setImageBitmap(bitmap);
}
}
}
public void cancleAllTasks(){
if (mVisiableTasks != null){
for (ImageAsycTask task : mVisiableTasks) {
task.cancel(true);
}
}
}
private class ImageAsycTask extends AsyncTask<String,Void,Bitmap>{
private String taskImgUrl;
public ImageAsycTask(String taskImgUrl) {
this.taskImgUrl = taskImgUrl;
}
@Override
protected Bitmap doInBackground(String... params) {
String url = params[0];
Bitmap bitmap = getBitmapBy(url);
if (bitmap != null){
// 加入到緩存
addToCache(url,bitmap);
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
ImageView imgView = (ImageView) mListView.findViewWithTag(taskImgUrl);
if (imgView != null && bitmap != null){
imgView.setImageBitmap(bitmap);
}
mVisiableTasks.remove(this);
}
}
}
# 接口
public interface OnTaskListener {
// 將Task獨(dú)立出來以及 定義接口和將返回值 定義成 JSONObject 都是為了封裝
public void onSucceed(JSONObject jsonObject);
public void onFailed(String msg);
}