版權(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 導致的。如果你重用了 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片下載的 例子中, 重復創(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)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024-2030年膀胱切除鏡行業(yè)市場現(xiàn)狀供需分析及投資評估規(guī)劃分析研究報告
- 2024-2030年綠原行業(yè)市場現(xiàn)狀供需分析及投資評估規(guī)劃分析研究報告
- 2024-2030年納米銀產(chǎn)業(yè)發(fā)展分析及規(guī)劃專項研究報告
- 2024-2030年粗妥爾油行業(yè)市場現(xiàn)狀供需分析及投資評估規(guī)劃分析研究報告
- 三人合作綠色建筑協(xié)議
- 樂器制造廠勞動合同模板
- 產(chǎn)學研合作技術(shù)成果轉(zhuǎn)化協(xié)議
- 個人貸款合同樣本
- 倉儲服務(wù)銷售合同模板
- 代持股權(quán)質(zhì)量協(xié)議
- 《園林工程》圖文課件ppt-項目二
- 油氣集輸工藝理論--單點系泊和浮式生產(chǎn)系統(tǒng)
- 軟件無線電接收機課件
- 醫(yī)院大樓二次裝修工程施工組織設(shè)計(99頁含施工工藝)
- 醫(yī)院往來賬款管理制度
- T∕CGMA 031003-2020 一般用離心空氣壓縮機
- 企業(yè)消防安全教育培訓教案PPT課件
- 古詩選擇題帶答案解析
- 三元催化器機械性能及老化試驗規(guī)范[1]
- 新北師大版五年級數(shù)學上冊全冊課件(完整版)
- 牛頭刨床說明書(一)
評論
0/150
提交評論