Android自学——ListView
ListView
简介:ListView允许用户通过手指上下滑动的方式将屏幕外的数据滚动到屏幕内,例如QQ的聊天记录等。所谓ListView也就是把具有相同布局的View给放在一个列表中展示而已。
使用ListView需要设置数据适配器Adapter,那什么是Adapter?
Adapter
An Adapter object acts as a bridge between an AdapterView and the underlying data for that view. The Adapter provides access to the data items. The Adapter is also responsible for making a View for each item in the data set.
一个Adapter是AdapterView视图与数据之间的桥梁,Adapter提供对数据的访问,也负责为每一项数据产生一个对应的View,后将View添加到ListView之中。
可能直接这样说比较抽象,下面我们来举例说明一下:我们的手机通讯录就是一个简单的ListView。通讯录中有一条一条的联系人,每一个联系人都显示了相应的信息。现在通讯录比较简单,一般情况下只显示头像和姓名,以前还会显示手机号。每一条联系人的显示方式又是一个View。Adapter适配器将我们的信息(头像和姓名)根据一定的格式适配到每个View中,然后再将View添加到ListView中。大家根据这个例子可能对ListView控件和Adapter适配器以及他们之间的关系有了更好的理解了。
引用自http://www.cnblogs.com/gaobig/p/5016651.html
Adapter的构造函数有三个参数,分别代表当前上下文
、ListView子项布局的id
和要适配的数据
。
Android提供了很多适配器的实现类,我们这里先看看ArrayAdapter,其通过泛型来指定要适配的数据,比如我们要传入文本类型的数据:
private String[] data = {"a", "b", "c", "d", "e", "f", "g"};@Overrideprotected void onCreate(Bindle savedInstanceState) { super.onCreate(saveInstanceState); setContentView(R.id.activity_main); //生成适配器实例,其中android.R.layout.simple_list_item_1是内置的简单布局,只含有一个TextView ArrayAdapter adapter = new ArrayAdapter(MainActivity.this, android.R.layout.simple_list_item_1, data); //生成ListView实例并载入适配器, //其中R.id.lsit_view是自定义的ListView控件,该控件的用法和普通控件一样,这里就不写布局的代码了 ListView listView = (ListView) findViewById(R.id.lsit_view); listView.setAdapter(adapter);}
Adapter中有一个getView()
方法,每当子项被滚动到屏幕内的时候就会被ListView调用,用于干什么呢?当然是给当前的ListView的子项填进一个View,所以理所当然该方法返回的是View类型,因此如果要自定义ListView的子项布局的话,就可以自己新建一个View然后再用自定义的布局的控件跟View绑定到一起。
那怎么绑定呢?我们看一下下面的例子:
@Overridepublic View getView(int position, View convertView, ViewGroup parent) { View view = LayoutInflater.from(getContext()).inflate(自定义子项布局的id, parent, false); //对View中的控件进行设置,如: TextView text = (TextView) view.findViewById(R.id.text); text.setText("Hello"); return view;}
这里有必要再解释一下LayoutInflater.from(getContext()).inflate()
。
LayoutInflater.from(getContext())
可以构造出一个LayoutInflater对象,然后调用inflate()
方法动态加载一个布局。inflate()
方法有三个参数,,第一个参数是要填充的布局的id,第二个是父布局,第三个是是否把布局先添加到父布局再返回View。其中第三个参数省略的话默认为true:
inflate()
方法第三个参数attachToRoot是true的话,那第一个参数的layout文件就会被填充并附加在第二个参数所指定的ViewGroup内。方法返回结合后的View,根元素是第二个参数ViewGroup。如果是false的话,第一个参数所指定的layout文件会被填充并作为View返回。这个View的根元素就是layout文件的根元素。不管是true还是false,都需要ViewGroup的LayoutParams来正确的测量与放置layout文件所产生的View对象。作者:斯科特安链接:http://www.jianshu.com/p/41796f541e67來源:简书 著作权归作者所有。
反正就是这样来给View加载布局的。
然后就是要说说怎么自定义Adapter了,比如我们要在ListView中展示水果图片+名字的一组列表:
/*在定义Adapter之前先定义一个我们要展示的数据的类*/public class Fruit { private String name; private int imageId; public Fruit(String name, int imagId) { this.name = name; this.imageId = imageId; } //此处省略两个成员变量的getter和setter方法 ...}
/*一般自定义Adapter可以选择继承ArrayAdapter,可以省下很多功夫*/public class FruitAdapter extends ArrayAdapter<Fruit> { //resourceId是我们自定义的布局的id,在后面的getView()方法中需要用来给View加载布局 private int resourceId; //第二个参数之所以叫textViewResourceId是因为我们继承的ArrayAdapter就是只用来展示TextView的 //我们自定义的时候就可以传入我们自定义的布局id public FruitAdapter(Context context, int textViewResourceId, List objects) { super(context, textViewResourceId, objects); resourceId = textViewResourceId; } @Override public View getView(int position, View convertView, ViewGroup parent) { View view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false); //对View中的控件进行设置 TextView fruitName = (TextView) view.findViewById(R.id.name); ImagView fruitImage = (ImageView) view.findViewById(R.id.image) //getItem()方法是继承的ArrayAdapter中实现的方法,用于获取当前项的数据的实例 Fruit fruit = getItem(position); fruitName.setText(fruit.getName()); fruitImage.setImageResource(fruit.getImageId); return view; }}
自定义Adapter就是这样子了,应该没什么问题吧, 使用的话就跟ArrayAdapter的用法类似就可以了。
下面来学习怎么优化ListView的性能。
首先由于每次子项进入怕屏幕的时候都会调用getView()
方法,这样会导致每次都重新加载布局,当ListView快速滚动的时候性能就很差。
仔细看Adapter的getView()
方法,还会发现它的第二个参数convertView我们没有用到,这个参数就是用来缓存之前加载好的布局的。所以我们修改一下上面例子中getView()
的内容:
@Override public View getView(int position, View convertView, ViewGroup parent) { View view; if (convertView == null) { view = = LayoutInflater.from(getContext()).inflate(resourceId, parent, false); } else { view = convertView; } ... }}
其次由于getView()
方法中每次都会调用findViewById()
来获取控件实例,因此我们可以借助一个ViewHolder来进行优化:
@Override public View getView(int position, View convertView, ViewGroup parent) { Fruit fruit = getItem(position); View view; ViewHolder viewHolder; if (convertView == null) { view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false); viewHolder = new ViewHolder(); viewHolder.image = (ImageView) view.findViewById(R.id.iamge); viewHolder.name = (TextView) view.findViewById(R.id.name); //调用View的setTag()方法,该方法可以存储和View相关联的信息,然后调用View的getTag()可以读取信息 view.setTag(viewHolder); } else { view = convertView; viewHolder = (ViewHolder) view.getTag(); } viewHolder.name.setText(fruit.getName()); viewHolder.image.setImageResource(fruit.getImageId()); return view; }}class ViewHolder { ImageView image; TextView name;}
至此ListView的性能就很不错了。
ListView的每一个子项是可以点击的,下面学习一下如何注册点击事件。
ListView listView = (ListView) findViewById(R.id.list_View);listView.setAdapter(adapter);lsitView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Fruit fruit = fruitList.get(position); Toast.makeText(MainActivity.this, fruit.getName(), Toast.LENGTH_SHORT).show(); }});
其实跟Button和TextView等的监听器差不多,过一下就可以了。
ListView的学习就到这里了,初次写博客,有啥错误欢迎批评指出。
主要参考文献:郭霖《第一行代码(第2版)》
更多相关文章
- Android(安卓)Kotlin项目集成阿里ARouter
- Android:保存图片到Sqlite数据库
- Android官方提供的支持不同屏幕大小的全部方法
- 关于 Android(安卓)下的自动化测试方法介绍
- shareSdk错误码对照表
- Android(安卓)Retrofit + RxJava使用详解
- Android(安卓)之 下拉(Spinner) 组件示例
- 《Android》Lesson11-UI布局
- Android解决UnsupportedOperationException异常!