Android控件系列——ListView

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
发布了77 篇原创文章 · 获赞 75 · 访问量 34万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览