ListView常用最佳化技巧(Android群英傳)

來源:互聯網
上載者:User

標籤:

內容是博主照著書敲出來的,博主碼字挺辛苦的,轉載請註明出處,後序內容陸續會碼出。

前言:ListView——列表,它作為一個非常重要的顯示方式,不管是在Web中還是移動平台中,都是一個非常好的、不開或缺的展示資訊的工具。在Android中,ListView控制項接管了這一重擔,在大量的場合下,我們都需要使用這個控制項。雖然在Android 5.X時代,RecyclerView在很多地方都在逐漸取代ListView,但ListView的使用範圍依然非常的廣泛,它這萬年老大哥的地位也不是輕易就能撼動的。下面就介紹一下ListView常用最佳化技巧。

使用ViewHolder模式提高效率

  ViewHolder模式是提高ListView效率的一個很重要的方法。ViewHolder模式充分利用了ListView的視圖緩衝機制,避免了每次在調用getView()的時候都去通過findViewById()執行個體化控制項。據測試,使用ViewHolder將提高50%以上的效率。使用ViewHolder模式來最佳化ListView非常簡單,只需要在自訂Adapter中定義一個內部類ViewHolder,並將布局中的控制項作為成員變數,代碼如下所示。

public final class ViewHolder {    public ImageView img;    public TextView title;}

  接下來,只要在getView()方法中通過視圖緩衝機制來重用以緩衝即可,完整的使用ViewHolder建立ListView Adapter的執行個體代碼如下所示。

