ListView的優(yōu)化問(wèn)題可以說(shuō)是面試的必考題。我之前看過(guò)一遍視頻 Android必學(xué)-異步加載,感覺(jué)里面講解的知識(shí)都是ListView優(yōu)化常用的,這里我就通過(guò)里面的示例來(lái)做一個(gè)總結(jié)。本篇準(zhǔn)備通過(guò)這個(gè)示例來(lái)談一談ListView的優(yōu)化,通過(guò)分析在寫(xiě)code的過(guò)程中遇到的問(wèn)題,來(lái)談一下解決辦法。
示例是這樣的:
由http://www.imooc.com/api/teacher?type=4&num=30 中加載json格式的數(shù)據(jù),解析出來(lái),然后用ListView顯示出來(lái)。下面是完成后的圖片(沒(méi)有找到有效設(shè)置圖片大小的方法,大家有知道的話,留言給我)
先看我的示例listView的item的布局:


1.對(duì)于這樣一個(gè)需求,我們首先應(yīng)該是解析得到的數(shù)據(jù),然后將得到的數(shù)據(jù)提供給ListView的Adapter,有關(guān)于這部分的代碼,寫(xiě)在了MainActivity.java,我放到文章后面,畢竟重點(diǎn)不在這。
2.當(dāng)獲得到數(shù)據(jù)之后,接下來(lái)就是編寫(xiě)Adapter了。
-
使用ViewHolder模式來(lái)提高效率
Viewholder模式充分了ListView的視圖緩存機(jī)制,避免了每次在調(diào)用getView的時(shí)候都去通過(guò)findViewById實(shí)例化數(shù)據(jù)。《Android群英傳》中說(shuō):據(jù)測(cè)試,使用ViewHolder將提高50%的效率。對(duì)應(yīng)代碼中就是這樣,先在NewsAdapter(本示例中的Adapter)新建一個(gè)內(nèi)部類(lèi):(詳細(xì)代碼見(jiàn)文章后的NewsAdapter.java)
//使用ViewHolder
private static class ViewHolder {
private TextView tvTitle, tvContent;
private ImageView ivIcon;
}
在重寫(xiě)的getView方法中這樣寫(xiě):
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
//判斷是否有緩存
if (convertView == null) {
//通過(guò)LayoutInflate實(shí)例化布局
viewHolder = new ViewHolder();
convertView = mInflater.inflate(R.layout.item_layout, parent, false);
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 {
//通過(guò)tag找到緩存的布局
viewHolder = (ViewHolder) convertView.getTag();
}
NewsBean newsBean = newsBeanList.get(position);
String urlString = newsBean.newsIconUrl;
viewHolder.ivIcon.setTag(urlString); // 將ImageView與url綁定
//普通異步加載
// mImageLoader.showImageByThread(viewHolder.ivIcon,urlString);
mImageLoader.showImageByAsyncTask(viewHolder.ivIcon,urlString);
viewHolder.tvTitle.setText(newsBean.newsTitle);
viewHolder.tvContent.setText(newsBean.newsContent);
return convertView;
}
-
異步加載:耗時(shí)的操作放在異步線程中
如果在adapter中的某些操作需要耗費(fèi)大量的時(shí)間,這個(gè)時(shí)候就要用到異步線程來(lái)進(jìn)行異步就在數(shù)據(jù)。比如:現(xiàn)在要加載圖片,此時(shí)我們需要根據(jù)url訪問(wèn)網(wǎng)絡(luò)得到數(shù)據(jù),然后將數(shù)據(jù)解析為Bitmap設(shè)置給View。這些操作如果不進(jìn)行異步處理而直接放入adapter,可想而知,我們的ListView會(huì)有多卡。
這里向大家提供兩種異步加載線程的方式:(代碼查看下面的ImageLoader.java)
向網(wǎng)絡(luò)獲得數(shù)據(jù),異步中需要進(jìn)行的操作:
public Bitmap getBitmapFormURL(String urlString) {
Bitmap bitmap;
InputStream inputStream = null;
try {
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
inputStream = new BufferedInputStream(conn.getInputStream()); //得到圖片的數(shù)據(jù)流
bitmap = BitmapFactory.decodeStream(inputStream); //根據(jù)數(shù)據(jù)流來(lái)解析出圖片的bitmap
conn.disconnect();
return bitmap;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
一種是通過(guò)多線程方式通過(guò)Handler+Message進(jìn)行異步加載
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//這個(gè)getTag是下面的問(wèn)題要講的優(yōu)化,大家可以先看看
if (mImageView.getTag().equals(mUrl)) { //當(dāng)url標(biāo)記和原先設(shè)置的一樣時(shí),才設(shè)置ImageView
mImageView.setImageBitmap((Bitmap) msg.obj);
}
}
};
public void showImageByThread(ImageView imageView, final String url) {
this.mImageView = imageView;
this.mUrl = url;
new Thread() {
@Override
public void run() {
super.run();
Bitmap bitmap = getBitmapFormURL(url);
Message message = Message.obtain();
message.obj = bitmap;
handler.sendMessage(message);
}
}.start();
}
另一種是通過(guò)AsyncTask來(lái)進(jìn)行異步操作
AsyncTask的使用方法,不熟悉的可以學(xué)習(xí)下,這里不講了,畢竟不是重點(diǎn)。
public void showImageByAsyncTask(ImageView imageView,String url){
new NewsAsyncTask(imageView,url).execute(url);
}
構(gòu)建的AsyncTask的內(nèi)部類(lèi)
class NewsAsyncTask extends AsyncTask<String,Void,Bitmap>{
private ImageView myImageView;
private String mUrl;
public NewsAsyncTask(ImageView imageView,String url){
myImageView = imageView;
mUrl = url;
}
//String...params是可變參數(shù)接受execute中傳過(guò)來(lái)的參數(shù)
@Override
protected Bitmap doInBackground(String... params) {
String url=params[0];
//這里同樣調(diào)用我們的getBitmapFromeUrl
Bitmap bitmap = getBitmapFromUrl(params[0]);
return bitmap;
}
//這里的bitmap是從doInBackgroud中方法中返回過(guò)來(lái)的
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
imageView.setImageBitmap(bitmap);
}
}
listView錯(cuò)位加載問(wèn)題
當(dāng)上面的代碼寫(xiě)完時(shí),如果先不用讓大家先看看的mImageView.getTag().equals(mUrl)判斷一下(上面的代碼并不完整,下面會(huì)講),你會(huì)發(fā)現(xiàn)如下圖的情況,在Listview中的各個(gè)item加載的過(guò)程中,出現(xiàn)了item錯(cuò)位的情況。一張圖片在多個(gè)位置顯示,過(guò)了一段時(shí)間才正常。

