ArrayAdapter (Context context,
int resource,
int textViewResourceId, // 同上
List objects) // 添加数据集合
示例
String[] array = {“用户”, “首页”, “设置”, “关于”, “反馈”};
// 这里的android.R.layout.simple_list_item_1是系统提供的TextView布局文件推荐直接拿来用. 可以点开看下源码.
listView.setAdapter(new ArrayAdapter(this, android.R.layout.simple_list_item_1, array));
看上去是不是很方便就实现了ListView.
注意
- ArrayAdapter内部已经对ListView进行了Item复用
2. 传入的数据必须是字符串. 源码中进行了判断
3. 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 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
SimpleAdapter是这三种中最复杂的适配器, 但是数据填充ListView的item很方便. 同样非抽象类可以直接创建对象使用.
同样先介绍构造方法
SimpleAdapter (Context context,
List<? extends Map
示例
创建一个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)
WrapperListAdapter
之前就提过这个适配器和BaseAdapter一样继承自ListAdapter. 不过这是接口. 内部就一个方法:
// 返回适配器对象. 等同于getAdapter
ListAdapter getWrappedAdapter ()
HeaderViewListAdapter
支持头布局和脚布局的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;
}
ViewHolder
每次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”
屏蔽点击事件的多选模式
该模式下要想进入多选模式有以下两种方式:
- 长按item
- 通过函数调用
setItemChecked
GridView
翻译即"网格视图"的意思. 和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(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
ExpandableListView 是支持分组展开的ListView.
主要分为两部分:组和子列表
Attributes
指示器图标
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”
ExpandableListAdapter
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)
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
总结
算法知识点繁多,企业考察的题目千变万化,面对越来越近的“金九银十”,我给大家准备好了一套比较完善的学习方法,希望能帮助大家在有限的时间里尽可能系统快速的恶补算法,通过高效的学习来提高大家面试中算法模块的通过率。
这一套学习资料既有文字档也有视频,里面不仅仅有关键知识点的整理,还有案例的算法相关部分的讲解,可以帮助大家更好更全面的进行学习,二者搭配起来学习效果会更好。
部分资料展示:
有了这套学习资料,坚持刷题一周,你就会发现自己的算法知识体系有明显的完善,离大厂Offer的距离更加近。
资料获取方法:点赞+关注+转发,然后进入我的【GitHub】,里面有免费获取途径
,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频**
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-zGYj7aeF-1710508812259)]
总结
算法知识点繁多,企业考察的题目千变万化,面对越来越近的“金九银十”,我给大家准备好了一套比较完善的学习方法,希望能帮助大家在有限的时间里尽可能系统快速的恶补算法,通过高效的学习来提高大家面试中算法模块的通过率。
这一套学习资料既有文字档也有视频,里面不仅仅有关键知识点的整理,还有案例的算法相关部分的讲解,可以帮助大家更好更全面的进行学习,二者搭配起来学习效果会更好。
部分资料展示:
[外链图片转存中…(img-ac6Bz7sl-1710508812260)]
[外链图片转存中…(img-BtnQpINT-1710508812260)]
[外链图片转存中…(img-Tfk9r9UL-1710508812261)]
[外链图片转存中…(img-FRYao3ps-1710508812261)]
有了这套学习资料,坚持刷题一周,你就会发现自己的算法知识体系有明显的完善,离大厂Offer的距离更加近。
资料获取方法:点赞+关注+转发,然后进入我的【GitHub】,里面有免费获取途径