Android开发自学笔记(Android Studio)—4.4 AdapterView及其子类
一、引言
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)