最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Listview加載的性能優(yōu)化是如何實(shí)現(xiàn)的

 更新時(shí)間:2016年01月26日 09:13:47   作者:GoAgent  
在android開發(fā)中Listview是一個(gè)很重要的組件,它以列表的形式根據(jù)數(shù)據(jù)的長(zhǎng)自適應(yīng)展示具體內(nèi)容,用戶可以自由的定義listview每一列的布局,接下來(lái)通過本文給大家介紹Listview加載的性能優(yōu)化是如何實(shí)現(xiàn)的,對(duì)listview性能優(yōu)化相關(guān)知識(shí)感興趣的朋友一起學(xué)習(xí)吧

在android開發(fā)中Listview是一個(gè)很重要的組件,它以列表的形式根據(jù)數(shù)據(jù)的長(zhǎng)自適應(yīng)展示具體內(nèi)容,用戶可以自由的定義listview每一列的布局,但當(dāng)listview有大量的數(shù)據(jù)需要加載的時(shí)候,會(huì)占據(jù)大量?jī)?nèi)存,影響性能,這時(shí)候就需要按需填充并重新使用view來(lái)減少對(duì)象的創(chuàng)建。

listview加載的核心是其adapter,本文針對(duì)listview加載的性能優(yōu)化就是對(duì)adpter的優(yōu)化,總共分四個(gè)層次:

0、最原始的加載

1、利用convertView

2、利用ViewHolder

3、實(shí)現(xiàn)局部刷新

〇、最原始的加載

這里是不經(jīng)任何優(yōu)化的adapter,為了看起來(lái)方便,把listview的數(shù)據(jù)直接在構(gòu)造函數(shù)里傳給adapter了,代碼如下:

private class AdapterOptmL extends BaseAdapter {
private LayoutInflater mLayoutInflater;
private ArrayList<Integer> mListData;
public AdapterOptmL(Context context, ArrayList<Integer> data) {
mLayoutInflater = LayoutInflater.from(context);
mListData = data;
}
@Override
public int getCount() {
return mListData == null ? : mListData.size();
}
@Override
public Object getItem(int position) {
return mListData == null ? : mListData.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View viewRoot = mLayoutInflater.inflate(R.layout.listitem, parent, false);
if (viewRoot != null) {
TextView txt = (TextView)viewRoot.findViewById(R.id.listitem_txt);
txt.setText(getItem(position) + "");
}
return viewRoot;
}
}

一、利用convertView

上述代碼的第27行在Eclipse中已經(jīng)提示警告:

Unconditional layout inflation from view adapter: Should use View Holder pattern (use recycled view passed into this method as the second parameter) for smoother scrolling

這個(gè)意思就是說,被移出可視區(qū)域的view是可以回收復(fù)用的,它作為getview的第二個(gè)參數(shù)已經(jīng)傳進(jìn)來(lái)了,所以沒必要每次都從xml里inflate。

經(jīng)過優(yōu)化后的代碼如下:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.listitem, parent, false);
}
if (convertView != null) {
TextView txt = (TextView)convertView.findViewById(R.id.listitem_txt);
txt.setVisibility(View.VISIBLE);
txt.setText(getItem(position) + "");
}
return convertView;
}

上述代碼加了判斷,如果傳入的convertView不為null,則直接復(fù)用,否則才會(huì)從xml里inflate。

按照上述代碼,如果手機(jī)一屏最多同時(shí)顯示5個(gè)listitem,則最多需要從xml里inflate 5 次,比AdapterOptmL0中每個(gè)listitem都需要inflate顯然效率高多了。

上述的用法雖然提高了效率,但帶來(lái)了一個(gè)陷阱,如果復(fù)用convertView,則需要重置該view所有可能被修改過的屬性。

舉個(gè)例子:

如果第一個(gè)view中的textview在getview中被設(shè)置成INVISIBLE了,而現(xiàn)在第一個(gè)view在滾動(dòng)過程中出可視區(qū)域,并假設(shè)它作為參數(shù)傳入第十個(gè)view的getview而被復(fù)用

