在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
; 单纯的下拉列表, 提供依附功能和自定义宽高;