一、引言
AdapterView本身是一個抽象類,而它派生的子類在用法上也基本相似,只是在顯示上有一定區別,因此把他們也歸為一類。
AdapterView具有如下特征:
- AdapterView繼承自ViewGroup,本質是個容器
- AdapterView可以包含多個“列表項”,並將這多個列表項以合適的形式展示。
- AdapterView顯示的多個列表項的內容由Adapter提供
AdapterView及其子類的繼承關系如下圖所示:
由於Gallery是一個過時的API,Android推薦使用HorizontalScrollView來替代,所以我也不再對它進行學習。
而由AdapterView直接派生的三個類:AbsListView、AbsSpinner、AdapterViewAnimator也都是抽象類,所以我們用的最多的也就是圖中第四行及以下的子類。
二、ListView
ListView就是我們所說的列表視圖,也是我們最常用的一個組件,它是以垂直列表的形式展示所有數據項,而生成列表視圖的方式主要有兩種:
- 直接用ListView進行創建
- 讓Activity繼承ListActivity
而一旦獲取到ListView后,對數據的填充就是由Adapter提供內容了,通常使用setAdapter方法。
ListView、GridView、Spinner等AdapterView都只是容器,而Apdater負責提供容器的內容。
AdapterView則負責采用合適的方式顯示Adapter提供的內容。
AbsListView的常用屬性和相關方法見下表:
屬性 |
相關方法 |
說明 |
android:choiceMode | 設置AbsListView的選擇方式: singleChoice:允許單選 multipleChoice:允許多選 multipleChoiceModal:允許多選 |
|
android:drawSelectorOnTop | setDrawSelectorOnTop(boolean) | 如果該屬性設置為true,選中的列表項將會顯示在上面 |
android:fastScrollEnabled | 設置是否允許快速滾動。如果該屬性設置為true,將會顯示滾動圖標,並允許用戶拖動該滾動圖標進行快速滾動。 | |
android:listSelector | setSelector(int) | 指定被選中的列表項上繪制的Drawable |
android:scrollingCache | 如果設置為true,則在滾動時將會使用緩存 | |
android:smoothScrollbar | setSmoothScrollbarEnabled(boolean) | 如果設置為false,則不在header View之后繪制分隔條 |
android:stackFromBottom | 設置是否從底端開始排列列表項 | |
android:textFilterEnabled | 設置是否對列表項進行過濾。只有當該AdapterView對應的Adapter實現了Filter接口時該屬性才會作用。 | |
android:transcriptMode | 設置組件的滾動模式: disabled:關閉滾動(默認) normal:當該AbsListView收到數據改變通知,且最后一個列表項可見時,該AbsListView將會滾動到底端。 alwaysScroll:該AbsListView總會自動滾動到底端 |
ListView提供的屬性如下表:
屬性 |
說明 |
android:divider | 設置List列表項的分隔條(可用顏色分隔,也可用drawable對象分隔) |
android:dividerHeiht | 設置分隔條的高度 |
android:entries | 指定一個數組資源,Android將根據該數組資源來生成ListView |
android:footerDividerEnabled | 如果設置成false,則不在footer View之前繪制分隔條 |
andorid:headerDividerEnabled | 如果設置成false,則不再header View之前繪制分隔條 |
示例代碼及顯示效果圖如下:
示例布局中只定義了一個ListView,並通過android:entries指定了數據源,通過android:divider指定了紅色的分隔條。
數據源位於value/arrays.xml中,代碼如下:
使用數組作為數據源十分簡單,但是局限性也很大,能定制的內容也非常少,甚至字號大小,顏色都不能改變。
如果想對ListView的外觀、行為進行定制,就需要把ListView作為AdapterView使用,通過Adapter控制列表項的顯示和交互。
三、Adapter接口及其實現類
Adapter本身是一個接口,它派生了ListAdapter和SpinnerAdapter兩個子接口,其中ListAdapter為AbsAdapter提供列表項,而SpinnerAdapter為AbsSpinner提供列表項。Adapter及其實現類的繼承關系如下圖所示:
其中ArrayAdapter、SimpleAdapter、SimpleCursorAdapter、BaseAdapter都是常用的實現類。
1.ArrayAdapter:簡單、易用的Adapter,通常用於將數組或者List集合的多個值包裝為列表項的數據源
2.SimpleAdapter:功能強大的Adapter,可用於將List集合的多個對象包裝為列表項的數據源
3.SimpleCursorAdapter:與SimpleAdapter類似,只是用於包裝Cursor提供的數據
4.BaseAdapter:通用用於被擴展。擴展BaseAdapter可以對各個列表項進行最大程度的定制。
3.1 ArrayAdapter
布局頁和示意圖如下:
布局中只有一個ListView,設置了分隔條為紅色。看一下后台代碼:
其中定義了一個字符串數組,通過ArrayAdapter進行包裝作為listView的數據源,R.layout.array_item則是數據項的布局,代碼如下:
(上右圖為運行效果圖)通過ArrayAdapter實現Adapter很簡單、易用,但是它的功能也非常有限,它的每個列表項必須只能是TextView,如果我們想要更負責的列表項,ArrayAdapter就不能勝任了。
3.2 SimpleAdatper
SimpleAdapter是個非常強大的Adapter實現類,絕大多數ListView的使用場景,SimpleAdapter都可以搞定。我們看一組示例:
首先頁面布局很簡單:
頁面只聲明了一個ListView
設置分隔線為紅色,高度為2px
然后我們定義一個ListViewItem的布局:
頁面布局說明:
1.整體采用線性布局,並以水平排列
2.左邊第一項定義一個ImageView組件,用來顯示圖片。
3.右邊嵌套一個線性布局,並以垂直排列
4.嵌套的線性布局里面定義了兩個TextView,一個顯示名字,一個顯示介紹。
關於TextView字體、顏色、邊距參加代碼。
然后我們看一下后台代碼:
說明:
1.我們定義了一個字符串數組,用來定義名稱。
2.然后又定義了一個字符串數字,用來定義介紹
3.准備幾張圖片作為頭像,拷貝到drawable文件夾,然后定義一個int數組,把圖片ID加進來
4.定義一個List<Map<String,Object>>集合,用來存儲列表項
5.循環填充數據
6.創建一個SimpleAdapter對象
7.設置ListView的Adapter為剛創建的SimpleAdapter
然后我們來看一下運行效果:
因為手機屏幕顯示大小有限,我們定義了四組數據,只顯示出了三組,實際操作可以拖動繼續顯示,大家也可以跟着一起做一下。
那我們如何處理這個ListView的單擊事件呢?
我們只需要在后台代碼的最后加上綁定事件監聽器(選中事件類似,只是選中事件綁定的是OnItemSelection事件):
3.3 BaseAdapter
雖然書上介紹說BaseAdapter是個擴展性很強的Adapter,但我從瘋3這本書上並沒有學太明白,所以這里占個位,等回頭吃透了再把這部分補上有養分的內容。
3.4 ListActivity
如果程序的窗口只需要顯示一個列表,就可以讓Activity直接繼承ListActivity來實現,ListActivity的子類無需調用setContextView方法來顯示界面,而是可以直接傳入一個內容Adapter,這里不再詳細介紹,大家可以直接百度一下內容,我覺得真正要實現高度的可定制化,這個用的不多,所以這里不詳細說了。
四、GridView
GridView從詞義上也可以看出它是行、列形式在界面上展示組件和數據信息。GridView和ListView有共同的父類AbsListView,它們都是列表項,唯一的區別就是ListView顯示一列數據,而GridView可以顯示多列數據,從這個角度上看,ListView也算一個特殊的GridView,當GridView只顯示一列時,就變成了ListView,下面給出GridView的屬性和相關方法:
屬性 | 方法 | 說明 |
android:columnWidth | setColumnWidth(int) | 設置列的寬度 |
android:gravity | setGravity(int) | 設置對其方式 |
android:horizontalSpacing | setHorizontalSpacing(int) | 設置各元素之間的水平間距 |
android:numColums | setNumColums(int) | 設置列數 |
android:stretchMode | setStretchMode(int) | 設置拉伸方式 |
android:verticalSpacing | setVerticalSpacing(int) | 設置各元素之間的垂直間距 |
GridView的數據填充與ListView一直,都可以以上以上介紹的幾種Adapter進行填充,下面直接看示例:
主Activity布局如下:
主要布局元素:
1.設置水平間距為1pt
2.設置垂直間距為1pt
3.設置對其方式是居中對齊
4.定義列數為4列
看一下我們對單元格樣式的定義:
主要布局思路:
1.單元格顯示的內容分上下兩部分,所以外部采用線性布局
2.內部一個圖片顯示組件ImageView,下方一個TextView用來顯示文字名稱
看一下后台代碼:
說明:
1.imgID為自定的一組圖標
2.文字標題這里沒有單獨的再定義,直接去了ID的字符串的前五位,主要為了保持顯示整齊。
3.這里是采用SimpleAdapter作為填充Adapter
4.單擊和選中事件這里沒有編寫,請參看ListView環節,方法一樣。
最后一張圖補一下顯示效果:
五、AutoCompleteTextView
從詞面上就能看出來這是個自動完成文本框組件,侍從EditText派生而來,實際上它也是個編輯框,只是比編輯框多了一個功能,就是當它進行輸入時,自動完成文本框會顯示一個下拉菜單供用戶選擇,用戶選擇后把選中的內容回填到文本框中。
AutoCompleteTextView除了可以使用EditText支持的屬性和方法以外,還支持以下所示的屬性和方法:
屬性 |
相關方法 |
說明 |
android:completionHint | setCompletionHint(CharSequence) | 設置下拉文本中的提示標題 |
android:completionHintView | 設置下拉文本中提示標題的視圖 | |
android:completionThreshold | setThreshold(int) | 設置用戶至少輸入幾個字符才會顯示提示 |
android:dropDownAnchor | setDropDownAnchor(int) | 設置下拉文本的定位錨點,如果不指定該屬性,將使用TextView本身作為錨點 |
android:dropDownHeight | setDropDownHeight(int) | 設置下拉文本的高度 |
android:dropDownHorizontalOffset | 設置下拉文本與文本框之間的水平偏移 | |
android:dropDownVerticalOffset | 設置下拉文本與文本框之間的垂直偏移 | |
android:dropDownWidth | setDropDownWidth(int) | 設置下拉菜單的寬度 |
android:popupBackground | setDropDownBackgroundResource(int) | 設置下拉菜單的背景 |
android:dropDownSelector | 下拉列表被選中的行的背景 |
使用AutoCompleteTextView比較簡單,只需要給它設置一個提供下拉文本的數據適配器就可以了,這也是把這個組件歸到這小節介紹的原因。直接給出示例代碼:
代碼很簡單,看下效果圖(虛擬機里沒調出來中文輸入,所以就隨便填了些英文):
六、ExpandableListView
ExpandableListView是通過分類列表來組織數據的一種ListView,在用戶點擊分組時他可以自動折疊或展開分組,最常見的就是我們平時用的最多的QQ,他的好友分組列表就是個很典型的例子。
因為它是ListView的子類,所以與ListView的用法基本類似,只是數據源的提供需要由ExpandableLisAdapter提供,因為ExpandableListAdapter是個接口,所以通常我們常用的實現方式會有如下幾種:
- 擴展實現BaseExpandableListAdapter實現ExpandableListAdapter
- 使用SimpleExpandableListAdapter將兩個List集合包裝成ExpandableListAdapter
- 使用SimpleCursorTreeAdapter將Cursor中的數據包裝成ExpandableListAdapter
說明:
ExpandableListAdapter接口繼承圖請自行查看API,我這里PD突然壞了打不開了,就不畫了。
ExpandableListView額外支持的屬性如下表:
屬性 | 說明 |
android:childDivider | 指定各組內各子列表之間的分隔條 |
android:chileIndicator | 顯示在子列表旁的圖片 |
android:groupIndicator | 顯示在組列表旁的圖片 |
下面我們直接來個示例用ExpandableListView來模仿QQ的好友分組顯示效果學習一下ExpandableListView的簡單用法
界面布局:
樣式很簡單,只有一個ExpandableListView控件。我把父節點和子項的顯示模板也定義一下:
這里也沒有用復雜的樣式簡單的放一個TextView顯示。后台代碼這次有點多,這次直接貼代碼了。
package com.boyliupan.studylessions.listview; import android.content.Context; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.widget.BaseExpandableListAdapter; import android.widget.ExpandableListView; import android.widget.TextView; import com.boyliupan.studylessions.R; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class ExpandListViewActivity extends AppCompatActivity { ExpandableListView expandableListView=null; List<String> parent=null; Map<String,List<String>> map=null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_expand_list_view); expandableListView= (ExpandableListView) findViewById(R.id.expandLview); initData(); expandableListView.setAdapter(new MySelfAdapter()); } public void initData() { parent = new ArrayList<String>(); parent.add("我的好友"); parent.add("家人"); parent.add("朋友"); map = new HashMap<String, List<String>>(); List<String> list1 = new ArrayList<String>(); list1.add("張三"); list1.add("李四"); list1.add("王五"); map.put("我的好友", list1); List<String> list2 = new ArrayList<String>(); list2.add("劉邦"); list2.add("劉霞"); list2.add("劉萌"); map.put("家人", list2); List<String> list3 = new ArrayList<String>(); list3.add("影墜"); list3.add("霍元子"); list3.add("噩夢領主"); map.put("朋友", list3); } class MySelfAdapter extends BaseExpandableListAdapter { @Override public int getGroupCount() { return parent.size(); } @Override public int getChildrenCount(int groupPosition) { String key = parent.get(groupPosition); int count=map.get(key).size(); return count; } @Override public Object getGroup(int groupPosition) { return parent.get(groupPosition); } @Override public Object getChild(int groupPosition, int childPosition) { String key = parent.get(groupPosition); return (map.get(key).get(childPosition)); } @Override public long getGroupId(int groupPosition) { return groupPosition; } @Override public long getChildId(int groupPosition, int childPosition) { return childPosition; } @Override public boolean hasStableIds() { return true; } @Override public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { if (convertView == null) { LayoutInflater inflater = (LayoutInflater) ExpandListViewActivity.this .getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.layout_ex_parent, null); } TextView tv = (TextView) convertView .findViewById(R.id.parenTv); tv.setText(ExpandListViewActivity.this.parent.get(groupPosition)); return convertView; } @Override public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { String key = ExpandListViewActivity.this.parent.get(groupPosition); String info = map.get(key).get(childPosition); if (convertView == null) { LayoutInflater inflater = (LayoutInflater) ExpandListViewActivity.this .getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.layout_ex_children, null); } TextView tv = (TextView) convertView .findViewById(R.id.childTv); tv.setText(info); return convertView; } @Override public boolean isChildSelectable(int groupPosition, int childPosition) { return true; } } }
效果圖如下:
這個例子寫下來感覺不理想,還需要再研究一下,精髓感覺沒把握住。給出兩個參考鏈接:
六、Spinner
Spinner是一個列表選擇框,功能使用也比較簡單,只需要給它指定對應的數據源,Spinner數據源的設定支持兩種方式,因為Spinner支持android:entries屬性,所以當設計時就可以確定下拉選項時,就可以直接指定entries屬性就可以讓Spinner正常工作,而如果Spinner的下拉項是動態的,則可以通過第二種方式就是adapter方式設定數據源。
在布局文件里,定義了兩個Spinner,第一個就是采用設定entries屬性的方式綁定數據源,第二種就是后台綁定數據。entries綁定的array就是個string的數組,這里不再貼代碼了。我們看一下后台動態添加代碼的實現:
代碼很簡單,主要用到了前面說的ArrayAdapter,如果下來列表想用更復雜的樣式,也可以用別的Adapter來實現。最終的顯示效果見下圖:
七、AdapterViewFlipper
AdapterViewFlipper繼承自AdapterViewAnimator,它會顯示Adapter提供的多個組件,但是每次只能顯示一個,可以通過調用它自身的方法切換上一個和下一個。
AdapterViewAnimator的屬性如下:
屬性 |
說明 |
android:animateFirstView | 設置顯示該組件的第一個View時是否使用動畫 |
android:inAnimation | 設置組件顯示時使用的動畫 |
android:loopViews | 設置循環到最后是否返回到第一個 |
android:outAnimation | 設置組件隱藏時的動畫 |
AdapterViewFlipper額外支持的屬性如下:
屬性 |
相關方法 |
說明 |
android:autoStart | startFlipping() | 設置是否自動播放 |
android:flipInterval | setFlipInterval(int) | 設置自動播放的間隔 |
下面給出一個簡單的示例來演示:
布局如圖:上面3個按鈕,下面一個AdapterViewFlipper。后台代碼如下,代碼中已經加了注釋,不再解釋說明:
package com.boyliupan.studylessions44678; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterViewFlipper; import android.widget.BaseAdapter; import android.widget.ImageView; public class ViewFlipperActivity extends AppCompatActivity { int[] imgIDs=new int[]{R.drawable.head1,R.drawable.head2,R.drawable.head3, R.drawable.head4,R.drawable.head5}; AdapterViewFlipper viewFlipper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_view_flipper); viewFlipper= (AdapterViewFlipper) findViewById(R.id.flipper); BaseAdapter adapter=new BaseAdapter() { @Override public int getCount() { return imgIDs.length; } @Override public Object getItem(int position) { return position; } @Override public long getItemId(int position) { return position; } //代表每一項的視圖 @Override public View getView(int position, View convertView, ViewGroup parent) { ImageView imageView=new ImageView(ViewFlipperActivity.this); imageView.setImageResource(imgIDs[position]); imageView.setScaleType(ImageView.ScaleType.FIT_XY);//設置縮放方式 //設置布局參數 imageView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); return imageView; } }; viewFlipper.setAdapter(adapter); } //顯示上一個 public void pre(View view) { viewFlipper.showPrevious(); viewFlipper.stopFlipping(); } //顯示下一個 public void next(View view) { viewFlipper.showNext(); viewFlipper.stopFlipping(); } //自動播放 public void auto(View view) { viewFlipper.startFlipping(); } }
效果圖如下:
八、StackView
StackView也是AdapterViewAnimator的子類,只是他會以堆疊的形式來顯示,我們先來看一下最終效果,再看代碼:
看一下頁面布局:
為了演示方便,沒有再加入新的圖片,還是用的上個例子的圖片,看一下后台代碼:
里面用到了自定義的一個cell.xml的布局文件,這里也給出代碼:
很簡答的一個例子,大家可以自己動手試一下。
九、結語&下章預告
這一章終於寫完了,感覺寫完了,學的還是不夠深入,有些東西似懂非懂,所以停下來好好思考一下。
對這些內容感興趣的可以收藏一下這個系列的目錄,對您又幫助的別忘記點下推薦!謝謝。
下章預告就是下章的內容會依次說一下關於進度條相關控件的使用方法。
十、源碼下載
源碼下載:點擊下載 (不含Spinner、AdapterViewFipper、StackView)
源碼下載:點擊下載 (僅含Spinner、AdapterViewFipper、StackView)