那么,在第十個(gè)view的getview里面不僅要setText,還要重新setVisibility,因?yàn)檫@個(gè)被復(fù)用的view當(dāng)前處于INVISIBLE狀態(tài)!

二、利用ViewHolder

從AdapterOptmL0第27行的警告中,我們還可以看到編譯器推薦了一種模型叫ViewHolder,這是個(gè)什么東西呢,先看代碼:

private class AdapterOptmL extends BaseAdapter {

private LayoutInflater mLayoutInflater;
private ArrayList<Integer> mListData;
public AdapterOptmL(Context context, ArrayList<Integer> data) {
mLayoutInflater = LayoutInflater.from(context);
mListData = data;
}
private class ViewHolder {
public ViewHolder(View viewRoot) {
txt = (TextView)viewRoot.findViewById(R.id.listitem_txt);
}
public TextView txt;
}
@Override
public int getCount() {
return mListData == null ? : mListData.size();
}
@Override
public Object getItem(int position) {
return mListData == null ? : mListData.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.listitem, parent, false);
ViewHolder holder = new ViewHolder(convertView);
convertView.setTag(holder);
}
if (convertView != null && convertView.getTag() instanceof ViewHolder) {
ViewHolder holder = (ViewHolder)convertView.getTag();
holder.txt.setVisibility(View.VISIBLE);
holder.txt.setText(getItem(position) + "");
}
return convertView;
}
}

從代碼中可以看到,這一步做的優(yōu)化是用一個(gè)類ViewHolder來(lái)保存listitem里面所有找到的子控件,這樣就不用每次都通過耗時(shí)的findViewById操作了。

這一步的優(yōu)化,在listitem布局越復(fù)雜的時(shí)候效果越為明顯。

三、實(shí)現(xiàn)局部刷新

OK,到目前為止,listview普遍需要的優(yōu)化已經(jīng)做的差不多了,那就該考慮實(shí)際使用場(chǎng)景中的優(yōu)化需求了。

實(shí)際使用listview過程中,通常會(huì)在后臺(tái)更新listview的數(shù)據(jù),然后調(diào)用Adatper的notifyDataSetChanged方法來(lái)更新listview的UI。

那么問題來(lái)了,一般情況下,一次只會(huì)更新listview的一條/幾條數(shù)據(jù),而調(diào)用notifyDataSetChanged方法則會(huì)把所有可視范圍內(nèi)的listitem都刷新一遍,這是不科學(xué)的!

所以,進(jìn)一步優(yōu)化的空間在于,局部刷新listview,話不多說見代碼:

private class AdapterOptmL3 extends BaseAdapter {
private LayoutInflater mLayoutInflater;
private ListView mListView;
private ArrayList<Integer> mListData;
public AdapterOptmL3(Context context, ListView listview, ArrayList<Integer> data) {
mLayoutInflater = LayoutInflater.from(context);
mListView = listview;
mListData = data;
}
private class ViewHolder {
public ViewHolder(View viewRoot) {
txt = (TextView)viewRoot.findViewById(R.id.listitem_txt);
}
public TextView txt;
}
@Override
public int getCount() {
return mListData == null ? 0 : mListData.size();
}
@Override
public Object getItem(int position) {
return mListData == null ? 0 : mListData.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.listitem, parent, false);
ViewHolder holder = new ViewHolder(convertView);
convertView.setTag(holder);
}
if (convertView != null && convertView.getTag() instanceof ViewHolder) {
updateView((ViewHolder)convertView.getTag(), (Integer)getItem(position));
}
return convertView;
}
public void updateView(ViewHolder holder, Integer data) {
if (holder != null && data != null) {
holder.txt.setVisibility(View.VISIBLE);
holder.txt.setText(data + "");
}
}
public void notifyDataSetChanged(int position) {
final int firstVisiablePosition = mListView.getFirstVisiblePosition();
final int lastVisiablePosition = mListView.getLastVisiblePosition();
final int relativePosition = position - firstVisiablePosition;
if (position >= firstVisiablePosition && position <= lastVisiablePosition) {
updateView((ViewHolder)mListView.getChildAt(relativePosition).getTag(), (Integer)getItem(position));
} else {
//不在可視范圍內(nèi)的listitem不需要手動(dòng)刷新,等其可見時(shí)會(huì)通過getView自動(dòng)刷新
}
}
} 