import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;import java.util.List;/********************************************** author: Blankj on 2016/7/23 15:39 * blog:   http://blankj.com* e-mail: [email protected]*********************************************/public class ViewHolderAdapter extends BaseAdapter {    private List<String> mData;    private LayoutInflater mInflater;    public ViewHolderAdapter(Context context, List<String> data) {        this.mData = data;        mInflater = LayoutInflater.from(context);    }    @Override    public int getCount() {        return mData.size();    }    @Override    public Object getItem(int position) {        return mData.get(position);    }    @Override    public long getItemId(int position) {        return position;    }    @Override    public View getView(int position, View convertView, ViewGroup parent) {        ViewHolder viewHolder = null;        // 判斷是否緩衝        if (convertView == null) {            viewHolder = new ViewHolder();            // 通過LayoutInflater執行個體化布局            convertView = mInflater.inflate(R.layout.viewholder_item, null);            viewHolder.img = (ImageView) convertView.findViewById(R.id.imageView);            viewHolder.title = (TextView) convertView.findViewById(R.id.textView);            convertView.setTag(viewHolder);        }else {            // 通過tag找到緩衝的布局            viewHolder = (ViewHolder) convertView.getTag();        }        // 設定布局中控制項要顯示的視圖        viewHolder.img.setBackgroundResource(R.mipmap.ic_launcher);        viewHolder.title.setText(mData.get(position));        return convertView;    }    public final class ViewHolder {        public ImageView img;        public TextView title;    }}

  效果很簡單,這就是一個簡單的ListView,如所示。



設定項目間分隔線

  ListView的各個項目之間,可以通過設定分隔線來進行區分,系統提供了divider和dividerHeight這樣兩個屬性來協助我們實現這一功能。通過這兩個屬性,也可以控制ListView之間的分隔線和它的高度。當然,分隔線不僅僅可以設定為一個顏色,同樣也可以設定為一個圖片資源,分隔線的使用代碼如下所示。

android:divider="@color/colorAccent"android:dividerHeight="10dp"

  以上代碼所實行的效果如所示。




  特殊情況下,當設定分隔線為如下代碼時,就可以把分隔線設定為透明了。

android:divider="@null"
隱藏ListView的捲軸

  預設的ListView在滾動時,在右邊會顯示捲軸,指示當前滑動的位置,我們可以設定scrollbars屬性,控制ListView的捲軸狀態。特別地,當設定scrollbars屬性為none的時候,ListView滾動或者不滾動,就都不會出現捲軸了,代碼如下所示。

android:divider="@null"
取消ListView的Item點擊效果

  當點擊ListView中的一項時,系統預設會出現一個點擊效果,在Android5.X上是一個波紋效果,而在Android5.X之下的版本則是一個改變背景顏色的效果,但可以通過修改listSelector屬性來取消掉點擊後的回饋效果,代碼如下所示。

android:listSelector="#00000000"

  當然,也可以直接使用Android內建的透明色來實現這個效果,代碼如下所示。

android:listSelector="@android:color/transparent"
設定ListView需要顯示在第幾項

  ListView以Item為單位進行顯示,預設顯示在第一個Item,當需要指定具體顯示的Item時,可以通過如下代碼來實現。

mListView.setSelection(N);

  其中N就是需要顯示的第N個Item。
  當然,這個方法類似scrollTo,是瞬間完成的移動。除此以外,還可以使用如下代碼來實現平滑移動。

mListView.smoothScrollBy(distance,duration);mListView.smoothScrollByOffset(offset);mListView.smoothScrollToPosition(index);
動態修改ListView

  ListView中的資料在某些情況下是需要變化的,當然可以通過重新設定ListView的Adapter來更新ListView的顯示,但這也就需要重新擷取一下資料,相當於重新重新整理建立的ListView,這樣顯然不是非常友好,而且效率也不會太高。因此,可以使用一個更簡單的方法來實現ListView的動態修改,代碼如下所示。

mData.add("new");mAdapter.notifyDataSetChanged();

  當修改了傳遞給Adapter的映射List之後,只需要通過調用Adapter的notifyDataSetChanged()方法,通知ListView更改資料來源即可完成對ListView的動態修改。不過,使用這個方法有一點需要注意的是,在使用mAdapter.notifyDataSetChanged()方法時,必須保證傳進Adapter的資料List是同一個List而不能是其他對象,否則將無法實現該效果。下面這個執行個體就示範了如何動態地修改ListView。通過點擊按鈕,不斷地給原有的List增加一個新的Item,並調用notifyDataSetChanged()方法來實現ListView的動態更新,完整代碼如下所示。

import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.widget.ListView;import java.util.ArrayList;public class MainActivity extends AppCompatActivity {    ListView mListView;    ViewHolderAdapter mAdapter;    ArrayList<String> mData;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mListView = (ListView) findViewById(R.id.listView);        mData = new ArrayList<>();        for (int i = 0; i < 20; ++i) {            mData.add(i + "");        }        mAdapter = new ViewHolderAdapter(this, mData);        mListView.setAdapter(mAdapter);        mAdapter.notifyDataSetChanged();        for (int i = 0, len = mListView.getCheckedItemCount(); i < len; i++) {            View view = mListView.getChildAt(i);        }    }    public void btnAdd(View view) {        mData.add("new");        mAdapter.notifyDataSetChanged();        mListView.setSelection(mData.size() - 1);    }}

實現的效果如所示。



遍曆ListView中的所有Item

  ListView作為一個ViewGroup,為我們提供了操縱子View的各種方法,最常用的就是通過getChildAt()來擷取第i個子View,代碼如下所示。

for (int i = 0, len = mListView.getCheckedItemCount(); i < len; i++) {    View view = mListView.getChildAt(i);}
處理空ListView

  ListView用於展示列表資料,但當列表中無資料時,ListView不會顯示任何資料或提示,按照完善使用者體驗的需求,這裡應該給以無資料的提示。幸好,ListView提供了一個方法——setEmptyView(),通過這個方法,我們可以給ListView設定一個在空資料下顯示的預設提示。包含ListView的布局設定如下。

<?xml version="1.0" encoding="utf-8"?><FrameLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.blankj.listviewskill.MainActivity">    <ListView        android:id="@+id/listView"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:divider="@null"        android:listSelector="@android:color/transparent"        android:paddingBottom="40dp"/>    <ImageView        android:id="@+id/empty_view"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:src="@mipmap/ic_launcher"/></FrameLayout>

  在代碼中,我們通過以下方式給ListView設定空資料時要顯示的布局,代碼如下所示。

mListView.setEmptyView(findViewById(R.id.empty_view));

  通過以上代碼,就給ListView在空資料時顯示了一張預設的圖片,用來提示使用者;而在有資料時,則不會顯示。

ListView的滑動監聽

  ListView的滑動監聽,是ListView中最重要的技巧,很多重寫的ListView基本上都是在滑動事件的處理上下功夫,通過判斷滑動事件進行不同的邏輯處理。而為了更佳精確地監聽滑動事件,開發人員通常還需要使用GestureDetector手勢識別、VelocityTracker滑動速度檢測等輔助類來完成更好的監聽。這裡介紹兩種監聽ListView滑動事件的方法,一個是通過OnTouchListener來實現監聽,另一個是使用OnScrollListener來實現監聽。

OnTouchListener

  OnTouchListener是View中的監聽事件,通過監聽ACTION_DOWN、ACTION_MOVE、ACTION_UP這三個事件發生時的座標,就可以根據座標判斷使用者滑動的方向,並在不同的事件中進行相應的邏輯處理,這種方式的使用代碼如下所示。

mListView.setOnTouchListener(new View.OnTouchListener() {    @Override    public boolean onTouch(View v, MotionEvent event) {        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                // 觸摸時操作                break;            case MotionEvent.ACTION_MOVE:                // 移動時操作                break;            case MotionEvent.ACTION_UP:                // 離開時操作                break;        }        return false;    }});
OnScrollListener

  OnScrollListener是AbsListView中的監聽事件,它封裝了很多ListView相關的資訊,使用起來也更加靈活。首先來看一下OnScrollListener的一般使用方法,代碼如下所示。

mListView.setOnScrollListener(new AbsListView.OnScrollListener() {    @Override    public void onScrollStateChanged(AbsListView view, int scrollState) {        switch (scrollState) {            case SCROLL_STATE_IDLE:                // 滑動停止時                Log.d("Test", "SCROLL_STATE_IDLE");                break;            case SCROLL_STATE_TOUCH_SCROLL:                // 正在滾動                Log.d("Test", "SCROLL_STATE_TOUCH_SCROLL");                break;            case SCROLL_STATE_FLING:                // 手指拋動時,即手指用力滑動                // 在離開後ListView由於慣性繼續滑動                Log.d("Test", "SCROLL_STATE_FLING");                break;        }    }    @Override    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {        // 滾動時一直調用        Log.d("Test", "onScroll");    }

  OnScrollListener中有兩個回調方法——onScrollStateChanged()和onScroll()。
  先來看第一個方法onScrollStateChanged(),這個方法根據它的參數scrollState來決定其回調的次數,scrollState有以下三種模式:
  **·**SCROLL_STATE_IDLE:滾動停止時。
  **·**SCROLL_STATE_TOUCH_SCROLL:正在滾動時。
  **·**SCROLL_STATE_FLING:手指拋動時,即手指用力滑動,在離開後ListView由於慣性繼續滑動
  當使用者沒有做手指拋動的狀態時,這個方法只會回調2次,否則會回調三次,差別就是手指拋動的這個狀態。通常情況下,我們會在這個方法中通過不同的狀態來設定一些標誌Flag,來區分不同的滑動狀態,供其他方法處理。
  下面再來看看onScroll()這個回調方法,它在ListView滾動時會一直回調,而方法中的後三個int類型的參數,則非常精確地顯示了當前ListView滾動的狀態,這三個參數如下所示。
  **·**firstVisibleItem:當前能看見的第一個Item的ID(從0開始)
  **·**visibleItemCount:當前能看見的Item的總數。
  **·**totalItemCount:整個ListView的Item總數。
  這裡需要注意的是,當前能看見的Item數,包括沒有顯示完整的Item,即顯示一小半的Item也包括在內了。通過這幾個參數,可以很方便地進行一些判斷,比如判斷是否滾動到最後一行,就可以使用如下代碼進行判斷,當前可視的另一個Item的ID加上當前可視Item的和等於Item總數的時候,即滾動到了最後一行。

if (firstVisibleItem + visibleItemCount == totalItemCount && totalItemCount > 0) {    Log.d("Test", "滾動到最後一行");}

  再比如,可以通過如下代碼來判斷滾動的方向,代碼如下所示。

if(firstVisibleItem > lastVisibleItem){    // 上滑}else if(firstVisibleItem < lastVisibleItem){    // 下滑}lastVisibleItem = firstVisibleItem;

  通過一個成員變數lastVisibleItem來記錄上次第一個可視的Item的ID並於當前的可視Item的ID進行比較,即可知道當前滾動的方向。
  要理解整個OnScrollListener,最好的方法還是在代碼中添加Log,並列印出狀態資訊來進行分析學習。在以上代碼中,已經添加了相應的Log,對照Log進行分析,會很快掌握OnScrollListener的用法。
  當然,ListView也給我們提供了一些封裝的方法來獲得當前可視的Item的位置等資訊。

// 擷取可視地區內最後一個Item的idmListView.getLastVisiblePosition();// 擷取可視地區內第一個Item的idmListView.getFirstVisiblePosition();

項目地址→ListViewSkill

原文地址ListView常用最佳化技巧(Android群英傳)
我的自媒體部落格blankj小站(OJ、LeetCode、Android開發),歡迎來逛逛。

ListView常用最佳化技巧(Android群英傳)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.