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>


第三步:了解JSON是什么东东:

输入网站打开: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;}


第五步:将JSON中的数据加载到程序中来:别忘了添加INTERNET访问网络权限:

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;}


第九步:具体使用Thread线程实现图片的异步加载:并不能够真正理解线程的操作方式:

修改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();}}}

总结:













0 0