版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
1、android 中 ListView 異步加載圖片時的圖片錯位問題解決方案android 中 ListView 異步加載圖片時的圖片錯位問題解 決方案分類: android 實例 android 基礎(chǔ)2012-12-12 21:21196 人閱讀評論 (0)收藏舉報 Android 中的 ListView 是一個非常常用的控件, 但是它 卻并不像想象中的那么簡單。 特別是當你需要在 ListView 中 展示大量網(wǎng)絡(luò)圖片的時候,處理不好輕則用戶體驗不佳,重 則 OOM ,異步線程丟失或者圖片錯位。關(guān)于其中的 OOM 和異步線程丟失的問題,是一個很龐大的 話題,本人能力有限,無法說清,只有遇到的
2、時候臨時找原 因,想辦法解決了。但是對于圖片錯位,卻是可以避免的, 今天我們就來說一說 ListView 異步加載圖片中的圖片錯位 問題。為什么會出現(xiàn)圖片錯位的問題呢?一般是重用了convertView 導(dǎo)致的。如果你重用了 convertView ,此時 convertView 中的 ImageView 的 id 值是相等的, 而我們在設(shè) 置 ImageView 的圖片時,是根據(jù) id 來設(shè)置的,此時就出現(xiàn) 了圖片錯位問題。這里童鞋們可以自己去測試一下,不重用 convertView ,也就是每次 getView 的時候,都使用 findViewById(R.id.xx) 去得到每一個 It
3、em 的 ImageView ,異 步下載圖片的方法也只是簡單的開一個 AsyncTask 執(zhí)行下 載。在這種情況下,圖片一般是不會產(chǎn)生錯位的。原因很簡 單,認真讀一讀前面的內(nèi)容就明白了。但是你如果真的在使 用這種方法來使用 getView 的話,并且圖片量比較大的時候, 你程序的性能肯定不會好到哪里去了。因此,重用 convertView 還是很有必要的。這里需要注意, convertView 是否為 null 會根據(jù) ListView 的 中布局標簽值的不同有區(qū)別,具體的內(nèi)容請參見這兩篇文章: android listview 連續(xù)調(diào)用 getview 問題分析及解決 Android Li
4、stView 中 getView 的原理如何在 ListView 中 放置多個 item 這也就是說,某種情況下你界面中的第一張和第二張圖片之 間就有可能產(chǎn)生錯位, 因為有可能第二個可見的 ImageView 就來自共用的 convertView 。 處理像這種圖片的異步加載的問題,我們的一般思路是:下 載的圖片根據(jù)圖片名稱存入到 SDCard 中,最新加載的圖片 存入到軟引用中。我們在 getView 中給 ImageView 設(shè)置圖 片的時候, 首先根據(jù) url ,從軟引用中讀取圖片數(shù)據(jù); 如果軟 引用中沒用, 則根據(jù) url( 對應(yīng)圖片名 )從 SDCard 中讀取圖片 數(shù)據(jù);如果 SD
5、Card 中也沒有,則從網(wǎng)絡(luò)上下載圖片,在圖 片下載完成后,回調(diào)主線中的方法更新 ImageView 。下面我 們就照著上面的思路,先把程序整出來再說吧。先看下效果 圖:布局文件有兩個,很簡單,一個表示 ListView(main.xml) , 一個表示 ListView 中的元素 (single_data.xml) ,如下: java view plaincopy<?xml version=1.0 encoding=utf-8?> <LinearLayout xmlns:android=/apk/res/andro id x
6、mlns:tools=/tools android:layout_width=fill_parent android:layout_height=fill_parent android:orientation=vertical android:background=android:color/darker_gray tools:context=.MainActivity > <ListView android:layout_width=fill_parent android:layout_height=wrap_content an
7、droid:cacheColorHint=nullandroid:id=+id/listview /> </LinearLayout>java view plaincopy<?xml version=1.0 encoding=utf-8?> <RelativeLayout xmlns:android=/apk/res/andro id android:layout_width=fill_parent android:layout_height=wrap_content android:background=a
8、ndroid:color/white > <ImageView android:layout_width=150dp android:layout_height=150dp android:scaleType=fitXY android:id=+id/image_view android:background=drawable/ic_launcher/> <TextView android:layout_width=wrap_content android:layout_height=wrap_content android:layout_alignTop=id/ima
9、ge_view android:layout_alignBottom=id/image_view android:layout_marginLeft=20dp android:layout_alignParentRight=true android:gravity=center_vertical android:layout_toRightOf=id/image_viewandroid:singleLine=true android:ellipsize=end android:text=string/hello android:id=+id/text_view /> </Relat
10、iveLayout>加入訪問網(wǎng)絡(luò)和讀取,寫入 sdcard 的權(quán)限。java view plaincopy<uses-permission android:name=android.permission.INTERNET/> <uses-permission android:name=android.permission.WRITE_EXTERNAL_S TORAGE/> <uses-permission android:name=android.permission.MOUNT_UNMOUNT_ FILESYSTEMS/> 接下來,我們來看看Main
11、Activity.java 。性能考慮,我們使用 convertView 和 ViewHolder 來重用控件。 這里涉及到比較關(guān)鍵的一步, 我們 會在 getView 的時候給 ViewHolder 中的 ImageView 設(shè)置 tag , 其值為要放置在該 ImageView 上的圖片的 url 地址。這個 tag 很重要,在異步下載圖片完成回調(diào)的方法中,我們使用 findViewWithTag(String url) 來找到 ListView 中對應(yīng)的 ImagView ,然后給該 ImageView 設(shè)置圖片即可。其他的就 是設(shè)置 adapter 的一般操作了。java view p
12、laincopypublic class MainActivity extendsActivity ListView mListView;MyListAdapterImageDownloader mDownloader;myListAdapter;private static final String TAG =MainActivity;int m_flag = 0;private staticfinal String URLS = /圖片地址就不貼了,自己去這篇帖子中找吧:/liongname/articles/2345087.html/其中有幾張圖
13、片訪問不了。;Overridepublic void onCreate(BundlesavedInstanceState)super.onCreate(savedInstanceState);setContentView(R.layout.main);Util.flag = 0;mListView = (ListView) findViewById(R.id.listview);myListAdapter = new MyListAdapter();mListView.setAdapter(myListAdapter);private class MyListAdapter extends B
14、aseAdapterprivate ViewHolder mHolder;Overridepublic int getCount()OverrideOverridereturn URLS.length;public Object getItem(int position)return URLSposition;public long getItemId(int position) return position; Overridepublic View getView(int position,View convertView, ViewGroup parent) / 只有當 convertV
15、iew 不存在的時候才去 inflate 子元素 if (convertView = null) convertView =getLayoutInflater().inflate(R.layout.single_data, null); mHolder = new ViewHolder(); mHolder.mImageView = (ImageView) convertView.findViewById(R.id.image_view); mHolder.mTextView = (TextView) convertView.findViewById(R.id.text_view); conv
16、ertView.setTag(mHolder); else mHolder = (ViewHolder) convertView.getTag(); final String url = URLSposition; mHolder.mTextView.setText(url != null ? url.substring(url.lastIndexOf(/) + 1) : ); mHolder.mImageView.setTag(URLSposition);if (mDownloader = null) mDownloader = new ImageDownloader(); / 這句 代碼的
17、作用是為了解決 convertView 被重用的時候,圖片預(yù)設(shè)的問題mHolder.mImageView.setImageResource(R.drawable.ic_launcher);if (mDownloader != null) /異步下載圖片mDownloader.imageDownload(url, mHolder.mImageView,/yanbin,MainActivity.this, new OnImageDownload()Overridepublic void onDownloadSucc(Bitmap bitmap,String c_url,ImageView mima
18、geView) ImageView imageView = (ImageView) mListView.findViewWithTag(c_url);if (imageView != null)ImageBitmap(bitmap); imageView.setTag();); convertView; 使用 ViewHolder 來優(yōu)化 yanbin *imageView.setreturn/* *listview* author*/private classImageView mImageView;ViewHolder TextView mTextView; 上面的 mDownloader
19、.imageDownload() 就是異步下載圖片比較核 心的方法,該方法在 ImageDownloader.java 類下。其中的 五個參數(shù)分別為:要設(shè)置在當前 ImageView 上的圖片的 url 地址,當前 ImageView ,文件緩存地址,當前的 activity 以 及圖片回調(diào)接口。在 ImageDownloader 類中, 我們首先根據(jù) url 從軟引用中獲 取圖片,如果不存在,從 sdcard 中讀取圖片,如果還不存 在, 則啟動一個 AsyncTask 異步下載圖片。 注意注意: 這里 我們做了一個這樣的操作:用一個 map 將當前的 url 及其對 應(yīng)的 MyAsyncT
20、ask 存放起來了。由于 getView 會執(zhí)行至少 一次,這一步的操作是為了相同的 url 創(chuàng)建相同的 AsyncTask 在 onPostExecute() 方法中,將該 url 對應(yīng)的信息從 map 中 刪除,一定要記得執(zhí)行這一步??吹胶芏嗟漠惒綀D片下載的 例子中, 重復(fù)創(chuàng)建 AsyncTask 都是普遍存在的, 這里我們使 用上面的思路解決掉了這一問題。更詳細的代碼自己看 ImageDownloader.java 類吧,首先給出 OnImageDownload.java 接口的代碼:java view plaincopypublic interface OnImageDownload
21、void onDownloadSucc(Bitmap bitmap,Stringc_url,ImageView imageView); ImageDownloader.java的代碼 (有兩百多行,拷貝到 eclipse 中看會舒服一點 ):java view plaincopypublic class ImageDownloaderprivate static final String TAG =ImageDownloader;private HashMap<String,MyAsyncTask> map = new HashMap<String, MyAsyncTask&
22、gt;();private Map<String,SoftReference<Bitmap>> imageCaches = new HashMap<String, SoftReference<Bitmap>>();/* * * param url 該 mImageView 對應(yīng)的 url * param mImageView* param path 文件存儲路徑 * param mActivity* param downloadOnImageDownload 回調(diào)接口,在 onPostExecute() 中被調(diào)用 */ public void i
23、mageDownload(String url,ImageView mImageView,String path,Activity mActivity,OnImageDownload download) SoftReference<Bitmap> currBitmap = imageCaches.get(url);BitmapsoftRefBitmap = null;if(currBitmap !=null)softRefBitmap =currBitmap.get();StringimageName = ;if(url !=null)imageName =Util.getInst
24、ance().getImageName(url);Bitmap bitmap =getBitmapFromFile(mActivity,imageName,path);/先從軟引用中拿數(shù)據(jù)if(currBitmap != null&& mImageView != null &&softRefBitmap != null &&mImagurl.equals(mImageView.getTag()eView.setImageBitmap(softRefBitmap);/軟引用中沒有,從文件中拿數(shù)據(jù)elseif(bitmap != null &
25、& mImageView != null&&url.equals(mImageView.getTag()mImageView.setImageBitmap(bitmap);/文件中也沒有,此時根據(jù) mImageView 的 tag ,即url 去判斷該 url 對應(yīng)的 task 是否已經(jīng)在執(zhí)行,如果在執(zhí)行,本次操作不創(chuàng)建新的線程,否則創(chuàng)建新的線程。elseif(url != null &&needCreateNewTask(mImageView)MyAsyncTask task = new MyAsyncTask(url, mImageView,path
26、,mActivity,download);if(mImageView != null)Log.i(TAG, 執(zhí)行 MyAsyncTask -> + Util.flag);Util.flag +;task.execute();/將對應(yīng)的 url 對應(yīng)的任務(wù)存起來map.put(url, task);/* 判斷是否需要重新創(chuàng)建線程下載圖片, 如果需要,返回值為 true 。* param url* parammImageView* return*/privateboolean needCreateNewTask(ImageViewmImageView)boolean b = true;if(
27、mImageView != null)String curr_task_urlb;/* 檢查該 url (最終反映的= (String)mImageView.getTag();if(isTasksContains(curr_task_url)false;return是當前的 ImageView 的 tag ,tag 會根據(jù) position 的不同而不同)對應(yīng)的 task 是否存在* param urlreturn*/private booleanboolean b = false;isTasksContains(String url)if(map != null && map
28、.get(url) !=null)b = true;returnb;/* 刪除 map 中該 url 的信息,這一步很重要,不然 MyAsyncTask 的引用會“一直”存在于map 中* param url*/private voidif(url != nullremoveTaskFormMap(String url)&& map != null && map.get(url) !=null)map.remove(url);System.out.println(當前 map 的大小=+map.size();/* 從文件中拿圖片* param mActivity
29、param imageName圖片名字* param path 圖片路徑* return*/private BitmapgetBitmapFromFile(Activity mActivity,StringimageName,String path)Bitmap bitmap = null;if(imageName != null)File file = null;String real_path = ;tryif(Util.getInstance().hasSDCard()real_path =Util.getInstance().getExtPath() + (path != null &
30、amp;&path.startsWith(/) ? path : / +path);elsereal_path = Util.getInstance().getPackagePath(mActivity) + (path != null && path.startsWith(/) ? path : / +path);file = newFile(real_path, imageName);if(file.exists()bitmap =BitmapFactory.decodeStream(newFileInputStream(file); catch (Exceptio
31、n e)e.printStackTrace();bitmap = null;return bitmap; /* 將下載好的圖片存放到文件中* param path圖片路徑param mActivity* param imageName 圖片名字* param bitmap圖片 * return*/private boolean setBitmapToFile(String path,Activity mActivity,String imageName,Bitmapbitmap)File file = null;Stringreal_path = ; try if(Util.getInstan
32、ce().hasSDCard() real_path = Util.getInstance().getExtPath() +(path != null && path.startsWith(/) ? path : / + path); else real_path = Util.getInstance().getPackagePath(mActivity) + (path != null && path.startsWith(/) ? path : / +path); file = newFile(real_path, imageName);if(!file.e
33、xists()File file2 = newFile(real_path + /);file2.mkdirs(); file.createNewFile(); FileOutputStream fos = null;if(Util.getInstance().hasSDCard() fos = newFileOutputStream(file);elsefos = mActivity.openFileOutput(imageName, Context.MODE_PRIVATE); if (imageName != null &&(imageName.contains(.png
34、) | imageName.contains(.PNG) bitma press(Bitmap.CompressFormat.PNG, 90,fos);press(Bitmap.CompressFormat.JPEG, 90, fos);fos.flush();if(fos !=null)fos.close();return true; catch (Exception e)e.printStackTrace();returnfalse; /* 輔助方法,一般不調(diào)用* param path* parammActivity* param imageName*
35、/private void removeBitmapFromFile(String path,ActivityFile file = null;mActivity,String imageName)String real_path = ;trydownload)this.mImageView =if(Util.getInstance().hasSDCard()real_path = Util.getInstance().getExtPath() +(path != null && path.startsWith(/) ? path : / +path);elsereal_pat
36、h= Util.getInstance().getPackagePath(mActivity) + (path != null && path.startsWith(/) ? path : / +path);file = newif(file != null)File(real_path, imageName);file.delete(); catch (Exception e)e.printStackTrace();/* 異步下載圖片的方法* author yanbinprivateImageView mImageView;private String url;*/priva
37、te class MyAsyncTask extendsAsyncTask<String, Void, Bitmap>privateprivate OnImageDownload download;String path;private Activity mActivity;public MyAsyncTask(String url,ImageView mImageView,String path,Activity mActivity,OnImageDownloadmImageView;this.url = url;this.path = path;this.mActivity =
38、 mActivity;this.download = download;BitmapURLInputStreamOverride protected Bitmap doInBackground(String. params) data = null; if(url != null) try c_url = new URL(url);bitmap_data = c_url.openStream();data = BitmapFactory.decodeStream(bitmap_data);String imageName = Util.getInstance().getImageName(ur
39、l);if(!setBitmapToFile(path,mActivity,imageName,data)removeBitmapFromFile(path,mActivity,imageName); imageCaches.put(url, newSoftReference<Bitmap>(data.createScaledBitmap(data, 100, 100, true); catch (Exceptione)e.printStackTrace(); returndata;Overrideprotected void onPreExecute() super.onPreExecute(); Override protected void onPostExecute(Bitmap result) / 回調(diào)設(shè)置圖片if(download !=null) download.onDownl
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 山西省呂梁市離石區(qū)2025屆高考英語二模試卷含解析
- 《solidworks 機械設(shè)計實例教程》 課件 任務(wù)3.2 調(diào)節(jié)盤的設(shè)計
- 2025屆廣東省梅州市皇華中學(xué)高三下學(xué)期聯(lián)合考試英語試題含解析
- 《訪問規(guī)則》課件
- 2025屆福建省福州市八中高三第三次模擬考試語文試卷含解析
- 2025屆廣東省遂溪縣第一中學(xué)高三第一次調(diào)研測試語文試卷含解析
- 2025屆浙江省高中學(xué)高三(最后沖刺)語文試卷含解析
- 重慶市南川中學(xué)2025屆高考仿真卷語文試卷含解析
- 江蘇省蘇州市新草橋中學(xué)2025屆高三第一次調(diào)研測試英語試卷含解析
- 2025屆河南省鶴壁一中高考臨考沖刺英語試卷含解析
- 四川政采評審專家入庫考試基礎(chǔ)題復(fù)習(xí)測試附答案
- 新:中國兒童中樞性肌肉痙攣體外沖擊波治療臨床實踐指南
- 山東省濟南市2023-2024學(xué)年高二上學(xué)期期末考試生物試題 附答案
- 印刷投標服務(wù)方案
- 2024陜西榆林市黃河?xùn)|線引水工程限公司招聘20人易考易錯模擬試題(共500題)試卷后附參考答案
- 2024年電動自行車項目申請報告
- GB/T 44819-2024煤層自然發(fā)火標志氣體及臨界值確定方法
- 寵物犬鑒賞與疾病防治(石河子大學(xué))知到智慧樹章節(jié)答案
- 重慶財經(jīng)學(xué)院《物流系統(tǒng)建模與仿真》2022-2023學(xué)年第一學(xué)期期末試卷
- 冬季安全施工安全培訓(xùn)
- 雇傭護工的協(xié)議書
評論
0/150
提交評論