ListView/GridListView
基本使用
ListView lv = findViewById(R.id.lv);
lv.setAdapter(new MyAdapter(this,strings));
public class MyAdapter extends BaseAdapter { //自定义类继承BaseAdpter
private Context context;
private List<String> strings;
public MyAdapter(Context ctx,List<String> strings){ //构造传入参数
context = ctx;
this.strings = strings;
}
@Override
public int getCount() { //返回条目数量
if (strings != null){
return strings.size();
}
return 0;
}
@Override
public Object getItem(int i) {//返回条目对消
return strings.get(i);
}
@Override
public long getItemId(int i) {//返回条目对应的id
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {//获取View
ViewHolder holder;
if (view == null){
//生成View
view = LayoutInflater.from(context).inflate(R.layout.list_item,viewGroup,false);
//view = inflater.inflate(R.layout.list_item,viewGroup);
holder = new ViewHolder();
holder.tv = view.findViewById(R.id.tv);//将条目中内容设置到holder中去
view.setTag(holder); //设置tag
}else {
holder = (ViewHolder) view.getTag();//获取tag
}
holder.tv.setText(strings.get(i));
return view;
}
private class ViewHolder{ //自定义VieWholder
TextView tv;
}
}
Android中的适配器
BaseAdapter:最基本适配器,用于扩展
ArrayAdapter:最为简单,只能展示一列的数据形式
SimpleAdapter/SimpleCursorAdapter:对数据库的简单结合,易于展示数据库中内容
CursorAdapter
HeaderViewListAdapter
ResourceCursorAdapter
WrapperListAdapter
BaseAdapter implements ListAdapter ,SpinnerAdapter
ArrayAdapter
SimpleAdapter
CursorAdapter
**SimpleCursorAdapter
ArrayAdapter
lv = (ListView) findViewById(R.id.lv);
String[] objects = new String[]{"111","222","333","444","555","666"};
lv.setAdapter(new ArrayAdapter<String>(this, android.R.layout.list_item, objects));
//Objects:要显示的数据,数组或者List集合都行
SimpleAdapter
lv = (ListView) findViewById(R.id.lv);
List<Map<String, Object>> data = new ArrayList<Map<String,Object>>(); //准备数据
Map<String, Object> map1 = new HashMap<String, Object>();
map1.put("icon", R.drawable.ic_menu_preferences);
map1.put("name", "功能设置");
data.add(map1);
Map<String, Object> map2 = new HashMap<String, Object>();
map2.put("icon", R.drawable.ic_menu_recent_history);
map2.put("name", "时钟设置");
data.add(map2);
lv.setAdapter(new SimpleAdapter(this, data, R.layout.item, new String[]{"icon","name"}, new int[]{R.id.iv,R.id.tv}));
//new String[]{"icon","name"}:Map集合key的数组
//new int[]{R.id.iv,R.id.tv}:item布局对应key的控件id
动态设置listview的高度
应用:动态插入数据时,ListView扔使用插入前的高度,导致数据显示不完整;
解决ScrollView和ListView的冲突
public void setListViewHeightBasedOnChildren(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));
((MarginLayoutParams) params).setMargins(10, 10, 10, 10);//设置margin
listView.setLayoutParams(params);
}
动态设置GridView的高度
public void setListViewHeightBasedOnChildren(GridView gridView) {
// 获取gridView的adapter
ListAdapter listAdapter = gridView.getAdapter();
if (listAdapter == null) {
return;
}
// 固定列宽,有多少列
int col = 4;// gridView.getNumColumns();
int totalHeight = 0;
for (int i = 0; i < listAdapter.getCount(); i += col) {
// 获取listview的每一个item
View listItem = listAdapter.getView(i, null, gridView);
listItem.measure(
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
// 获取item的高度和
totalHeight += listItem.getMeasuredHeight();
}
// 获取listview的布局参数
ViewGroup.LayoutParams params = gridView.getLayoutParams();
// 设置高度
params.height = totalHeight;
// 设置margin
((ViewGroup.MarginLayoutParams) params).setMargins(10, 10, 10, 10);
// 设置参数
gridView.setLayoutParams(params);
}
优化
1、getView()对ConvertView复用
2、使用ViewHolder,不要在getView()中使用findViewById()。因getView()会执行多次,频繁使用findViewById影响性能及浪费内存
3、使用分页加载大量数据。新数据对旧数据进行覆盖
4、图片优化。使用具有良好的缓存机制的三方库加载
5、手指滑动,或列表处于滑动状态,尽可能不加载图片,保证滑动流畅性
recycleBin机制
//需要优化
在某一时刻,我们看到ListView中有许多View呈现在UI上,这些View对我们来说是可见的,这些可见的View可以称作OnScreen的View,即在屏幕中能看到的View,也可以叫做ActiveView,因为它们是在UI上可操作的。
当触摸ListView并向上滑动时,ListView上部的一些OnScreen的View位置上移,并移除了ListView的屏幕范围,此时这些OnScreen的View就变得不可见了,不可见的View叫做OffScreen的View,即这些View已经不在屏幕可见范围内了,也可以叫做ScrapView,Scrap表示废弃的意思,ScrapView的意思是这些OffScreen的View不再处于可以交互的Active状态了。ListView会把那些ScrapView(即OffScreen的View)删除,这样就不用绘制这些本来就不可见的View了,同时,ListView会把这些删除的ScrapView放入到RecycleBin中存起来,就像把暂时无用的资源放到回收站一样。
当ListView的底部需要显示新的View的时候,会从RecycleBin中取出一个ScrapView,将其作为convertView参数传递给Adapter的getView方法,从而达到View复用的目的,这样就不必在Adapter的getView方法中执行LayoutInflater.inflate()方法了。
RecycleBin中有两个重要的View数组,分别是mActiveViews和mScrapViews。这两个数组中所存储的View都是用来复用的,只不过mActiveViews中存储的是OnScreen的View,这些View很有可能被直接复用;而mScrapViews中存储的是OffScreen的View,这些View主要是用来间接复用的。
源码分析
待更新
问题
条目中有按钮,条目点击事件失效
解决方法:ListView的条目(list_item)根布局的设置属性:
android:descendantFocusability="blocksDescendants"
ListView异步加载图片错位原因与解决方法
原因:异步加载图片未完成,listview重用之前的item,导致图片错位
解决方法:对ImageView设置tag为图片URL,并设置默认图片。等异步加载完成,需要比较ImageView的tag与请求的Url是否匹配
刷新问题,与RecycleView对比
ListView调用Adapter.notifyDataSetChanged()会重绘每个item
RecyclerView.Adapter提供notifyItemChanged(int postion)刷新单个item
解决ScrollView嵌套ListView和GridView冲突
//重写ListView的onMeasure方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
考察对MeasureSpec的三种模式的理解
https://blog.csdn.net/btt2013/article/details/53447649