編輯:關於Android編程
前言:ListView——列表,它作為一個非常重要的顯示方式,不管是在Web中還是移動平台中,都是一個非常好的、不開或缺的展示信息的工具。在Android中,ListView控件接管了這一重擔,在大量的場合下,我們都需要使用這個控件。雖然在Android 5.X時代,RecyclerView在很多地方都在逐漸取代ListView,但ListView的使用范圍依然非常的廣泛,它這萬年老大哥的地位也不是輕易就能撼動的。下面就介紹一下ListView常用優化技巧。
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 ListmData; private LayoutInflater mInflater; public ViewHolderAdapter(Context context, List 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,如下圖所示。
<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoMyBpZD0="設置項目間分隔線">設置項目間分隔線
ListView的各個項目之間,可以通過設置分隔線來進行區分,系統提供了divider和dividerHeight這樣兩個屬性來幫助我們實現這一功能。通過這兩個屬性,也可以控制ListView之間的分隔線和它的高度。當然,分隔線不僅僅可以設置為一個顏色,同樣也可以設置為一個圖片資源,分隔線的使用代碼如下所示。
android:divider="@color/colorAccent" android:dividerHeight="10dp"
以上代碼所實行的效果如下圖所示。
android:divider="@null"
默認的ListView在滾動時,在右邊會顯示滾動條,指示當前滑動的位置,我們可以設置scrollbars屬性,控制ListView的滾動條狀態。特別地,當設置scrollbars屬性為none的時候,ListView滾動或者不滾動,就都不會出現滾動條了,代碼如下所示。
android:divider="@null"
當點擊ListView中的一項時,系統默認會出現一個點擊效果,在Android5.X上是一個波紋效果,而在Android5.X之下的版本則是一個改變背景顏色的效果,但可以通過修改listSelector屬性來取消掉點擊後的回饋效果,代碼如下所示。
android:listSelector="#00000000"
當然,也可以直接使用Android自帶的透明色來實現這個效果,代碼如下所示。
android:listSelector="@android:color/transparent"
ListView以Item為單位進行顯示,默認顯示在第一個Item,當需要指定具體顯示的Item時,可以通過如下代碼來實現。
mListView.setSelection(N);
其中N就是需要顯示的第N個Item。
當然,這個方法類似scrollTo,是瞬間完成的移動。除此以外,還可以使用如下代碼來實現平滑移動。
mListView.smoothScrollBy(distance,duration); mListView.smoothScrollByOffset(offset); mListView.smoothScrollToPosition(index);
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; ArrayListmData; @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作為一個ViewGroup,為我們提供了操縱子View的各種方法,最常用的就是通過getChildAt()來獲取第i個子View,代碼如下所示。
for (int i = 0, len = mListView.getCheckedItemCount(); i < len; i++) { View view = mListView.getChildAt(i); }
ListView用於展示列表數據,但當列表中無數據時,ListView不會顯示任何數據或提示,按照完善用戶體驗的需求,這裡應該給以無數據的提示。幸好,ListView提供了一個方法——setEmptyView(),通過這個方法,我們可以給ListView設置一個在空數據下顯示的默認提示。包含ListView的布局設置如下。
<framelayout android:layout_height="match_parent" android:layout_width="match_parent" tools:context="com.blankj.listviewskill.MainActivity" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"></framelayout>
在代碼中,我們通過以下方式給ListView設置空數據時要顯示的布局,代碼如下所示。
mListView.setEmptyView(findViewById(R.id.empty_view));
通過以上代碼,就給ListView在空數據時顯示了一張默認的圖片,用來提示用戶;而在有數據時,則不會顯示。
ListView的滑動監聽,是ListView中最重要的技巧,很多重寫的ListView基本上都是在滑動事件的處理上下功夫,通過判斷滑動事件進行不同的邏輯處理。而為了更佳精確地監聽滑動事件,開發者通常還需要使用GestureDetector手勢識別、VelocityTracker滑動速度檢測等輔助類來完成更好的監聽。這裡介紹兩種監聽ListView滑動事件的方法,一個是通過OnTouchListener來實現監聽,另一個是使用OnScrollListener來實現監聽。
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是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的id mListView.getLastVisiblePosition(); // 獲取可視區域內第一個Item的id mListView.getFirstVisiblePosition();
項目地址→ListViewSkill
有些在使用越獄工具的朋友反映,使用過程中會提示“沒有找到蘋果設備”,由於這個錯誤提醒導致越獄不能繼續進行。當遇到這種情況我們應該怎麼
Android -- Camera2(Android5.0) Camera2 Camera2是Android5.0中的其中一個新的特性,新的API。與原來的
前言作為android六大布局中最為簡單的布局之一,該布局直接在屏幕上開辟出了一塊空白區域,當我們往裡面添加組件的時候,所有的組件都會放置於這塊區域的左上角;幀布局的大小
※效果 ※使用方法 package com.fancyy.calendarweight; import java.util.ArrayList; import j