在Android中可滑動的列表是常見的UI佈局效果. 因此熟練掌握列表控件是確定的; RecyclerView是列表控件中最重要最複雜的因此將在另外一篇文章中詳細講解, 本文不涉及;java
主要功能使列表複用. 能夠處理大量的列表控件組合. 在android21之前屬於最經常使用控件之一. 以後引入RecyclerView控件. 其做用比ListView更增強大可定製性更高.android
可是某些時候用ListView比RecyclerView更加方便. 並且官方並無說應該被廢棄.api
在佈局文件中可使用的xml屬性數組
屬性 | 描述 |
---|---|
android:entries | 引用一個數組資源來構成列表 |
android:divider | 分割線(android:divider="@null" 能夠去除分割線) |
android:dividerHeight | 分割線高度(即便設爲0也會有1dp高度) |
android:footerDividersEnabled | 是否開啓腳部分割線 |
android:headerDividersEnabled | 會否開啓頭部分割線 |
預覽item緩存
tools:listitem="@layout/demo_item"
複製代碼
添加布局bash
void addHeaderView (View v, Object data, boolean isSelectable) void addHeaderView (View v) void addFooterView (View v, Object data, boolean isSelectable) void addFooterView (View v) 複製代碼
刪除佈局架構
boolean removeHeaderView (View v) boolean removeFooterView (View v) 複製代碼
分割線app
// 分割線是否啓用
boolean areHeaderDividersEnabled () boolean areFooterDividersEnabled () // 是否啓用分割線 void setHeaderDividersEnabled (boolean headerDividersEnabled) void setFooterDividersEnabled (boolean footerDividersEnabled) // 頭部和腳部的越界顯示圖片, 默認狀況ListView是沒法越界, 故默認沒有效果 void setOverscrollFooter (Drawable footer) void setOverscrollHeader (Drawable header) Drawable getOverscrollHeader () Drawable getOverscrollFooter () 複製代碼
獲得佈局數量ide
int getFooterViewsCount () int getHeaderViewsCount () 複製代碼
條目選擇函數
void setSelection (int position) 複製代碼
smoothScroll表明平滑滾動, By表明相對距離移動, To只會滾動到指定位置後無變化
// 相對滾動位置
void smoothScrollToPosition (int position) void smoothScrollByOffset (int offset) 複製代碼
除此以外還繼承了AbsListView的滾動方法
// 取消快速滾動條(快速滾動時依舊有個沒法拖動的小滾動條)
void setSmoothScrollbarEnabled (boolean enabled) boolean isSmoothScrollbarEnabled () // 平滑滾動的同時限定了最大滾動範圍 void smoothScrollToPosition (int position, int boundPosition) // 範圍單位px // 滾動像素單位, 而且能夠控制滾動持續時間 void smoothScrollBy (int distance, int duration) // 指定的滾動位置會向上偏移一段距離 void smoothScrollToPositionFromTop (int position, int offset) // 偏移距離 // 增長控制滾動持續時間 void smoothScrollToPositionFromTop (int position, int offset, int duration) 複製代碼
滾動
void scrollListBy (int y)
複製代碼
學習適配器就要區分方法的做用:
用於重寫的方法. 這類方法是給ListView來調用的(適配器經過setAdapter()傳入ListView)
用於調用的方法. 暴露給用戶來控制Item的
ListView採用MVC的架構, View和Data由一個Adapter控制. ListView使用的Adapter是接口ListAdapter. 使用setAdapter()
方法設置.
最基礎的適配器ListAdapter屬於接口. 須要實現的方法不少. 爲了方便提供了繼承ListAdapter的抽象適配器
適配器的繼承關係
以上講的適配器適用於ListView和GridView以及Spinner.
ListAdapter屬於接口, 通常狀況並不直接使用, 由於不必重寫所有方法. 通常使用其子類.
// 是否啓用item. 若是fasle則不啓用. item處於沒法選擇和點擊的狀態
boolean isEnabled(int position);
// 能夠看到沒有position參數. 因此若是返回fasle則所有item都處於不啓用狀態
public boolean areAllItemsEnabled();
複製代碼
繼承父類的方法
int getCount () // 決定ListView的Item數量 Object getItem (int position) // 獲得item 數據. 這裏返回的值會在ListView中使用到 long getItemId (int position) // 獲得item 的 id. 這裏返回的值會在ListView中使用到 // 返回Item類型, 類型是否相同決定是否複用item int getItemViewType (int position) // 返回Item視圖內容 View getView (int position, // 位置 View convertView, // 複用視圖 ViewGroup parent) // 父容器 // 返回Item類型數量 int getViewTypeCount () // id是否惟一 boolean hasStableIds () boolean isEmpty () // 是否爲空 void registerDataSetObserver (DataSetObserver observer) // 註冊數據觀察者 void unregisterDataSetObserver (DataSetObserver observer) // 取消數據觀察者 複製代碼
hasStableIds()
這個方法是判斷id是不是有效. 返回true有效false無效.
getItemId()
的返回id值來判斷item是不是相同首先我講講最經常使用適配器 BaseAdapter.
特色:
ListView支持高度自定義的Item
須要本身重寫該適配器來使用
重寫方法
final String[] title = {"用戶", "首頁", "設置", "關於", "反饋"};
// 這是寫了個匿名類
mListView.setAdapter(new BaseAdapter() {
/** * 控制ListView的Item的數量 * @return */
@Override
public int getCount() {
return title.length;
}
/** * 控制ListView的某些方法返回的Object數據. * 例如ListView的getItemAtPosition()方法. 經過位置索引獲得數據對象, 即該方法返回的Object對象 * * @param position * @return */
@Override
public Object getItem(int position) {
return null;
}
/** * 每次點擊item都會回調該方法. 一樣是爲了ListView的getItemIdAtPosition()方法可以獲得item的id * * @param position * @return */
@Override
public long getItemId(int position) {
return 0;
}
/** * 控制ListView的Item的視圖顯示 * * @param position 當前顯示的視圖位置 * @param convertView 緩存的視圖. 用於複用item * @param parent 父容器佈局 * @return */
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = View.inflate(MainActivity.this, R.layout.item_list, null);
// 根據傳入的數據進行修改
TextView text = ButterKnife.findById(view, R.id.text);
text.setText(title[position]);
return view;
}
});
複製代碼
在主佈局中添加控件
<RelativeLayout 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.liangjingkanji.listview.MainActivity">
<ListView android:id="@+id/list" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</RelativeLayout>
複製代碼
注意inflate item 視圖的時候是否啓用parent.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
<TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</LinearLayout>
複製代碼
後面的佈局文件我不會再寫出來了. 多餘代碼影響閱讀性.
介紹下BaseAdapter相對於父類ListAdapter增長的方法. 這些方法都不是必須的
// 判斷適配器是否存在item
boolean isEmpty () // **DropDownView**等方法是重寫的SpinnerAdapter的. 因此會在講解Spinner的時候詳細講. ListView和GridView用不到 View getDropDownView (int position, View convertView, ViewGroup parent) boolean hasStableIds () void notifyDataSetChanged () // 數據若是發生變化通知ListView局部更新 void notifyDataSetInvalidated () //數據若是發生變化通知ListView整個更新 複製代碼
ArrayAdapter是BaseAdapter的子類, 進行了進一步的封裝, 可以快速實現最簡單的字符串列表(同時限制了數據只能是單一的字符串). 注意這不是抽象類. 能夠直接建立對象.
特色:
建立ArrayAdapter的時候須要指定泛型ArrayAdapter<T>
. 泛型決定了構造方法能接受的數據類型
構造方法
ArrayAdapter (Context context, // 上下文
int resource) // 佈局id. 只支持根佈局是TextView的佈局
ArrayAdapter (Context context,
int resource, // 這個構造方法就支持任意佈局了
int textViewResourceId) // 指定一個Textview的id來設置數據.
ArrayAdapter (Context context,
int resource,
T[] objects) // 直接在構造方法添加數據, 必須是字符串的數組
ArrayAdapter (Context context,
int resource,
int textViewResourceId,
T[] objects) // 同上
ArrayAdapter (Context context,
int resource,
List<T> objects)// 添加數據集合, 一樣必須是字符串
ArrayAdapter (Context context,
int resource,
int textViewResourceId, // 同上
List<T> objects) // 添加數據集合
複製代碼
示例
String[] array = {"用戶", "首頁", "設置", "關於", "反饋"};
// 這裏的android.R.layout.simple_list_item_1是系統提供的TextView佈局文件推薦直接拿來用. 能夠點開看下源碼.
listView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, array));
複製代碼
看上去是否是很方便就實現了ListView.
注意
ArrayAdapter內部已經對ListView進行了Item複用
傳入的數據必須是字符串. 源碼中進行了判斷
ArrayAdapter並無進行ViewHolder的複用.
方法
// 添加一個數據
void add (T object) // 添加多個數據 void addAll (T... items) // 添加集合 void addAll (Collection<? extends T> collection) // 刪除所有數據 void clear () // 刪除指定數據 void remove (T object) // 對應位置插入數據 void insert (T object, int index) // 默認爲true. 因此每次你對數據修改的時候都會調用notifyOnChange方法. 屢次調用影響效率(頻繁刷新UI). 能夠用該方法設置false.而後本身調用notifyChange等方法來更新Item的UI. void setNotifyOnChange (boolean notifyOnChange) // 這是一個靜態方法直接建立ListView. 簡單直接 ArrayAdapter<CharSequence> createFromResource (Context context, int textArrayResId, // 文本數據 int textViewResId) // 文本控件id // 獲得傳入的上下文 Context getContext () // 經過數據獲得索引 int getPosition (T item) 複製代碼
再介紹兩個用於重寫支持ListView的篩選Item功能的方法
// 過濾器. 例如聯繫人的聯想篩選
Filter getFilter () // 對數據使用標準的比較器排序操做 void sort (Comparator<? super T> comparator) 複製代碼
SimpleAdapter是這三種中最複雜的適配器, 可是數據填充ListView的item很方便. 一樣非抽象類能夠直接建立對象使用.
一樣先介紹構造方法
SimpleAdapter (Context context,
List<? extends Map<String, ?>> data, // 數據. 每一個Item對應一個Map集合
int resource, // Item的佈局
String[] from, // Map集合是無序的. 因此須要一個key數組來控制順序
int[] to) // 數據填充到該數組對應的View id
複製代碼
示例
建立一個ListView做爲數據
// List集合存儲Map集合表明數據
List<Map<String, Object>> list = new ArrayList<>();
Map<String, Object> map = new HashMap<>();
map.put("icon", R.mipmap.ic_launcher);
map.put("name", "設置");
Map<String, Object> map2 = new HashMap<>();
map2.put("icon", R.mipmap.ic_launcher);
map2.put("name", "關於");
list.add(map);
list.add(map2);
// String數據的值是Map集合中的鍵, 對應int數組中的控件id. 將鍵對應的值填充到對應的id控件上
listView.setAdapter(new SimpleAdapter(this,list, R.layout.list_item, new String[]{"icon", "name"}, new int[]{R.id.icon, R.id.name}));
複製代碼
查看源碼能夠看出來其實SimpleAdapter只支持TextView和Checkable以及ImageView三種控件的屬性值設定, 若是非該三種將拋出語法異常信息.
若是傳入ViewBinder接口能夠在回調方法內手動處理數據填充
SimpleAdapter.ViewBinder getViewBinder () void setViewBinder (SimpleAdapter.ViewBinder viewBinder) 複製代碼
以前就提過這個適配器和BaseAdapter同樣繼承自ListAdapter. 不過這是接口. 內部就一個方法:
// 返回適配器對象. 等同於getAdapter
ListAdapter getWrappedAdapter () 複製代碼
支持頭佈局和腳佈局的ListAdapter
構造方法
// 能夠看出主要就是加入兩個包含頭佈局和腳佈局的集合外加一個普通ListAdapter便可
HeaderViewListAdapter (ArrayList<ListView.FixedViewInfo> headerViewInfos,
ArrayList<ListView.FixedViewInfo> footerViewInfos,
ListAdapter adapter)
複製代碼
其實ListView支持直接添加頭佈局和腳佈局, 這個以前提過. addHeaderView
和addFootView
內部實現就是將原有適配器包裹成HeaderViewListAdapter.
FixedViewInfo
public class FixedViewInfo {
// 視圖
public View view;
// 數據
public Object data;
// 是否可選擇. Selectable狀態
public boolean isSelectable;
}
複製代碼
// 普通點擊事件
void setOnClickListener (View.OnClickListener l) // item點擊事件 void setOnItemClickListener (AdapterView.OnItemClickListener listener) // item長按點擊事件 void setOnItemLongClickListener (AdapterView.OnItemLongClickListener listener) // item選擇事件 void setOnItemSelectedListener (AdapterView.OnItemSelectedListener listener) 複製代碼
經常一個列表中不可能條目都是如出一轍的, 須要摻雜一些不一樣類型的Item.
下面介紹兩個方法getViewTypeCount
和getItemViewType
默認狀況下兩個方法實現
咱們經過重寫BaseAdapter中的這兩個方法實現多條目
Tip: 若是直接在getView方法中經過position判斷的話會致使視圖複用機制(convertView)沒法正常運行. 會致使視圖錯亂.
划動屏幕時ListView從屏幕消失的條目會被銷燬, 而新出如今屏幕的條目會被建立. 若是在大量的且顯示內容過多的條目快速划動會形成回收內存的速度趕不上建立條目對象的速度, 而形成oom內存溢出. 爲了不這種狀況ListView須要進行內存複用優化.
條目在每次被顯示在屏幕上的時候都會調用getView()
方法, 若是每次在該方法中都建立一個View對象的狀況下有大量的條目會致使OOM內存溢出, 因此使用的時候須要複用這個View對象, 而getView方法中已經緩存了這個對象即convertView
參數,只須要調用便可.
/** * 返回條目顯示內容, 就是一個View對象 * @param position 條目所在位置 * @param convertView 條目複用對象 * @param parent * @return */
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
// 若是convertView不爲空則複用
if (convertView == null) {
view = View.inflate(MainActivity.this, R.layout.list_item, null);
}else {
view = convertView;
}
TextView text = (TextView) view.findViewById(R.id.text);
text.setText("當前條目" + position);
return view;
}
複製代碼
每次findViewById都會進行整個佈局的遍歷, 容易影響程序的運行效率, 因此能夠建立ViewHolder類進行控件引用的存儲. 而後每一個Item對該ViewHolder進行存取操做;
關鍵方法setTag()
//在外面先定義,ViewHolder靜態類
static class ViewHolder {
public ImageView img;
public TextView title;
public TextView info;
}
//而後重寫getView
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if(convertView == null)
{
holder = new ViewHolder();
convertView = mInflater.inflate(R.layout.list_item, null);
holder.img = (ImageView)item.findViewById(R.id.img)
holder.title = (TextView)item.findViewById(R.id.title);
holder.info = (TextView)item.findViewById(R.id.info);
convertView.setTag(holder);
}else
{
holder = (ViewHolder)convertView.getTag();
}
holder.img.setImageResource(R.drawable.ic_launcher);
holder.title.setText("Hello");
holder.info.setText("World");
}
return convertView;
}
複製代碼
RecyclerView都是強制使用ViewHolder;
ListView其實支持多種選擇模式(ChoiceModel)的設置
/** * 沒有選擇模式 */
public static final int CHOICE_MODE_NONE = 0;
/** * 單選模式 */
public static final int CHOICE_MODE_SINGLE = 1;
/** * 多選模式 */
public static final int CHOICE_MODE_MULTIPLE = 2;
/** * The list allows multiple choices in a modal selection mode */
public static final int CHOICE_MODE_MULTIPLE_MODAL = 3;
複製代碼
經過佈局屬性設置選擇模式
android:choiceMode
複製代碼
Constant | Value | Description |
---|---|---|
multipleChoice | 2 | 多選模式 |
multipleChoiceModal | 3 | 屏蔽點擊事件的多選模式 |
none | 0 | 非選模式 |
singleChoice | 1 | 單選模式 |
關於選擇模式的方法都在AbsListView抽象類中
boolean isItemChecked (int position) // 判斷指定位置的item是否被選中 void setItemChecked (int position, boolean value) // 設置item選中 int getCheckedItemCount () // 被選中的item數量 long[] getCheckedItemIds () // 若是非非選模式而且(hasStableIds() == true) 纔有效 int getCheckedItemPosition () // 選擇的位置索引(只在單選模式有效) SparseBooleanArray getCheckedItemPositions () // 獲得被選中的全部item的位置 void clearChoices () // 清理全部被選擇 int getChoiceMode () // 選擇模式 void setChoiceMode (int choiceMode) // 設置選擇模式 void setMultiChoiceModeListener (AbsListView.MultiChoiceModeListener listener) // 多選監聽器 複製代碼
選擇模式生效必須知足如下兩點:
設置選擇模式(默認狀況是單選模式)
Item沒有子控件攔截焦點
android:focusable="false"
複製代碼
該模式下要想進入多選模式有如下兩種方式:
setItemChecked
翻譯即"網格視圖"的意思. 和ListView的區別就是網格列表.
可是注意GridView並非水平滑動佈局, 只不過是增長列數的ListView(即知足列數限制就換行, 默認一行即ListView). 須要水平滑動佈局可使用HorizontalScrollView
以及RecyclerView的GridLayoutManager佈局
GridView兼容ListView的全部適配器.
每一個屬性都有對應的方法. 詳細介紹看方法.
屬性 | 描述 |
---|---|
android:columnWidth | 列寬(默認無效)須要配合拉伸模式使用 |
android:numColumns | 列數(默認爲1) |
android:stretchMode | 拉伸模式 |
android:horizontalSpacing | 水平間距 |
android:verticalSpacing | 垂直間距 |
android:gravity | 對齊方式 |
用法和ListView同樣. 支持以前講過的全部的ListAdapter適配器;
mList.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, title));
mList.setNumColumns(2); // 設置列數. 默認1. 即和ListView沒區別
複製代碼
GridView就像一個網格列表. 學會如何排列網格是很是重要的.
上圖標註了GridView的列寬以及水平和垂直間距
// 格子寬度. 像素單位
int getColumnWidth () void setColumnWidth (int columnWidth) 複製代碼
Tip: 若是在item的佈局中設置了固定大小, 會致使裁剪效果.
拉伸模式我認爲是GridView最重要也是最難理解的地方. 注意理解個人分析
// 設置拉伸模式
void setStretchMode (int stretchMode) 複製代碼
支持四種拉伸模式
NO_STRETCH
不拉伸, 尺寸本身控制. 該模式下必須設置網格寬度不然什麼都不顯示
STRETCH_COLUMN_WIDTH (拉伸列寬)
默認模式, 列寬由屏幕拉伸決定. 因此以前介紹的setColumnWidth無效. 網格內容會根據屏幕的大小來比例縮放控制
STRETCH_SPACING (拉伸間距)
該模式必須制定列寬不然不顯示, 同時本身指定的間距(包括水平間距和垂直間距)無效
STRETCH_SPACING_UNIFORM
此模式下列寬和間距都是有效設置值, 而且水平方向最左邊也會有間距.
可是因爲都是有效值因此沒法作到屏幕均布的效果
除了STRETCH_COLUMN_WIDTH其餘模式都須要指定網格寬度(setColumnWidth).
總結:
// 設置水平間隔
int getHorizontalSpacing () void setHorizontalSpacing (int horizontalSpacing) // 設置垂直間隔 void setVerticalSpacing (int verticalSpacing) int getVerticalSpacing () 複製代碼
void setNumColumns (int numColumns) int getNumColumns () 複製代碼
int getGravity () void setGravity (int gravity) 複製代碼
// 返回適配器
ListAdapter getAdapter () 複製代碼
這裏介紹GridView和ListView之間或者兩種相同列表的相互嵌套.
列表嵌套的問題分爲兩種狀況:
若是存在列表嵌套了一個高度爲wrap_content|match_parent
的列表時會發現被嵌套的列表沒法徹底顯示, 可是若是固定的高度就不會發生這種狀況, 可是不少數據都並非固定的而是經過數據的數量動態加載.
經過自定義onMeasure方法給被嵌套的ListView一個無限的高度最大值
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
複製代碼
integer的最大值是32位, 之因此右移兩位是由於在MeasureSpec中前兩位表示模式
// 移位位數 30 private static final int MODE_SHIFT = 30;
// int 型佔 32 位,左移 30 位,該屬性表示掩碼值,用來與 size 和 mode 進行 "&" 運算,獲取對應值。
private static final int MODE_MASK = 0x3 << MODE_SHIFT;// 左移 30 位,其值爲 00...(此處省略 30 個0)
public static final int UNSPECIFIED = 0 << MODE_SHIFT;// 左移 30 位,其值爲 01...(此處省略 30 個0)
public static final int EXACTLY = 1 << MODE_SHIFT;// 左移 30 位,其值爲 10...(此處省略 30 個0)
public static final int AT_MOST = 2 << MODE_SHIFT;
若是不想重寫就經過ListView的Item數量來動態的設置ListView的固定高度
private void setListViewHeight(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
return;
}
int totalHeight = 0;
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight
+ (listView.getDividerHeight() * (listAdapter.getCount() - 1));
listView.setLayoutParams(params);
}
複製代碼
或者你能夠在getView方法中設置每一個View對象的layoutParams
上面介紹的兩種方法只是針對ListView被嵌套時不顯示的問題. 可是若是ListView裏面嵌套的要是一個可滑動的ListView就須要另外解決了.
其實Google已經考慮到這種問題了, 提供方法能夠直接生效
ViewCompat.setNestedScrollingEnabled(mList,true); // api21以上能夠直接使用View而不是ViewCompat
複製代碼
關於NestedScrollView以及ScrollView中嵌套ListView或GridView不會出現第二種狀況, 可是也會出現顯示不徹底. 一樣解決方法.
對於RecyclerView中嵌套GridView和ListView第二種方法的解決辦法就失效了.
ExpandableListView 是支持分組展開的ListView.
主要分爲兩部分:組和子列表
指示器圖標
android:groupIndicator
android:childIndicator
複製代碼
指示器間隔
android:indicatorEnd
android:indicatorLeft
android:indicatorRight
android:indicatorStart
android:childIndicatorEnd
android:childIndicatorLeft
android:childIndicatorRight
android:childIndicatorStart
複製代碼
指示器圖標會和你的getView()
視圖內容重疊. 建議給item設置一個padding
子列表分割線
android:childDivider
複製代碼
分割線能夠是圖片或者顏色. 可是不管如何都是一個高度爲1dp的全屏寬度的分割線. 且必須適配器isChildSelectable()
方法返回true纔會顯示.
去除默認的指示器和分割線
android:divider="@null"
android:groupIndicator="@null"
複製代碼
ExpandaleListAdapter屬於接口. 通常狀況直接使用其子類.
abstract boolean areAllItemsEnabled() // 該方法用於適配器返回視圖數據 abstract Object getChild(int groupPosition, int childPosition) // 子列表item id abstract long getChildId(int groupPosition, int childPosition) // 子列表item 視圖 abstract View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) // 子列表 abstract int getChildrenCount(int groupPosition) abstract long getCombinedChildId(long groupId, long childId) abstract long getCombinedGroupId(long groupId) // 組對象 abstract Object getGroup(int groupPosition) // 組數量 abstract int getGroupCount() // 組id abstract long getGroupId(int groupPosition) // 返回組視圖 abstract View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) // id是否穩定, 該方法等同於ListView abstract boolean hasStableIds() // 子列表是否可選 abstract boolean isChildSelectable(int groupPosition, int childPosition) abstract boolean isEmpty() // 組摺疊回調 abstract void onGroupCollapsed(int groupPosition) // 組展開回調 abstract void onGroupExpanded(int groupPosition) 複製代碼
相似BaseAdapter, 須要實現的方法上面已經介紹過了. 下面直接示例;
public class CustomExpandableListAdapter extends BaseExpandableListAdapter {
private Context mContext;
private List<String> mGroupData;
private List<ArrayList<String>> mChildData;
public CustomExpandableListAdapter(Context context, List<String> groupData, List<ArrayList<String>> childData) {
mContext = context;
mGroupData = groupData;
mChildData = childData;
}
@Override public int getGroupCount() {
return mGroupData.size();
}
@Override public int getChildrenCount(int i) {
return mChildData.size();
}
@Override public Object getGroup(int i) {
return mGroupData.get(i);
}
@Override public Object getChild(int i, int i1) {
return mChildData.get(i).get(i1);
}
@Override public long getGroupId(int i) {
return i;
}
@Override public long getChildId(int i, int i1) {
return i+i1;
}
@Override public boolean hasStableIds() {
return true;
}
@Override public View getGroupView(int i, boolean b, View view, ViewGroup viewGroup) {
if(view == null) {
}
View groupView = LayoutInflater.from(mContext).inflate(R.layout.item_group_text, viewGroup, false);
TextView tvTitle = ButterKnife.findById(groupView, R.id.tv_title);
tvTitle.setText(mGroupData.get(i));
return groupView;
}
@Override public View getChildView(int i, int i1, boolean b, View view, ViewGroup viewGroup) {
View childView = LayoutInflater.from(mContext).inflate(R.layout.item_group_text, viewGroup, false);
TextView tvTitle = ButterKnife.findById(childView, R.id.tv_title);
tvTitle.setText(mChildData.get(i).get(i1));
return childView;
}
@Override public boolean isChildSelectable(int i, int i1) {
return true;
}
}
複製代碼
SimpleExpandableListAdapter和SimpleAdapter差很少不屬於抽象類, 只須要使用構造方法建立實例便可.
SimpleExpandableListAdapter (Context context,
List<? extends Map<String, ?>> groupData, // 數據
int groupLayout, // 組視圖佈局
String[] groupFrom, // 數據鍵
int[] groupTo, // 佈局控件id
List<? extends List<? extends Map<String, ?>>> childData,
int childLayout, // 子列表視圖
String[] childFrom,
int[] childTo)
SimpleExpandableListAdapter (Context context,
List<? extends Map<String, ?>> groupData,
int expandedGroupLayout, // 租展開佈局
int collapsedGroupLayout, // 組摺疊佈局
String[] groupFrom,
int[] groupTo,
List<? extends List<? extends Map<String, ?>>> childData,
int childLayout,
String[] childFrom,
int[] childTo)
SimpleExpandableListAdapter (Context context,
List<? extends Map<String, ?>> groupData,
int expandedGroupLayout,
int collapsedGroupLayout,
String[] groupFrom,
int[] groupTo,
List<? extends List<? extends Map<String, ?>>> childData,
int childLayout,
int lastChildLayout, // 租最後一個子列表的視圖
String[] childFrom,
int[] childTo)
複製代碼
從構造方法能夠看到SimpleExpandableListAdapter
還支持兩種固定的多類型佈局. 不過須要注意的是多類型佈局的groupTo/childTo
控件id仍是必須包括在內的.
子列表點擊事件
void setOnChildClickListener (ExpandableListView.OnChildClickListener onChildClickListener) 複製代碼
組點擊事件
void setOnGroupClickListener (ExpandableListView.OnGroupClickListener onGroupClickListener) 複製代碼
組收縮和展開事件
void setOnGroupCollapseListener (ExpandableListView.OnGroupCollapseListener onGroupCollapseListener) void setOnGroupExpandListener (ExpandableListView.OnGroupExpandListener onGroupExpandListener) 複製代碼
取消組的摺疊和收縮只須要在組點擊事件的回調中返回true便可
mExpand.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
@Override
public boolean onGroupClick(ExpandableListView expandableListView, View view, int i, long l) {
return true;
}
});
複製代碼
雖然Spinner是容器佈局不過並不支持子控件.由於其繼承了AdapterView;
簡單實現
佈局中建立控件
<Spinner android:id="@+id/spinner" android:layout_width="wrap_content" android:layout_height="wrap_content" android:entries="@array/entries" />
複製代碼
values/strings 建立數組實體
<resources>
<string-array name="entries">
<item>Mercury</item>
<item>Venus</item>
<item>Earth</item>
<item>Mars</item>
<item>Jupiter</item>
<item>Saturn</item>
<item>Uranus</item>
<item>Neptune</item>
</string-array>
</resources>
複製代碼
// 水平和垂直偏移. 只有垂直是有效的
android:dropDownHorizontalOffset
android:dropDownVerticalOffset
android:dropDownWidth // 下拉彈窗寬度
android:dropDownSelector // 下拉顏色選擇器
android:gravity
android:popupBackground // 下拉彈窗背景顏色
android:spinnerMode // 對話框/下拉彈窗
android:prompt // 對話框模式的標題, 注意須要引用string
複製代碼
mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
}
@Override public void onNothingSelected(AdapterView<?> parent) {
}
});
複製代碼
若是隻是想使用下拉列表能夠看看ListPopupWindow
; 單純的下拉列表, 提供依附功能和自定義寬高;