AsyncTask案例——异步加载ListView内容,包括优化
来源:互联网 发布:淘宝分销受骗例子 编辑:程序博客网 时间:2024/04/30 07:31
1. 第一步:创建ListView中每一项item的页面布局itemlayout.xml:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="4dp" > <ImageView android:id="@+id/iv_icon" android:layout_width="64dp" android:layout_height="64dp" android:src="@drawable/ic_launcher" /> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" android:paddingLeft="4dp" > <TextView android:id="@+id/tv_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:maxLines="1" android:text="Title" android:textSize="15sp" /> <TextView android:id="@+id/tv_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:maxLines="3" android:text="Content" android:textSize="10sp" /> </LinearLayout></LinearLayout>
第二步:设置主页面布局activity_main.xml:包含一个ListView控件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <ListView android:id="@+id/lv_main" android:layout_width="match_parent" android:layout_height="match_parent" /></RelativeLayout>
输入网站打开:http://www.imooc.com/api/teacher?type=4&num=30
然后理应看到这样的JSON页面:
如果显示出来这样的:
那可以复制代码到一些网站中去在线解析,例如:http://json.cn/
那么在这个例子中,我们将其中的name作为Title,description作为Content,picSmall作为ImageView。嗯嗯。
第四步:创建NewsBean.java文件用于封装JSON中的数据,便于使用:
package com.example.asynctaskdemo2;/* * 用于封装JSON中的数据,便于使用: * */public class NewsBean {public String newsIconUrl;public String newsTitle;public String newsContent;}
package com.example.asynctaskdemo2;import java.io.BufferedReader;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 java.net.MalformedURLException;import java.net.URL;import android.os.AsyncTask;import android.os.Bundle;import android.app.Activity;import android.util.Log;import android.view.Menu;import android.widget.ListView;public class MainActivity extends Activity {private ListView mListView;private static String URL = "http://www.imooc.com/api/teacher?type=4&num=30"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mListView = (ListView) findViewById(R.id.lv_main); new NewAsyncTask().execute(URL); } private List<NewsBean> getJsonData(String url){ List<NewsBean> newsBeanList = new ArrayList<NewsBean>(); // readStream此句功能与url.openConnection().getInputStream()相同 // 可根据URL直接联网获取网络数据,简单粗暴! // 返回值类型为InputStream。 // 用URL必须前面引入:import java.net.URL; String jsonString = null;try {jsonString = readStream(new URL(url).openStream());} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} Log.d("xys",jsonString);// 打印获取信息return newsBeanList; } /* * 通过InputStream去读取网络信息: * 传来的参数是一个InputStream的字节流is, * 通过InputStreamReader将字节流转化为字符流, * 再通过BufferedReader将字符流以Buffer的形式读取出来, * 最终拼接到result里面。 * 这样就完成了整个数据的读取。 * */ private String readStream(InputStream is){ InputStreamReader isr; String result = ""; try { String line = "";isr = new InputStreamReader(is,"utf-8");BufferedReader br = new BufferedReader(isr);while((line=br.readLine()) != null){result += line;}} catch (UnsupportedEncodingException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} return result; } /* * 定义内部类: * <Params, Progress, Result> * */ class NewAsyncTask extends AsyncTask<String, Void, List<NewsBean>>{@Overrideprotected List<NewsBean> doInBackground(String... params) {// params[0]为请求网站,因为只传了一个网址,所以只取0即可。return getJsonData(params[0]);} }}效果图:
第六步:解析JSON数据到List中:主要改动在getJsonData中,最终将结果存储在NewsBeanList中并返回:
package com.example.asynctaskdemo2;import java.io.BufferedReader;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 java.net.MalformedURLException;import java.net.URL;import org.json.JSONArray;import org.json.JSONException;import org.json.JSONObject;import android.os.AsyncTask;import android.os.Bundle;import android.app.Activity;import android.util.Log;import android.view.Menu;import android.widget.ListView;public class MainActivity extends Activity {private ListView mListView;private static String URL = "http://www.imooc.com/api/teacher?type=4&num=30";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mListView = (ListView) findViewById(R.id.lv_main);new NewAsyncTask().execute(URL);}/* * 将url对应的JSON格式数据转化为我们所封装的NewsBean对象 */private List<NewsBean> getJsonData(String url) {List<NewsBean> newsBeanList = new ArrayList<NewsBean>();// 此句功能与url.openConnection().getInputStream()相同// 可根据URL直接联网获取网络数据,简单粗暴!// 返回值类型为InputStream。// 用URL必须前面引入:import java.net.URL;String jsonString = null;try {jsonString = readStream(new URL(url).openStream());JSONObject jsonObject;NewsBean newsBean;try {// 这边很神奇啊,jsonObject = new JSONObject(jsonString);JSONArray jsonArray = jsonObject.getJSONArray("data");for (int i = 0; i < jsonArray.length(); i++) {jsonObject = jsonArray.getJSONObject(i);newsBean = new NewsBean();newsBean.newsIconUrl = jsonObject.getString("picSmall");newsBean.newsTitle = jsonObject.getString("name");newsBean.newsContent = jsonObject.getString("description");newsBeanList.add(newsBean);}} catch (JSONException e) {e.printStackTrace();}} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}// Log.d("xys",jsonString);return newsBeanList;}/* * 通过InputStream去解析读取网络信息: 传来的参数是一个InputStream的字节流is, * 通过InputStreamReader将字节流转化为字符流, 再通过BufferedReader将字符流以Buffer的形式读取出来, * 最终拼接到result里面。 这样就完成了整个数据的读取。 */private String readStream(InputStream is) {InputStreamReader isr;String result = "";try {String line = "";isr = new InputStreamReader(is, "utf-8");BufferedReader br = new BufferedReader(isr);while ((line = br.readLine()) != null) {result += line;}} catch (UnsupportedEncodingException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return result;}/* * 定义内部类: <Params, Progress, Result>,实现网络异步访问 */class NewAsyncTask extends AsyncTask<String, Void, List<NewsBean>> {@Overrideprotected List<NewsBean> doInBackground(String... params) {// params[0]为请求网站,因为只传了一个网址,所以只取0即可。return getJsonData(params[0]);}// 适配器在这里设置哦。@Overrideprotected void onPostExecute(List<NewsBean> newsBeans) {super.onPostExecute(newsBeans);NewsAdapter adapter = new NewsAdapter(MainActivity.this,newsBeans);mListView.setAdapter(adapter);}}}
在Debug下查看结果:
第七步:设置适配器:
新建NewAdapter类来继承BaseAdapter类:下面使用文艺式写法:
这里有个很重要的地方就是:
import com.example.asynctaskdemo2.R;这样写才能使下面的使用布局正常。
import java.util.List;import com.example.asynctaskdemo2.NewsBean;import com.example.asynctaskdemo2.R;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;public class NewsAdapter extends BaseAdapter{private List<NewsBean> mList;private LayoutInflater mInflater;public NewsAdapter(Context context,List<NewsBean> data){mList = data;mInflater = LayoutInflater.from(context);}@Overridepublic int getCount() {return mList.size();}@Overridepublic Object getItem(int arg0) {return mList.get(arg0);}@Overridepublic long getItemId(int arg0) {return arg0;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder viewHolder = null;if(convertView == null){viewHolder = new ViewHolder();convertView = mInflater.inflate(R.layout.itemlayout, null);viewHolder.ivIcon = (ImageView)convertView.findViewById(R.id.iv_icon);viewHolder.tvTitle = (TextView)convertView.findViewById(R.id.tv_title);viewHolder.tvContent = (TextView)convertView.findViewById(R.id.tv_content);convertView.setTag(viewHolder);}else{viewHolder = (ViewHolder) convertView.getTag();}viewHolder.ivIcon.setImageResource(R.drawable.ic_launcher);// default pictureviewHolder.tvTitle.setText(mList.get(position).newsTitle);viewHolder.tvContent.setText(mList.get(position).newsContent);return convertView;}/* * 用文艺式 * */class ViewHolder{public TextView tvTitle,tvContent;public ImageView ivIcon;}}
到目前为止的效果图:
哈哈
到目前为止的一个整体流程:
首先在onCreate方法中new一个NewsAysncTask方法,它将一个URL传递进去,实现对这个URL的异步访问,然后这个URL传递给了getJsonData方法,通过这个方法我们将URL中的内容转化为NewsBean对象,然后将这个对象在onPostExecute方法中将其传递给NewsAdapter,通过这个适配器构造了mListView的数据源,从而让mListView显示我们要的数据。
第八步:通过ImageLoader类的getBitmapFromURL使用URL获取Bitmap,再添加一个简单的线程:
创建一个ImageLoader类:
package com.example.asynctaskdemo2;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 android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.widget.ImageView;public class ImageLoader {public void showImageByThread(ImageView imageView,String url){// 创建一个简单的线程new Thread(){@Overridepublic void run() {super.run();}}.start();}/* * 从一个url获取一个Bitmap,这个函数还是与线程没有什么关系的,只是获取一个bitmap。 * */public Bitmap getBitmapFromURL(String urlString){Bitmap bitmap;InputStream is = null;try {URL url = new URL(urlString);HttpURLConnection connection = (HttpURLConnection) url.openConnection();is = new BufferedInputStream(connection.getInputStream());// 通过Decode将InputStream转化为Bitmap:bitmap = BitmapFactory.decodeStream(is);connection.disconnect();return bitmap;} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally{try {is.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}return null;}}还需要修改一下适配器NewsAdapter中的getView函数:添加了一条语句而已,通过URL获取Bitmap图片。
@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder viewHolder = null;if(convertView == null){viewHolder = new ViewHolder();convertView = mInflater.inflate(R.layout.itemlayout, null);viewHolder.ivIcon = (ImageView)convertView.findViewById(R.id.iv_icon);viewHolder.tvTitle = (TextView)convertView.findViewById(R.id.tv_title);viewHolder.tvContent = (TextView)convertView.findViewById(R.id.tv_content);convertView.setTag(viewHolder);}else{viewHolder = (ViewHolder) convertView.getTag();}viewHolder.ivIcon.setImageResource(R.drawable.ic_launcher);// default picture// 通过URL获取Bitmap图片new ImageLoader().showImageByThread(viewHolder.ivIcon, mList.get(position).newsIconUrl);viewHolder.tvTitle.setText(mList.get(position).newsTitle);viewHolder.tvContent.setText(mList.get(position).newsContent);return convertView;}
修改ImageLoader文件:
package com.example.asynctaskdemo2;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 android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.Handler;import android.os.Message;import android.widget.ImageView;public class ImageLoader {private ImageView mImageView;private String mUrl;private Handler mHandler = new Handler(){@Overridepublic void handleMessage(android.os.Message msg) {super.handleMessage(msg);// 判断相同才设置图片,避免图片出现错位现象if(mImageView.getTag().equals(mUrl)){mImageView.setImageBitmap((Bitmap) msg.obj);}};};/* * Android的三线程模型,非主线程不能够在子线程中之间编辑UI, * 我们只能通过Handler来做一个消息的传递 * */public void showImageByThread(ImageView imageView,final String url){mImageView = imageView;mUrl = url;// 创建一个简单的线程new Thread(){@Overridepublic void run() {super.run();Bitmap bitmap = getBitmapFromURL(url);// 通过Handler的Message来传递BitmapMessage message = Message.obtain();message.obj = bitmap;mHandler.sendMessage(message);}}.start();}/* * 从一个url获取一个Bitmap * */public Bitmap getBitmapFromURL(String urlString){Bitmap bitmap;InputStream is = null;try {URL url = new URL(urlString);HttpURLConnection connection = (HttpURLConnection) url.openConnection();is = new BufferedInputStream(connection.getInputStream());// 通过Decode将InputStream转化为Bitmap:bitmap = BitmapFactory.decodeStream(is);connection.disconnect();return bitmap;} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally{try {is.close();} catch (IOException e) {e.printStackTrace();}}return null;}}修改NewsAdapter文件的getView方法:
@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder viewHolder = null;if(convertView == null){viewHolder = new ViewHolder();convertView = mInflater.inflate(R.layout.itemlayout, null);viewHolder.ivIcon = (ImageView)convertView.findViewById(R.id.iv_icon);viewHolder.tvTitle = (TextView)convertView.findViewById(R.id.tv_title);viewHolder.tvContent = (TextView)convertView.findViewById(R.id.tv_content);convertView.setTag(viewHolder);}else{viewHolder = (ViewHolder) convertView.getTag();}viewHolder.ivIcon.setImageResource(R.drawable.ic_launcher);// default pictureString url = mList.get(position).newsIconUrl;// 绑定,通过设置Tag避免因为网络时间造成的卡顿,错位的问题。viewHolder.ivIcon.setTag(url);// 通过URL获取Bitmap图片,用线程的方法加载图片。new ImageLoader().showImageByThread(viewHolder.ivIcon, url);viewHolder.tvTitle.setText(mList.get(position).newsTitle);viewHolder.tvContent.setText(mList.get(position).newsContent);return convertView;}上面就实现了用线程的方法加载图片。
效果图:
第十步:AsyncTask方式实现图片的异步加载:
改ImageLoader添加AsyncTask异步方式:
package com.example.asynctaskdemo2;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 android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.AsyncTask;import android.os.Handler;import android.os.Message;import android.widget.ImageView;public class ImageLoader {private ImageView mImageView;private String mUrl;private Handler mHandler = new Handler() {@Overridepublic void handleMessage(android.os.Message msg) {super.handleMessage(msg);// 判断相同才设置图片,避免图片出现错位现象if (mImageView.getTag().equals(mUrl)) {mImageView.setImageBitmap((Bitmap) msg.obj);}};};/* * 用线程的方式实现图片的异步加载: Android的三线程模型,非主线程不能够在子线程中之间编辑UI, 我们只能通过Handler来做一个消息的传递 */public void showImageByThread(ImageView imageView, final String url) {mImageView = imageView;mUrl = url;// 创建一个简单的线程new Thread() {@Overridepublic void run() {super.run();Bitmap bitmap = getBitmapFromURL(url);// 通过Handler的Message来传递BitmapMessage message = Message.obtain();message.obj = bitmap;mHandler.sendMessage(message);}}.start();}/* * 从一个url获取一个Bitmap */public Bitmap getBitmapFromURL(String urlString) {Bitmap bitmap;InputStream is = null;try {URL url = new URL(urlString);HttpURLConnection connection = (HttpURLConnection) url.openConnection();is = new BufferedInputStream(connection.getInputStream());// 通过Decode将InputStream转化为Bitmap:bitmap = BitmapFactory.decodeStream(is);connection.disconnect();return bitmap;} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {try {is.close();} catch (IOException e) {e.printStackTrace();}}return null;}/* * 用AsyncTask的方式实现图片的异步加载: */public void showImageByAsyncTask(ImageView imageView, String url) {new NewsAsyncTask(imageView, url).execute(url);}/* * 创建NewsAsyncTask来继承实现AsyncTask,实现具体的异步加载过程。 */private class NewsAsyncTask extends AsyncTask<String, Void, Bitmap> {private ImageView mImageView;private String mUrl; // 用于解除图片多次加载,错位的问题public NewsAsyncTask(ImageView imageView, String url) {mImageView = imageView;mUrl = url;}@Overrideprotected Bitmap doInBackground(String... params) {return getBitmapFromURL(params[0]);}@Overrideprotected void onPostExecute(Bitmap bitmap) {super.onPostExecute(bitmap);// 用于解除图片多次加载,错位的问题if(mImageView.getTag().equals(mUrl)){mImageView.setImageBitmap(bitmap);}}}}改NewsAdapter的getView方法:
@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder viewHolder = null;if(convertView == null){viewHolder = new ViewHolder();convertView = mInflater.inflate(R.layout.itemlayout, null);viewHolder.ivIcon = (ImageView)convertView.findViewById(R.id.iv_icon);viewHolder.tvTitle = (TextView)convertView.findViewById(R.id.tv_title);viewHolder.tvContent = (TextView)convertView.findViewById(R.id.tv_content);convertView.setTag(viewHolder);}else{viewHolder = (ViewHolder) convertView.getTag();}viewHolder.ivIcon.setImageResource(R.drawable.ic_launcher);// default pictureString url = mList.get(position).newsIconUrl;// 用于解除图片多次加载,错位的问题viewHolder.ivIcon.setTag(url);/*// 使用线程的方式实现图片的异步加载:new ImageLoader().showImageByThread(viewHolder.ivIcon, url);*/// AsyncTask异步方法加载图片:new ImageLoader().showImageByAsyncTask(viewHolder.ivIcon, url);viewHolder.tvTitle.setText(mList.get(position).newsTitle);viewHolder.tvContent.setText(mList.get(position).newsContent);return convertView;}效果图:家里的渣渣网速:
上面已经实现了基本的功能,下面的我们进行进一步的优化:
第十一步:
改ImageLoader类:
package com.example.asynctaskdemo2;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 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;public class ImageLoader {private ImageView mImageView;private String mUrl;// 创建Cache// 第一个参数是需要保存对象的名字,第二个参数是保存对象private LruCache<String, Bitmap> mCaches;public ImageLoader(){// 获取最大可用内存:int maxMemory = (int) Runtime.getRuntime().maxMemory();// 设定LruCache可用范围:int cacheSize = maxMemory / 4;// 初始化LruCache的大小:mCaches = new LruCache<String, Bitmap>(cacheSize){@Overrideprotected int sizeOf(String key, Bitmap value) {// 返回当前存入对象实际大小。在每次存入缓存的时候调用。return value.getByteCount();}};}/* * 将内容存到缓存中:增加之前先校验是否存在。 * */public void addBitmapToCache(String url, Bitmap bitmap){if(getBitmapFromCache(url) == null){mCaches.put(url, bitmap);}}/* * 将内容从缓存中取出来: * */public Bitmap getBitmapFromCache(String url){return mCaches.get(url);}private Handler mHandler = new Handler() {@Overridepublic void handleMessage(android.os.Message msg) {super.handleMessage(msg);// 判断相同才设置图片,避免图片出现错位现象if (mImageView.getTag().equals(mUrl)) {mImageView.setImageBitmap((Bitmap) msg.obj);}};};/* * 用线程的方式实现图片的异步加载: Android的三线程模型,非主线程不能够在子线程中之间编辑UI, 我们只能通过Handler来做一个消息的传递 */public void showImageByThread(ImageView imageView, final String url) {mImageView = imageView;mUrl = url;// 创建一个简单的线程new Thread() {@Overridepublic void run() {super.run();Bitmap bitmap = getBitmapFromURL(url);// 通过Handler的Message来传递BitmapMessage message = Message.obtain();message.obj = bitmap;mHandler.sendMessage(message);}}.start();}/* * 从一个url获取一个Bitmap */public Bitmap getBitmapFromURL(String urlString) {Bitmap bitmap;InputStream is = null;try {URL url = new URL(urlString);HttpURLConnection connection = (HttpURLConnection) url.openConnection();is = new BufferedInputStream(connection.getInputStream());// 通过Decode将InputStream转化为Bitmap:bitmap = BitmapFactory.decodeStream(is);connection.disconnect();return bitmap;} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {try {is.close();} catch (IOException e) {e.printStackTrace();}}return null;}/* * 用AsyncTask的方式实现图片的异步加载: */public void showImageByAsyncTask(ImageView imageView, String url) {// 先判断缓存中是否已经存在图片,避免多次下载:Bitmap bitmap = getBitmapFromCache(url);// 如果缓存中没有,那么必须去网络下载。if(bitmap == null){new NewsAsyncTask(imageView, url).execute(url);}else{// 如果有了就直接使用。imageView.setImageBitmap(bitmap);}}/* * 创建NewsAsyncTask来继承实现AsyncTask,实现具体的异步加载过程。 */private class NewsAsyncTask extends AsyncTask<String, Void, Bitmap> {private ImageView mImageView;private String mUrl; // 用于解除图片多次加载,错位的问题public NewsAsyncTask(ImageView imageView, String url) {mImageView = imageView;mUrl = url;}@Overrideprotected Bitmap doInBackground(String... params) {String url = params[0];// 从网络获取图片:Bitmap bitmap = getBitmapFromURL(url);if(bitmap != null){// 将不在缓存的图片加入缓存:addBitmapToCache(url, bitmap);}return bitmap;}@Overrideprotected void onPostExecute(Bitmap bitmap) {super.onPostExecute(bitmap);// 用于解除图片多次加载,错位的问题if(mImageView.getTag().equals(mUrl)){mImageView.setImageBitmap(bitmap);}}}}改NewsAdapter类:
package com.example.asynctaskdemo2;import java.util.List;import com.example.asynctaskdemo2.NewsBean;import com.example.asynctaskdemo2.R;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;public class NewsAdapter extends BaseAdapter{private List<NewsBean> mList;private LayoutInflater mInflater;private ImageLoader mImageLoader;public NewsAdapter(Context context,List<NewsBean> data){mList = data;mInflater = LayoutInflater.from(context);mImageLoader = new ImageLoader();}@Overridepublic int getCount() {return mList.size();}@Overridepublic Object getItem(int arg0) {return mList.get(arg0);}@Overridepublic long getItemId(int arg0) {return arg0;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder viewHolder = null;if(convertView == null){viewHolder = new ViewHolder();convertView = mInflater.inflate(R.layout.itemlayout, null);viewHolder.ivIcon = (ImageView)convertView.findViewById(R.id.iv_icon);viewHolder.tvTitle = (TextView)convertView.findViewById(R.id.tv_title);viewHolder.tvContent = (TextView)convertView.findViewById(R.id.tv_content);convertView.setTag(viewHolder);}else{viewHolder = (ViewHolder) convertView.getTag();}viewHolder.ivIcon.setImageResource(R.drawable.ic_launcher);// default pictureString url = mList.get(position).newsIconUrl;// 用于解除图片多次加载,错位的问题viewHolder.ivIcon.setTag(url);/*// 使用线程的方式实现图片的异步加载:new ImageLoader().showImageByThread(viewHolder.ivIcon, url);*//* 这种方法每次都new一个ImageLoader,会导致ImageLoader中创建多次LruCache// AsyncTask异步方法加载图片:new ImageLoader().showImageByAsyncTask(viewHolder.ivIcon, url);*/mImageLoader.showImageByAsyncTask(viewHolder.ivIcon, url);viewHolder.tvTitle.setText(mList.get(position).newsTitle);viewHolder.tvContent.setText(mList.get(position).newsContent);return convertView;}/* * 用文艺式 * */class ViewHolder{public TextView tvTitle,tvContent;public ImageView ivIcon;}}
这样就可以实现图片的缓存机制了。
第十二步:滚动时的高效优化:
改MainActivity中的一些传参问题:
package com.example.asynctaskdemo2;import java.io.BufferedReader;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 java.net.MalformedURLException;import java.net.URL;import org.json.JSONArray;import org.json.JSONException;import org.json.JSONObject;import android.os.AsyncTask;import android.os.Bundle;import android.app.Activity;import android.util.Log;import android.widget.ListView;public class MainActivity extends Activity {private ListView mListView;private static String URL = "http://www.imooc.com/api/teacher?type=4&num=30";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mListView = (ListView) findViewById(R.id.lv_main);new NewAsyncTask().execute(URL);}private List<NewsBean> getJsonData(String url) {List<NewsBean> newsBeanList = new ArrayList<NewsBean>();// 此句功能与url.openConnection().getInputStream()相同// 可根据URL直接联网获取网络数据,简单粗暴!// 返回值类型为InputStream。// 用URL必须前面引入:import java.net.URL;String jsonString = null;try {jsonString = readStream(new URL(url).openStream());JSONObject jsonObject;NewsBean newsBean;try {// 这边很神奇啊,jsonObject = new JSONObject(jsonString);JSONArray jsonArray = jsonObject.getJSONArray("data");for (int i = 0; i < jsonArray.length(); i++) {jsonObject = jsonArray.getJSONObject(i);newsBean = new NewsBean();newsBean.newsIconUrl = jsonObject.getString("picSmall");newsBean.newsTitle = jsonObject.getString("name");newsBean.newsContent = jsonObject.getString("description");newsBeanList.add(newsBean);}} catch (JSONException e) {e.printStackTrace();}} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}// Log.d("xys",jsonString);return newsBeanList;}/* * 通过InputStream去读取网络信息: 传来的参数是一个InputStream的字节流is, * 通过InputStreamReader将字节流转化为字符流, 再通过BufferedReader将字符流以Buffer的形式读取出来, * 最终拼接到result里面。 这样就完成了整个数据的读取。 */private String readStream(InputStream is) {InputStreamReader isr;String result = "";try {String line = "";isr = new InputStreamReader(is, "utf-8");BufferedReader br = new BufferedReader(isr);while ((line = br.readLine()) != null) {result += line;}} catch (UnsupportedEncodingException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return result;}/* * 定义内部类: <Params, Progress, Result> */class NewAsyncTask extends AsyncTask<String, Void, List<NewsBean>> {@Overrideprotected List<NewsBean> doInBackground(String... params) {// params[0]为请求网站,因为只传了一个网址,所以只取0即可。return getJsonData(params[0]);}@Overrideprotected void onPostExecute(List<NewsBean> newsBeans) {super.onPostExecute(newsBeans);NewsAdapter adapter = new NewsAdapter(MainActivity.this,newsBeans,mListView);mListView.setAdapter(adapter);}}}改NewsAdapter来实现OnScrollListener滚动监听:
package com.example.asynctaskdemo2;import java.util.List;import com.example.asynctaskdemo2.NewsBean;import com.example.asynctaskdemo2.R;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.AbsListView;import android.widget.AbsListView.OnScrollListener;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.ListView;import android.widget.TextView;/* * 实现滚动监听: * */public class NewsAdapter extends BaseAdapter implements OnScrollListener {private List<NewsBean> mList;private LayoutInflater mInflater;private ImageLoader mImageLoader;private int mStart, mEnd;// 标识下面URLS中的位置public static String[] URLS;// 保存当前我们获得的所有的URL地址,public便于在其他类中使用。public NewsAdapter(Context context, List<NewsBean> data,ListView listView) {mList = data;mInflater = LayoutInflater.from(context);mImageLoader = new ImageLoader(listView);// 将所有NewsBean中的URL对象保存在URLS中:URLS = new String[data.size()];for(int i=0; i<data.size(); i++){URLS[i] = data.get(i).newsIconUrl;}// 一定要注册对应的事件:listView.setOnScrollListener(this);}@Overridepublic int getCount() {return mList.size();}@Overridepublic Object getItem(int arg0) {return mList.get(arg0);}@Overridepublic long getItemId(int arg0) {return arg0;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder viewHolder = null;if (convertView == null) {viewHolder = new ViewHolder();convertView = mInflater.inflate(R.layout.itemlayout, null);viewHolder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon);viewHolder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title);viewHolder.tvContent = (TextView) convertView.findViewById(R.id.tv_content);convertView.setTag(viewHolder);} else {viewHolder = (ViewHolder) convertView.getTag();}viewHolder.ivIcon.setImageResource(R.drawable.ic_launcher); // default// pictureString url = mList.get(position).newsIconUrl;// 用于解除图片多次加载,错位的问题viewHolder.ivIcon.setTag(url);/* * // 使用线程的方式实现图片的异步加载: new * ImageLoader().showImageByThread(viewHolder.ivIcon, url); *//* * 这种方法每次都new一个ImageLoader,会导致ImageLoader中创建多次LruCache // * AsyncTask异步方法加载图片: new * ImageLoader().showImageByAsyncTask(viewHolder.ivIcon, url); */mImageLoader.showImageByAsyncTask(viewHolder.ivIcon, url);viewHolder.tvTitle.setText(mList.get(position).newsTitle);viewHolder.tvContent.setText(mList.get(position).newsContent);return convertView;}/* * 用文艺式 */class ViewHolder {public TextView tvTitle, tvContent;public ImageView ivIcon;}/* * 整个滑动过程中都调用: */@Overridepublic void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItem) {mStart = firstVisibleItem;mEnd = firstVisibleItem + visibleItemCount;}/* * 滑动状态改变时调用: */@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {// 如果处于不滑动的状态:加载可见项if (scrollState == SCROLL_STATE_IDLE) {mImageLoader.loadImages(mStart, mEnd);} else {mImageLoader.cancleAllTasks();}}}改ImageLoader:
package com.example.asynctaskdemo2;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 mUrl;// 创建Cache// 第一个参数是需要保存对象的名字,第二个参数是保存对象private LruCache<String, Bitmap> mCaches;private ListView mListView;private Set<NewsAsyncTask> mTask;public ImageLoader(ListView listview){mListView = listview;mTask = new HashSet<NewsAsyncTask>();// 获取最大可用内存:int maxMemory = (int) Runtime.getRuntime().maxMemory();// 设定LruCache可用范围:int cacheSize = maxMemory / 4;// 初始化LruCache的大小:mCaches = new LruCache<String, Bitmap>(cacheSize){@Overrideprotected int sizeOf(String key, Bitmap value) {// 返回当前存入对象实际大小。在每次存入缓存的时候调用。return value.getByteCount();}};}/* * 将内容存到缓存中:增加之前先校验是否存在。 * */public void addBitmapToCache(String url, Bitmap bitmap){if(getBitmapFromCache(url) == null){mCaches.put(url, bitmap);}}/* * 将内容从缓存中取出来: * */public Bitmap getBitmapFromCache(String url){return mCaches.get(url);}private Handler mHandler = new Handler() {@Overridepublic void handleMessage(android.os.Message msg) {super.handleMessage(msg);// 判断相同才设置图片,避免图片出现错位现象if (mImageView.getTag().equals(mUrl)) {mImageView.setImageBitmap((Bitmap) msg.obj);}};};/* * 用线程的方式实现图片的异步加载: Android的三线程模型,非主线程不能够在子线程中之间编辑UI, 我们只能通过Handler来做一个消息的传递 */public void showImageByThread(ImageView imageView, final String url) {mImageView = imageView;mUrl = url;// 创建一个简单的线程new Thread() {@Overridepublic void run() {super.run();Bitmap bitmap = getBitmapFromURL(url);// 通过Handler的Message来传递BitmapMessage message = Message.obtain();message.obj = bitmap;mHandler.sendMessage(message);}}.start();}/* * 从一个url获取一个Bitmap */public Bitmap getBitmapFromURL(String urlString) {Bitmap bitmap;InputStream is = null;try {URL url = new URL(urlString);HttpURLConnection connection = (HttpURLConnection) url.openConnection();is = new BufferedInputStream(connection.getInputStream());// 通过Decode将InputStream转化为Bitmap:bitmap = BitmapFactory.decodeStream(is);connection.disconnect();return bitmap;} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {try {is.close();} catch (IOException e) {e.printStackTrace();}}return null;}/* * 用AsyncTask的方式实现图片的异步加载: */public void showImageByAsyncTask(ImageView imageView, String url) {// 先判断缓存中是否已经存在图片,避免多次下载:Bitmap bitmap = getBitmapFromCache(url);// 如果缓存中没有,那么必须去网络下载。if(bitmap == null){imageView.setImageResource(R.drawable.ic_launcher);}else{// 如果有了就直接使用。imageView.setImageBitmap(bitmap);}}/* * 加载从start到end的item项 * */public void loadImages(int start,int end){for(int i=start; i<end; i++){// 从NewsAdapter中获取想要的URL:String url = NewsAdapter.URLS[i];// 先判断缓存中是否已经存在图片,避免多次下载:Bitmap bitmap = getBitmapFromCache(url);// 如果缓存中没有,那么必须去网络下载。if(bitmap == null){NewsAsyncTask task = new NewsAsyncTask(url);task.execute(url);mTask.add(task);}else{// 如果有了就直接使用。ImageView imageView = (ImageView) mListView.findViewWithTag(url);imageView.setImageBitmap(bitmap);}}}/* * 取消当前所有的下载任务: * */public void cancleAllTasks(){if(mTask != null){for(NewsAsyncTask task:mTask){task.cancel(false);}}}/* * 创建NewsAsyncTask来继承实现AsyncTask,实现具体的异步加载过程。 */private class NewsAsyncTask extends AsyncTask<String, Void, Bitmap> {//private ImageView mImageView;private String mUrl; // 用于解除图片多次加载,错位的问题public NewsAsyncTask(String url) {//mImageView = imageView;mUrl = url;}@Overrideprotected Bitmap doInBackground(String... params) {String url = params[0];// 从网络获取图片:Bitmap bitmap = getBitmapFromURL(url);if(bitmap != null){// 将不在缓存的图片加入缓存:addBitmapToCache(url, bitmap);}return bitmap;}@Overrideprotected void onPostExecute(Bitmap bitmap) {super.onPostExecute(bitmap);// 用于解除图片多次加载,错位的问题ImageView imageView = (ImageView) mListView.findViewWithTag(mUrl);if(imageView != null && bitmap != null){imageView.setImageBitmap(bitmap);}mTask.remove(this);}}}效果是当只有滚动结束以后才会加载图片。
但有一个缺点是,第一次开启程序,由于没有滚动,所以第一次也就不会加载。
第十三步:首次启动预加载:
只需要改NewsAdapter添加一个标识即可:
package com.example.asynctaskdemo2;import java.util.List;import com.example.asynctaskdemo2.NewsBean;import com.example.asynctaskdemo2.R;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.AbsListView;import android.widget.AbsListView.OnScrollListener;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.ListView;import android.widget.TextView;/* * 实现滚动监听: * */public class NewsAdapter extends BaseAdapter implements OnScrollListener {private List<NewsBean> mList;private LayoutInflater mInflater;private ImageLoader mImageLoader;private int mStart, mEnd;// 标识下面URLS中的位置public static String[] URLS;// 保存当前我们获得的所有的URL地址,public便于在其他类中使用。private boolean mFirstIn;// 第一次进入程序标识public NewsAdapter(Context context, List<NewsBean> data,ListView listView) {mList = data;mInflater = LayoutInflater.from(context);mImageLoader = new ImageLoader(listView);// 将所有NewsBean中的URL对象保存在URLS中:URLS = new String[data.size()];for(int i=0; i<data.size(); i++){URLS[i] = data.get(i).newsIconUrl;}// 第一次启动:mFirstIn = true;// 一定要注册对应的事件:listView.setOnScrollListener(this);}@Overridepublic int getCount() {return mList.size();}@Overridepublic Object getItem(int arg0) {return mList.get(arg0);}@Overridepublic long getItemId(int arg0) {return arg0;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder viewHolder = null;if (convertView == null) {viewHolder = new ViewHolder();convertView = mInflater.inflate(R.layout.itemlayout, null);viewHolder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon);viewHolder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title);viewHolder.tvContent = (TextView) convertView.findViewById(R.id.tv_content);convertView.setTag(viewHolder);} else {viewHolder = (ViewHolder) convertView.getTag();}viewHolder.ivIcon.setImageResource(R.drawable.ic_launcher); // default// pictureString url = mList.get(position).newsIconUrl;// 用于解除图片多次加载,错位的问题viewHolder.ivIcon.setTag(url);/* * // 使用线程的方式实现图片的异步加载: new * ImageLoader().showImageByThread(viewHolder.ivIcon, url); *//* * 这种方法每次都new一个ImageLoader,会导致ImageLoader中创建多次LruCache // * AsyncTask异步方法加载图片: new * ImageLoader().showImageByAsyncTask(viewHolder.ivIcon, url); */mImageLoader.showImageByAsyncTask(viewHolder.ivIcon, url);viewHolder.tvTitle.setText(mList.get(position).newsTitle);viewHolder.tvContent.setText(mList.get(position).newsContent);return convertView;}/* * 用文艺式 */class ViewHolder {public TextView tvTitle, tvContent;public ImageView ivIcon;}/* * 整个滑动过程中都调用: * 第一次加载的过程中会调用 */@Overridepublic void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {mStart = firstVisibleItem;mEnd = firstVisibleItem + visibleItemCount;// 第一次显示的时候调用:if(mFirstIn && visibleItemCount > 0){mImageLoader.loadImages(mStart, mEnd);mFirstIn = false;}}/* * 滑动状态改变时调用: * 它在第一次加载的时候不会调用 */@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {// 如果处于不滑动的状态:加载可见项if (scrollState == SCROLL_STATE_IDLE) {mImageLoader.loadImages(mStart, mEnd);} else {mImageLoader.cancleAllTasks();}}}
总结:
- AsyncTask案例——异步加载ListView内容,包括优化
- Android ListView&异步加载的学习(三)——AsyncTask加载图片&运用Lru算法优化图片加载
- ListView数据异步加载图片和使用AsyncTask优化
- ListView异步加载优化
- ListView异步加载优化
- listview异步加载优化
- AsyncTask案例——异步加载一张图片并显示进度条
- Android:ListView数据异步加载、Handler、AsyncTask
- 异步加载(AsyncTask异步任务、Handler、Json解析、Lrucache缓存、ListView滑动优化等来实现ListView图文混排)
- listview异步图片加载优化
- ListView的异步加载&优化
- Android优化系列——控件优化(ListView 异步加载图片优化,SoftReference)
- AsyncTask实现ListView中异步加载网络图片
- ListView的异步加载(笔记,多线程和AsyncTask)
- 利用AsyncTask高效异步加载图片,适用于ListView 和GridView
- Android studio异步数据加载ListView+Adapter+AsyncTask
- Android listview异步图片加载之优化篇——ImageLoader
- Listview异步加载图片之优化篇
- 课程设计
- [LeetCode]First Missing Positive(!!!)
- 编程语言的本质:1.编程语言的计算能力
- Lucene.Net 与 盘古分词
- Combination Lock 号码锁 From USACO
- AsyncTask案例——异步加载ListView内容,包括优化
- 搞懂HMM(隐马尔可夫模型)
- GUID转换成16位字符串或19位唯一字符串
- 例题3-4 猜数字游戏的提示(Master-Mind Hints)
- 1576 最长严格上升子序列
- UNIX环境高级编程学习笔记(十)为何 fork 函数会有两个不同的返回值
- C++宏中的“#”与“##”用法
- Area - POJ 1265 Pick定理
- 黑马程序员——基础学习(十二)异常(Throwable)类、文件(File)类及递归