對(duì)于上面的情況,我們先來(lái)看看產(chǎn)生這種情況的原因(下面內(nèi)容主要引用自android listview 異步加載圖片并防止錯(cuò)位):

網(wǎng)上找了一張圖, listview 異步加載圖片之所以錯(cuò)位的根本原因是重用了 convertView 且有異步操作.
如果不重用 convertView 不會(huì)出現(xiàn)錯(cuò)位現(xiàn)象, 重用 convertView 但沒(méi)有異步操作也不會(huì)有問(wèn)題。
我簡(jiǎn)單分析一下:
當(dāng)重用 convertView 時(shí),最初一屏顯示 7 條記錄, getView 被調(diào)用 7 次,創(chuàng)建了 7 個(gè) convertView.
當(dāng) Item1 劃出屏幕, Item8 進(jìn)入屏幕時(shí),這時(shí)沒(méi)有為 Item8 創(chuàng)建新的 view 實(shí)例, Item8 復(fù)用的是
Item1 的 view 如果沒(méi)有異步不會(huì)有任何問(wèn)題,雖然 Item8 和 Item1 指向的是同一個(gè) view,但滑到
Item8 時(shí)刷上了 Item8 的數(shù)據(jù),這時(shí) Item1 的數(shù)據(jù)和 Item8 是一樣的,因?yàn)樗鼈冎赶虻氖峭粔K內(nèi)存,
但 Item1 已滾出了屏幕你看不見(jiàn)。當(dāng) Item1 再次可見(jiàn)時(shí)這塊 view 又涮上了 Item1 的數(shù)據(jù)。
但當(dāng)有異步下載時(shí)就有問(wèn)題了,假設(shè) Item1 的圖片下載的比較慢,Item8 的圖片下載的比較快,你滾上去
使 Item8 可見(jiàn),這時(shí) Item8 先顯示它自己下載的圖片沒(méi)錯(cuò),但等到 Item1 的圖片也下載完時(shí)你發(fā)現(xiàn)
Item8 的圖片也變成了 Item1 的圖片,因?yàn)樗鼈儚?fù)用的是同一個(gè) view。 如果 Item1 的圖片下載的比
Item8 的圖片快, Item1 先刷上自己下載的圖片,這時(shí)你滑下去,Item8 的圖片還沒(méi)下載完, Item8
會(huì)先顯示 Item1 的圖片,因?yàn)樗鼈兪峭豢靸?nèi)存,當(dāng) Item8 自己的圖片下載完后 Item8 的圖片又刷成
了自己的,你再滑上去使 Item1 可見(jiàn), Item1 的圖片也會(huì)和 Item8 的圖片是一樣的,
因?yàn)樗鼈冎赶虻氖峭粔K內(nèi)存。
解決的辦法由很多種,這里說(shuō)一下最簡(jiǎn)單的一種:使用findViewWithTag
在NewsAdapter中為ImageView設(shè)置Tag標(biāo)識(shí)位:
//將url設(shè)為imagView的標(biāo)識(shí)位
String urlString = newsBean.newsIconUrl;
viewHolder.ivIcon.setTag(urlString); // 將ImageView與url綁定
然后再加載的過(guò)程中通過(guò)url來(lái)判斷對(duì)應(yīng)imagview位置是否一致來(lái)決定是否加載。
if (mImageView.getTag().equals(mUrl)) {
//當(dāng)url標(biāo)記和原先設(shè)置的一樣時(shí),才設(shè)置ImageView
mImageView.setImageBitmap((Bitmap) msg.obj);
}
為圖片設(shè)置緩存
設(shè)置緩存的重要性不言而喻,在加載數(shù)據(jù)的過(guò)程中,你不可能每次都從網(wǎng)絡(luò)上加載(除非數(shù)據(jù)量小)。如果即之一這樣做就要面對(duì)兩個(gè)問(wèn)題:1.加載數(shù)據(jù)浪費(fèi)的時(shí)間 2.加載數(shù)據(jù)消耗的流量 。信不信用戶(hù)一言不合就把你這破app給卸載了。。。。。。
設(shè)置緩存的原理這里也不再說(shuō)了,要說(shuō)的話估計(jì)又成一片博文了。這里介紹用法,讓你看看怎么寫(xiě):(這部分代碼,在文章末尾的ImageLoader.java中)
1.首先聲明一個(gè)LrcCache
private LruCache<String,Bitmap> mMemoryCaches;
2.然后初始化
//下面是建立緩存
int maxMemory = (int) Runtime.getRuntime().maxMemory(); //運(yùn)行時(shí)最大內(nèi)存
int cacheSize = maxMemory/4; //緩存的大小
mCaches = new LruCache<String, Bitmap>(cacheSize){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
}
};
3.添加方法,在適當(dāng)?shù)牡胤秸{(diào)用
//將bitmap添加到緩存
public void addBitmapToCache(String url,Bitmap bitmap){
if (getBitmapFormCache(url) == null){
mCaches.put(url, bitmap);
}
}
//從緩存中獲取數(shù)據(jù)
public Bitmap getBitmapFormCache(String url){
return mCaches.get(url);
}
ListView的滑動(dòng)時(shí)停止加載和分頁(yè)加載
先說(shuō)分頁(yè)加載,我們不用每次把ListView所有的Item都一次性加載完畢,這樣做沒(méi)必要也很累。我們僅僅需要加載那部分顯示在屏幕部分的Item即可,這樣所要加載的數(shù)據(jù)就減少了很多。另一方面,我們需要考慮另一個(gè)問(wèn)題,當(dāng)用戶(hù)滑動(dòng)時(shí),顯示在屏幕的Item會(huì)不斷的變化,如果只是加載顯示在屏幕的Item,這也沒(méi)有必要,因此我們應(yīng)該在停止滑動(dòng)時(shí)再加載數(shù)據(jù)。這樣說(shuō),understand?
實(shí)現(xiàn)步驟:
1.讓NewsAdapter實(shí)現(xiàn)接口AbsListView.OnScrollListener,并復(fù)寫(xiě)里面的方法
2.在復(fù)寫(xiě)的方法里面進(jìn)行一些操作。
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState){
case SCROLL_STATE_IDLE: //滑動(dòng)停止時(shí)。
mImageLoader.loadImages(mStart, mEnd);
break;
case SCROLL_STATE_TOUCH_SCROLL: //正在滑動(dòng)時(shí)
mImageLoader.cancelAllTasks();
break;
case SCROLL_STATE_FLING: //手指拋動(dòng)時(shí),即手指用力滑動(dòng)在離開(kāi)后ListView由于慣性而繼續(xù)滑動(dòng)
break;
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
mStart = firstVisibleItem;
mEnd = firstVisibleItem + visibleItemCount;
//第一次的時(shí)候預(yù)加載
if (mFirstIn && visibleItemCount > 0){
mImageLoader.loadImages(mStart, mEnd);
mFirstIn = false;
}
}
可以看到在滑動(dòng)停止時(shí)我們調(diào)用了ImageLoader類(lèi)的loadIamge方法,這是一個(gè)加載圖片的方法,用于加載Item對(duì)應(yīng)的第mSart項(xiàng)t到第mEnd項(xiàng)之間的數(shù)據(jù)(這兩個(gè)數(shù)據(jù)怎么來(lái)的,你先把這個(gè)問(wèn)題放在心里),看下loadImage法的代碼實(shí)現(xiàn):
public void loadImages(int start, int end){
for (int i = start; i < end; i++){
String url = NewsAdapter.URLS[i];
//由緩存中得到bitmap
Bitmap bitmap = getBitmapFormCache(url);
if (bitmap == null){
//當(dāng)bitmap為空時(shí),由AsyncTask進(jìn)行加載,并在onPostExecute()方法中setImageBitmap
NewsAsyncTask task = new NewsAsyncTask(url);
task.execute(url);
mAsyncTask.add(task);
} else {
//當(dāng)bitmap不為空時(shí),直接進(jìn)行setImageBitmap
ImageView imageView = (ImageView) mListView.findViewWithTag(url);
imageView.setImageBitmap(bitmap);
}
}
}
由代碼中可以看到我們由NewsAdatper.URLS[]中取出了start到end的url,然后加載了這些數(shù)據(jù),我們看下這個(gè)數(shù)組,這是在NewsAdapter的構(gòu)造器中,可以明顯看到里面存放了所有Item的圖片的url:
//將圖片的url存儲(chǔ)在數(shù)組中
URLS = new String[data.size()];
for (int i = 0; i < data.size(); i++) {
URLS[i] = data.get(i).newsIconUrl;
}
此時(shí)再來(lái)看NewsAsyncTask的代碼:
//參數(shù)1:?jiǎn)?dòng)任務(wù)輸入的參數(shù),參數(shù)2:后臺(tái)任務(wù)執(zhí)行的百分比,參數(shù)3,后臺(tái)執(zhí)行任務(wù)的返回方法
private class NewsAsyncTask extends AsyncTask<String, Void, Bitmap> {
private String mUrl;
public NewsAsyncTask(String stringUrl) {
mUrl = stringUrl;
}
//doInBackground方法的參數(shù)是上面輸入的第一個(gè)參數(shù),返回的對(duì)象會(huì)傳遞給onPostExecute方法
@Override
protected Bitmap doInBackground(String... params) {
String url = params[0];
Bitmap bitmap = getBitmapFormURL(url);
if (bitmap != null){
addBitmapToCache(url,bitmap); //將bitmap添加到緩存
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
//根據(jù)url從listView中找到對(duì)應(yīng)的ImageView
ImageView imageView = (ImageView) mListView.findViewWithTag(mUrl);
if (imageView != null && bitmap != null){
imageView.setImageBitmap(bitmap); //為imageView設(shè)置圖片
}
mAsyncTask.remove(this);
}
}
可以看到這就是根據(jù)url為所對(duì)應(yīng)ImageView設(shè)置圖片。看到這就只有一個(gè)問(wèn)題了,那就是mStart和mEnd是怎么得到的,再看代碼我們重寫(xiě)的onScroll方法:
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
mStart = firstVisibleItem;
mEnd = firstVisibleItem + visibleItemCount;
//第一次的時(shí)候預(yù)加載
if (mFirstIn && visibleItemCount > 0){
mImageLoader.loadImages(mStart, mEnd);
mFirstIn = false;
}
}
這個(gè)方法有三個(gè)參數(shù),對(duì)應(yīng)的為
firstVisibleItem:ListView所有當(dāng)前可見(jiàn)的Item第一個(gè)Item
visibleItemCount:可見(jiàn)Item的總數(shù)
totalItemCount :所有Item的總數(shù)
這樣一來(lái)大家是不是都懂了。
code
MainActivity.java
public class MainActivity extends AppCompatActivity {
private ListView listView;
private static final 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);
listView = (ListView) findViewById(R.id.lv);
new MainAsyncTask().execute(url);
}
public List<NewsBean> getJsonData(String url){
List<NewsBean> newsBeanList = new ArrayList<>();
try {
String jsonString = readStream(new URL(url).openStream());//利用readStream得到String數(shù)據(jù)
Log.e("JSON",jsonString); //打印出string數(shù)據(jù)
//下面解析得到的json數(shù)據(jù)
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 (IOException e) {
e.printStackTrace();
}
return newsBeanList;
}
//由輸入流中讀取數(shù)據(jù)并將數(shù)據(jù)返回
public String readStream(InputStream in){
InputStreamReader reader;
String result = "";
String line = "";
try {
reader = new InputStreamReader(in, "UTF-8");
BufferedReader br = new BufferedReader(reader);
while((line = br.readLine())!=null){
result += line;
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
//異步線程類(lèi)
class MainAsyncTask extends AsyncTask<String, Void, List<NewsBean>>{
//該方法運(yùn)行在后臺(tái)線程中
@Override
protected List<NewsBean> doInBackground(String... params) {
return getJsonData(params[0]);
}
@Override
protected void onPostExecute(List<NewsBean> newsBeanList) {
super.onPostExecute(newsBeanList);
NewsAdapter newsAdapter = new NewsAdapter(MainActivity.this, newsBeanList, listView);
listView.setAdapter(newsAdapter);
}
}
}
NewsAdapter.java
public class NewsAdapter extends BaseAdapter implements AbsListView.OnScrollListener {
private List<NewsBean> newsBeanList = new ArrayList<>();
private LayoutInflater mInflater;
private ImageLoader mImageLoader;
private int mStart, mEnd;
public static String[] URLS;
private boolean mFirstIn;
public NewsAdapter(Context context, List<NewsBean> data, ListView listView) {
newsBeanList = data;
mInflater = LayoutInflater.from(context);
mImageLoader = new ImageLoader(listView);
//將圖片的url存儲(chǔ)在數(shù)組中
URLS = new String[data.size()];
for (int i = 0; i < data.size(); i++) {
URLS[i] = data.get(i).newsIconUrl;
}
listView.setOnScrollListener(this);
mFirstIn = true;
}
@Override
public int getCount() {
return newsBeanList.size();
}
@Override
public Object getItem(int position) {
return newsBeanList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
//判斷是否有緩存
if (convertView == null) {
//通過(guò)LayoutInflate實(shí)例化布局
viewHolder = new ViewHolder();
convertView = mInflater.inflate(R.layout.item_layout, parent, false);
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 {
//通過(guò)tag找到緩存的布局
viewHolder = (ViewHolder) convertView.getTag();
}
NewsBean newsBean = newsBeanList.get(position);
String urlString = newsBean.newsIconUrl;
viewHolder.ivIcon.setTag(urlString); // 將ImageView與url綁定
//普通異步加載
// mImageLoader.showImageByThread(viewHolder.ivIcon,urlString);
mImageLoader.showImageByAsyncTask(viewHolder.ivIcon,urlString);
viewHolder.tvTitle.setText(newsBean.newsTitle);
viewHolder.tvContent.setText(newsBean.newsContent);
return convertView;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState){
case SCROLL_STATE_IDLE: //滑動(dòng)停止時(shí)。
mImageLoader.loadImages(mStart, mEnd);
break;
case SCROLL_STATE_TOUCH_SCROLL: //正在滑動(dòng)時(shí)
mImageLoader.cancelAllTasks();
break;
case SCROLL_STATE_FLING: //手指拋動(dòng)時(shí),即手指用力滑動(dòng)在離開(kāi)后ListView由于慣性而繼續(xù)滑動(dòng)
break;
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
mStart = firstVisibleItem;
mEnd = firstVisibleItem + visibleItemCount;
//第一次的時(shí)候預(yù)加載
if (mFirstIn && visibleItemCount > 0){
mImageLoader.loadImages(mStart, mEnd);
mFirstIn = false;
}
}
//使用ViewHolder
private static class ViewHolder {
private TextView tvTitle, tvContent;
private ImageView ivIcon;
}
}
ImageLoder.java
public class ImageLoader {
private ImageView mImageView;
private String mUrl;
private LruCache<String, Bitmap> mCaches;
private ListView mListView;
private Set<NewsAsyncTask> mAsyncTask;
public ImageLoader(ListView listView){
mListView = listView;
mAsyncTask = new HashSet<>();
//下面是建立緩存
int maxMemory = (int) Runtime.getRuntime().maxMemory(); //運(yùn)行時(shí)最大內(nèi)存
int cacheSize = maxMemory/4;
mCaches = new LruCache<String, Bitmap>(cacheSize){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
}
};
}
//將bitmap添加到緩存
public void addBitmapToCache(String url,Bitmap bitmap){
if (getBitmapFormCache(url) == null){
mCaches.put(url, bitmap);
}
}
//從緩存中獲取數(shù)據(jù)
public Bitmap getBitmapFormCache(String url){
return mCaches.get(url);
}
//===================================下面為普通異步加載===========================================
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (mImageView.getTag().equals(mUrl)) { //當(dāng)url標(biāo)記和原先設(shè)置的一樣時(shí),才設(shè)置ImageView
mImageView.setImageBitmap((Bitmap) msg.obj);
}
}
};
public void showImageByThread(ImageView imageView, final String url) {
this.mImageView = imageView;
this.mUrl = url;
new Thread() {
@Override
public void run() {
super.run();
Bitmap bitmap = getBitmapFormURL(url);
Message message = Message.obtain();
message.obj = bitmap;
handler.sendMessage(message);
}
}.start();
}
//====================上面是使用普通的異步加載,下面是使用AsyncTask進(jìn)行的異步加載==================
public Bitmap getBitmapFormURL(String urlString) {
Bitmap bitmap;
InputStream inputStream = null;
try {
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
inputStream = new BufferedInputStream(conn.getInputStream()); //得到圖片的數(shù)據(jù)流
bitmap = BitmapFactory.decodeStream(inputStream); //根據(jù)數(shù)據(jù)流來(lái)解析出圖片的bitmap
conn.disconnect();
return bitmap;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
//加載圖片
public void showImageByAsyncTask(ImageView ImageView, String url) {
Bitmap bitmap = getBitmapFormCache(url);
if (bitmap == null){
ImageView.setImageResource(R.mipmap.ic_launcher);
}else{
ImageView.setImageBitmap(bitmap);
}
}
public void cancelAllTasks(){
if (mAsyncTask != null){
for (NewsAsyncTask task : mAsyncTask){
task.cancel(false);
}
}
}
public void loadImages(int start, int end){
for (int i = start; i < end; i++){
String url = NewsAdapter.URLS[i];
//由緩存中得到bitmap
Bitmap bitmap = getBitmapFormCache(url);
if (bitmap == null){
//當(dāng)bitmap為空時(shí),由AsyncTask進(jìn)行加載,并在onPostExecute()方法中setImageBitmap
NewsAsyncTask task = new NewsAsyncTask(url);
task.execute(url);
mAsyncTask.add(task);
} else {
//當(dāng)bitmap不為空時(shí),直接進(jìn)行setImageBitmap
ImageView imageView = (ImageView) mListView.findViewWithTag(url);
imageView.setImageBitmap(bitmap);
}
}
}
//參數(shù)1:?jiǎn)?dòng)任務(wù)輸入的參數(shù),參數(shù)2:后臺(tái)任務(wù)執(zhí)行的百分比,參數(shù)3,后臺(tái)執(zhí)行任務(wù)的返回方法
private class NewsAsyncTask extends AsyncTask<String, Void, Bitmap> {
private String mUrl;
public NewsAsyncTask(String stringUrl) {
mUrl = stringUrl;
}
//doInBackground方法的參數(shù)是上面輸入的第一個(gè)參數(shù),返回的對(duì)象會(huì)傳遞給onPostExecute方法
@Override
protected Bitmap doInBackground(String... params) {
String url = params[0];
Bitmap bitmap = getBitmapFormURL(url);
if (bitmap != null){
addBitmapToCache(url,bitmap); //將bitmap添加到緩存
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
//根據(jù)url從listView中找到對(duì)應(yīng)的ImageView
ImageView imageView = (ImageView) mListView.findViewWithTag(mUrl);
if (imageView != null && bitmap != null){
imageView.setImageBitmap(bitmap);
}
mAsyncTask.remove(this);
}
}
}