我相信做過Android應用程序開發或多或少都遇到了這個問題。或者是在ListView數據損壞幻燈片事件。要么GridView數據損壞幻燈片事件。
讓我們來看看一個網友寫的文章,個人感覺還不錯的文章:
Android ListView滑動過程中圖片顯示反復錯位閃爍問題解決
主要分析Android ListView滾動過程中圖片顯示反復、錯亂、閃爍的原因及解決方法,順帶提及ListView的緩存機制。
1、原因分析
ListView item緩存機制:為了使得性能更優。ListView會緩存行item(某行相應的View)。ListView通過adapter的getView函數獲得每行的item。滑動過程中,
a. 假設某行item已經滑出屏幕,若該item不在緩存內,則put進緩存,否則更新緩存。
b. 獲取滑入屏幕的行item之前會先推斷緩存中是否有可用的item,假設有,做為convertView參數傳遞給adapter的getView。
更詳細可見源代碼ListView.obtainView。
這樣,例如以下的getView寫法就能夠充分利用緩存大大提升ListView的性能。即便上萬個行item。最多inflate的次數為n,n為一屏最多顯示ListView 行item的個數。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
@Override
public
View
getView
(
int
position
,
View
convertView
,
ViewGroup
parent
)
{
ViewHolder
holder
;
if
(
convertView
==
null
)
{
convertView
=
inflater
.
inflate
(
R
.
layout
.
list_item
,
null
)
;
holder
=
new
ViewHolder
(
)
;
……
convertView
.
setTag
(
holder
)
;
}
else
{
holder
=
(
ViewHolder
)
convertView
.
getTag
(
)
;
}
}
/**
* ViewHolder
*
* @author trinea@trinea.cn 2013-08-01
*/
private
static
class
ViewHolder
{
ImageView
appIcon
;
TextView
appName
;
TextView
appInfo
;
}
|
這樣提升了性能。但同一時候也會造成另外一些問題:
a. 行item圖片顯示反復
這個顯示反復是指當前行item顯示了之前某行item的圖片。
比方ListView滑動到第2行會異步載入某個圖片,可是載入非常慢,載入過程中listView已經滑動到了第14行,且滑動過程中該圖片載入結束。第2行已不在屏幕內,依據上面介紹的緩存原理,第2行的view可能被第14行復用,這樣我們看到的就是第14行顯示了本該屬於第2行的圖片。造成顯示反復。
b. 行item圖片顯示錯亂
這個顯示錯亂是指某行item顯示了不屬於該行item的圖片。
比方ListView滑動到第2行會異步載入某個圖片。可是載入非常慢。載入過程中listView已經滑動到了第14行。第2行已不在屏幕內。依據上面介紹的緩存原理,第2行的view可能被第14行復用,第14行顯示了第2行的View,這時之前的圖片載入結束,就會顯示在第14行,造成錯亂。
c. 行item圖片顯示閃爍
上面b的情況,第14行圖片又非常快載入結束。所以我們看到第14行先顯示了第2行的圖片,立刻又顯示了自己的圖片進行覆蓋造成閃爍錯亂。
2、解決方法
通過上面的分析我們知道了出現錯亂的原因是異步載入及對象被復用造成的。假設每次getView能給對象一個標識,在異步載入完畢時比較標識與當前行item的標識是否一致,一致則顯示,否則不做處理就可以。
以下以使用ImageCache為ListView提供圖片獲取緩存為例,ListView中強烈推薦使用ImageCache。
首先在listview adapter的getView中加入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
@Override
public
View
getView
(
int
position
,
View
convertView
,
ViewGroup
parent
)
{
ViewHolder
holder
;
if
(
convertView
==
null
)
{
convertView
=
inflater
.
inflate
(
R
.
layout
.
list_item
,
null
)
;
holder
=
new
ViewHolder
(
)
;
……
convertView
.
setTag
(
holder
)
;
}
else
{
holder
=
(
ViewHolder
)
convertView
.
getTag
(
)
;
}
……
// add tag for image, to compare it when image loaded finish
imageView
.
setTag
(
imageUrl
)
;
// if not in cache, restore default
if
(
!
Cache
.
ICON_CACHE
.
get
(
imageUrl
,
imageView
)
)
{
imageView
.
setImageDrawable
(
null
)
;
}
}
|
當中setTag表示設置標識,方便以下進行標志比對
1
|
if
(
!
Cache
.
ICON_CACHE
.
get
(
imageUrl
,
imageView
)
)
|
Cache.ICON_CACHE為ImageCache的實例。表示假設不在緩存內則設置drawable為null(當然你能夠能夠設置為你自己的默認資源),防止顯示了之前某個行item的圖片,攻克了a. 行item圖片顯示反復問題。
在ImageCache的OnImageCallbackListener的onGetSuccess函數中加入
1
2
3
4
5
6
7
8
9
10
11
|
public
void
onGetSuccess
(
String
imageUrl
,
Drawable
imageDrawable
,
View
view
,
boolean
isInCache
)
{
// can be another view child, like textView and so on
if
(
view
!=
null
&&
imageDrawable
!=
null
)
{
ImageView
imageView
=
(
ImageView
)
view
;
// add tag judge, avoid listView cache and so on
String
imageUrlTag
=
(
String
)
imageView
.
getTag
(
)
;
if
(
ObjectUtils
.
isEquals
(
imageUrlTag
,
imageUrl
)
)
{
imageView
.
setImageDrawable
(
imageDrawable
)
;
}
}
}
;
|
在上面用String imageUrlTag = (String)imageView.getTag();取得之前設置的tag,然后和當前的url進行比較,假設相等則顯示,攻克了b. 行item圖片顯示錯亂。c. 行item圖片顯示錯亂的兩個問題。
當中ObjectUtils可見ObjectUtils@Github.
其它異步載入過程解決原理類似。
插一句:標題上我加了此文章的原文鏈接。
(1)把上文中if(convertView==null){}else{holder=(ViewHolder)convertView.getTag() }給凝視 掉,然后數據混亂的問題就得以攻克了,可是這樣問題盡管攻克了,應用程序性能就減少了。顯然此種方法雖能解決這個問題,可是還是不建議使用。
(2)就是通過加入Tag標記,可是盡管是說加入Tag標記,每個應用程序加入Tag的方法不同。故在此無法給出一個統一的解決方法,僅僅能說一個統一的解決思路。比如:假設混亂的是一件商品的數量,能夠把商品的數量暫時存到一個數組中,然后每次取值都從數組里取值即可了。事實上說白了,就是依據詳細的情況new 一個對應的數組,來做數據的中轉站。例如以下圖情形:
在滑動時,我們會發現數量的值會發生混亂,或者在加減右圖中的數字時,也會出現這種情況。
以下提供一個類似的方案。事實上對應的代碼都是在你應用中的Adapter中改動。
首先聲明一個數組用於存放數量:
private int [] tempNum;
然后在本Adapter的構造方法中初始化此數組:tempNum=new int[this.list.size()];
然后在getView方法中一定要有例如以下代碼:mItemViewHolder.mTvNum.setTag(position);
mItemViewHolder.mTvNum.setText(tempNum[position]+"");
然后在加減時。也要把最新的數量存到數組里。例如以下:
<span style="white-space:pre"> </span>case R.id.menu_reduce_iv: mTextView=(TextView)v.getTag(R.id.menu_num_tv); num=Integer.valueOf(mTextView.getText().toString().trim()); num--; tempNum[(Integer)mTextView.getTag()]=num; if(num<0) { Toast.makeText(context, "親,不能再減了,趕緊加加吧。", Toast.LENGTH_SHORT).show(); } else { mTextView.setText( tempNum[(Integer)mTextView.getTag()]+""); } break; case R.id.menu_add_iv: mTextView=(TextView)v.getTag(R.id.menu_num_tv); num=Integer.valueOf(mTextView.getText().toString().trim()); num++; tempNum[(Integer)mTextView.getTag()]=num; mTextView.setText(tempNum[(Integer)mTextView.getTag()]+""); break;
總之。在做此類處理時,一定要new一個數組,然后在混亂的地方一定要setTag。
大致就說到這里,鄙人才疏學淺,有不正確之處,望大家及時指出。轉載請注明:http://blog.csdn.net/android_jiangjun/article/details/39924541
版權聲明:本文博客原創文章,博客,未經同意,不得轉載。