修改后的Adapter新增了一個(gè)方法 public void notifyDataSetChanged(int position) 可以根據(jù)position只更新指定的listitem。

局部刷新番外篇

在局部刷新數(shù)據(jù)的接口中,實(shí)際上還可以再干點(diǎn)事情:listview正在滾動(dòng)的時(shí)候不去刷新。

具體的思路是,如果當(dāng)前正在滾動(dòng),則記住一個(gè)pending任務(wù),等listview停止?jié)L動(dòng)的時(shí)候再去刷,這樣不會(huì)造成滾動(dòng)的時(shí)候刷新錯(cuò)亂。代碼如下:

private class AdapterOptmLPlus extends BaseAdapter implements OnScrollListener{
private LayoutInflater mLayoutInflater;
private ListView mListView;
private ArrayList<Integer> mListData;
private int mScrollState = SCROLL_STATE_IDLE;
private List<Runnable> mPendingNotify = new ArrayList<Runnable>();
public AdapterOptmLPlus(Context context, ListView listview, ArrayList<Integer> data) {
mLayoutInflater = LayoutInflater.from(context);
mListView = listview;
mListData = data;
mListView.setOnScrollListener(this);
}
private class ViewHolder {
public ViewHolder(View viewRoot) {
txt = (TextView)viewRoot.findViewById(R.id.listitem_txt);
}
public TextView txt;
}
@Override
public int getCount() {
return mListData == null ? : mListData.size();
}
@Override
public Object getItem(int position) {
return mListData == null ? : mListData.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.listitem, parent, false);
ViewHolder holder = new ViewHolder(convertView);
convertView.setTag(holder);
}
if (convertView != null && convertView.getTag() instanceof ViewHolder) {
updateView((ViewHolder)convertView.getTag(), (Integer)getItem(position));
}
return convertView;
}
public void updateView(ViewHolder holder, Integer data) {
if (holder != null && data != null) {
holder.txt.setVisibility(View.VISIBLE);
holder.txt.setText(data + "");
}
}
public void notifyDataSetChanged(final int position) {
final Runnable runnable = new Runnable() {
@Override
public void run() {
final int firstVisiablePosition = mListView.getFirstVisiblePosition();
final int lastVisiablePosition = mListView.getLastVisiblePosition();
final int relativePosition = position - firstVisiablePosition;
if (position >= firstVisiablePosition && position <= lastVisiablePosition) {
if (mScrollState == SCROLL_STATE_IDLE) {
//當(dāng)前不在滾動(dòng),立刻刷新
Log.d("Snser", "notifyDataSetChanged position=" + position + " update now");
updateView((ViewHolder)mListView.getChildAt(relativePosition).getTag(), (Integer)getItem(position));
} else {
synchronized (mPendingNotify) {
//當(dāng)前正在滾動(dòng),等滾動(dòng)停止再刷新
Log.d("Snser", "notifyDataSetChanged position=" + position + " update pending");
mPendingNotify.add(this);
}
}
} else {
//不在可視范圍內(nèi)的listitem不需要手動(dòng)刷新,等其可見時(shí)會(huì)通過getView自動(dòng)刷新
Log.d("Snser", "notifyDataSetChanged position=" + position + " update skip");
}
}
};
runnable.run();
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
mScrollState = scrollState;
if (mScrollState == SCROLL_STATE_IDLE) {
//滾動(dòng)已停止,把需要刷新的listitem都刷新一下
synchronized (mPendingNotify) {
final Iterator<Runnable> iter = mPendingNotify.iterator();
while (iter.hasNext()) {
iter.next().run();
iter.remove();
}
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
}

以上所述是針對(duì)Listview加載的性能優(yōu)化是如何實(shí)現(xiàn)的全部敘述,希望對(duì)大家有所幫助。

相關(guān)文章

  • 詳談自定義View之GridView單選 金額選擇Layout-ChooseMoneyLayout

    詳談自定義View之GridView單選 金額選擇Layout-ChooseMoneyLayout

    下面小編就為大家?guī)?lái)一篇詳談自定義View之GridView單選 金額選擇Layout-ChooseMoneyLayout。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧
    2017-05-05
  • Android 集成 google 登錄并獲取性別等隱私信息的實(shí)現(xiàn)代碼

    Android 集成 google 登錄并獲取性別等隱私信息的實(shí)現(xiàn)代碼

    這篇文章主要介紹了Android 集成 google 登錄并獲取 性別等隱私信息,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-06-06
  • Android文字基線Baseline算法的使用講解

    Android文字基線Baseline算法的使用講解

    今天小編就為大家分享一篇關(guān)于Android文字基線Baseline算法的使用講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-04-04
  • Android JetpackCompose使用教程講解

    Android JetpackCompose使用教程講解

    在今年的Google/IO大會(huì)上,亮相了一個(gè)全新的 Android 原生 UI 開發(fā)框架-Jetpack Compose, 與蘋果的SwiftIUI一樣,Jetpack Compose是一個(gè)聲明式的UI框架
    2022-10-10
  • AndroidStudio:手勢(shì)識(shí)別

    AndroidStudio:手勢(shì)識(shí)別

    這篇文章主要介紹了AndroidStudio手勢(shì)識(shí)別,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • 淺談關(guān)于Android WebView上傳文件的解決方案

    淺談關(guān)于Android WebView上傳文件的解決方案

    這篇文章主要介紹了淺談關(guān)于Android WebView上傳文件的解決方案 ,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-09-09
  • listview改變字體大小實(shí)例講解

    listview改變字體大小實(shí)例講解

    點(diǎn)擊字體,字體變大在本教程中將使用listview實(shí)現(xiàn),不會(huì)不懂的朋友可以參考下哈,希望對(duì)你有所幫助
    2013-05-05
  • Android之線程池ThreadPoolExecutor的簡(jiǎn)介

    Android之線程池ThreadPoolExecutor的簡(jiǎn)介

    今天小編就為大家分享一篇關(guān)于Android之線程池ThreadPoolExecutor的簡(jiǎn)介,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-03-03
  • 詳解Android冷啟動(dòng)實(shí)現(xiàn)APP秒開的方法

    詳解Android冷啟動(dòng)實(shí)現(xiàn)APP秒開的方法

    這篇文章給大家介紹的是Android冷啟動(dòng)實(shí)現(xiàn)APP秒開的方法,對(duì)大家日常開發(fā)APP還是很實(shí)用的,有需要的可以參考借鑒。
    2016-08-08
  • Flutter使用RepositoryProvider解決跨組件傳值問題

    Flutter使用RepositoryProvider解決跨組件傳值問題

    在實(shí)際開發(fā)過程中,經(jīng)常會(huì)遇到父子組件傳值的情況。本文將利用RepositoryProvider解決跨組件傳值的問題,感興趣的小伙伴可以了解一下
    2022-04-04

最新評(píng)論

时尚| 玉龙| 新宁县| 安顺市| 清新县| 清水河县| 密山市| 灌南县| 辽源市| 达孜县| 应用必备| 富蕴县| 中卫市| 安阳市| 安阳市| 定日县| 宁国市| 射洪县| 时尚| 盘锦市| 南皮县| 广宗县| 漳浦县| 西昌市| 濮阳市| 上杭县| 普陀区| 河津市| 会泽县| 邯郸县| 同心县| 青阳县| 福建省| 兰溪市| 出国| 吉木乃县| 滨海县| 德州市| 澄江县| 阿城市| 丹巴县|