ListView

•前言

  ListView 绝对可以称得上是 Android 中最常用的控件之一,几乎所有的应用程序都会用到它。

  由于手机屏幕空间有限,能够一次性在屏幕上显示的内容并不多,当我们的程序中有大量的数据需要展示的时候,就可以借助 ListView 来实现。

  ListView 允许用户通过手指上下滑动的方式将屏幕外的数据滚动到屏幕内,同时屏幕上原有的数据则会滚动出屏幕。

  其实你每天都在使用这个控件,比如查看 QQ聊天记录,翻阅微博消息,等等。

•ListView简介

  ListView 的直接父类是 View.Group,也就是说,他自己定义了子排列 View 的规则。

  ListView 和所要展示的内容(数据源)之间需要 Adapter(适配器) 来实现。

  Adapter 是一个桥梁,对 ListView 的数据进行管理。

  数据来源不同,所使用的 Adapter 也不同,数据源(Data source)、Adapter和列表(ListView)之间的关系如下图所示:

    
    

•ListView相关属性

  • android:dividerHeight="2dp" : 设置分割线高度
  • android:divider="@color/red" : 设置分割条,可以用颜色分割,也可以用 drawable 资源分割
  • android:entries="@array/myarray" : 设置 ListView 显示的内容

•ListView的简单用法

  在 res/values 下创建一个 arrays.xml 文件,添加代码如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="myarray">
<item>关羽</item>
<item>孙尚香</item>
<item>娜可露露</item>
</string-array>
</resources>

  新建一个 Activity,添加代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp"> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Test Array Adapter"
android:textSize="20sp"
/> <!-- 为 ListView 设置红色的分割线
并将分割线宽度设置为 2dp -->
<ListView
android:id="@+id/lv_array_adapter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:divider="@color/red"
android:dividerHeight="2dp"
android:entries="@array/myarray"
/> </LinearLayout>

•运行效果

  

•Adapter简介

  Adapter 的继承关系如下图所示:

    

  Adapter 是一个接口,ListAdapter 继承了 Adapter,也是一个接口,并需要子类实现。

  BaseAdapter 实现了 ListAdapter,他是一个抽象类。

  SimpleAdapter 继承自 BaseAdapter,他是 Adapter 的一个实例对象。

  另外,还有 ArrayAdapter 和 SimpleCursorAdapter 也是 Adapter 的实例对象。

    • ArrayAdapter:支持泛型操作,最简单的一个Adapter,只能展现一行文字
    • SimpleAdapter:同样具有良好扩展性的一个Adapter,可以自定义多种效果
    • BaseAdapter:抽象类,实际开发中我们会继承这个类并且重写相关方法,用得最多的一个Adapter
    • SimpleCursorAdapter:用于显示简单文本类型的listView,一般在数据库那里会用到,不过有点过时, 不推荐使用

ArrayAdapter

•示例一

  在 res/layout 新建 activity_array_adapter.xml 文件,添加代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp"> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Test Array Adapter"
android:textSize="20sp"
/> <!-- 为 ListView 设置红色的分割线
并将分割线宽度设置为 2dp -->
<ListView
android:id="@+id/lv_array_adapter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:divider="@color/red"
android:dividerHeight="2dp"
/> </LinearLayout>

  新建 ArrayAdapterActivity.java 文件,添加代码如下:

public class ArrayAdapterActivity extends AppCompatActivity {

    private ListView listview;

    @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_array_adapter); String[] s = new String[]{"关羽", "孙尚香", "娜可露露"};
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, R.layout.array_adapter_item, s); listview = findViewById(R.id.lv_array_adapter);
listview.setAdapter(adapter);
} }

  在这段代码中,定义了一个字符串数组 s ,不过数组 s 中的数据是无法直接传递给 ListView 的;

  我们还需要借助适配器来完成(这里我们借助 ArrayAdapter 这个适配器);

  ArrayAdapter 可以通过泛型来指定要适配的数据类型,然后再构造函数中把要适配的数据传入;

  ArrayAdapter 有多个构造函数的重载,我们应该根据实际情况选择最合适的一种;

  这里由于我们提供的数据都是字符串,因此将 ArrayAdapter 的泛型指定为 String;

  然后在 ArrayAdapter 的构造函数中依次传入:

    • 当前上下文(this)
    • ListView子项布局的 id(R.layout.array_adapter_item)
    • 适配的数据(s)

  R.layout.array_adapter_item 布局代码如下:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/textView"
android:textSize="20sp"
android:textColor="@color/black"> </TextView>

  此布局用来设置显示的字体(关羽、孙尚香、娜可露露)风格。

•运行效果

  

•示例二

  只能显示一段文本的 ListView 实在是太单调了,我们现在就来对 ListView 的界面进行定制,让它可以显示更加丰富的内容。

  首先需要准备一组图片,分别对应上面提供的英雄:

              

          $guan\_yu.jpg$     $sun\_shang\_xiang.jpg$  $na\_ke\_lu\_lu.jpg$

  接着定义一个实体类,作为 ListView 适配器的适配类型。

  新建类 Person,代码如下:

public class Person {
private String name;//英雄名称
private int imgId;//对应图片id public Person(String name,int imgId){
this.name = name;
this.imgId = imgId;
} public String getName() {
return name;
} public int getImgId() {
return imgId;
}
}

  Person 类中只有两个字段,name 表示英雄名称,imgId 表示英雄对应图片的资源 id。

  然后需要为 ListView 的子项指定一个我们自定义的布局;

  在 layout 目录下新建 person_item,添加代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:padding="10dp"
android:layout_marginTop="20dp"> <ImageView
android:id="@+id/person_img"
android:layout_width="100dp"
android:layout_height="150dp"
android:scaleType="centerCrop"/> <TextView
android:id="@+id/person_name"
android:layout_width="match_parent"
android:layout_height="150dp"
android:layout_marginLeft="10dp"
android:gravity="center"
android:textSize="20sp"
android:textColor="@color/red"
/> </LinearLayout>

  在这个布局中,我们定义了一个 ImageView 用来显示图片,有定义了一个 TextView 用来显示名称。

  接下来需要创建一个自定义的适配器,这个适配器继承自 ArrayAdapter,并将泛型指定为 Person 类。

  新建 PersonAdapter 类,添加代码如下:

public class PersonAdapter extends ArrayAdapter<Person> {

    private Context context;
private int resource; public PersonAdapter(@NonNull Context context, int resource, @NonNull List<Person> objects) {
super(context, resource, objects);
this.context = context;
this.resource = resource;
} @NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
Person person = getItem(position);//获取当前项的 Person 实例 View view = LayoutInflater.from(context).inflate(resource, parent, false);
ImageView img = view.findViewById(R.id.person_img);
TextView name = view.findViewById(R.id.person_name); img.setImageResource(person.getImgId());
name.setText(person.getName()); return view;
}
}

  PersonAdapter 重写了父类的一组构造函数,用于将上下文、ListView 子项布局 id 和数据都传递进来。

  另外又重写了 getView() 方法,这个方法在每个子项被滚动到屏幕内的时候被调用。

  在 getView() 方法中,首先通过 getItem() 方法得到当前项的 Person 实例,然后使用 LayoutInflater 来为这个子项加载我们传入的布局。

  通过 LayoutInflater 的 from() 方法可以构建出一个 LayoutInflater 对象,然后调用 inflate() 方法动态加载一个布局文件。

  inflate() 方法接收三个参数:

    • 第一个参数是要加载的布局文件的 id(resource)
    • 第二个参数是给加载好的布局再添加一个父布局(parent)
    • 第三个参数指定成 false

  接下来调用 view 的 findViewByid() 方法分别获取到 ImageView 和 TextView 的实例。

  并分别调用他们的 setImageResource() 和 setText() 方法来设置现实的图片和文字。

  最后将布局返回,这样我们的适配器就完成了。

  最后修改 ArrayAdapterActivity.java 中的代码,如下所示:

public class ArrayAdapterActivity extends AppCompatActivity {

    private ListView listview;

    @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_array_adapter); PersonAdapter adapter = new PersonAdapter(ArrayAdapterActivity.this, R.layout.person_item, getData()); listview = findViewById(R.id.lv_array_adapter);
listview.setAdapter(adapter);
} private List<Person> getData() {
List<Person> list = new ArrayList<>(); Person guanYu = new Person("关羽", R.drawable.guan_yu);
list.add(guanYu); Person sunShangXiang = new Person("孙尚香", R.drawable.sun_shang_xiang);
list.add(sunShangXiang); Person naKeLL = new Person("娜可露露", R.drawable.na_ke_lu_lu);
list.add(naKeLL); return list;
}
}

  可以看到,这里添加了一个 getData() 方法,用于获取数据。

  接着在 onCreate() 方法中创建了 PersonAdapter 对象,并将 PersonAdapter 作为适配器传递个 ListView。

  这样定值 ListView 界面的任务就完成了。

•运行效果

  

•提升ListView 的运行效率

  之所以说 ListView 这个控件很难用,是因为它有很多细节可以优化,其中运行效率就是很重要的一点;

  目前我们的 ListView 运行效率是很低的,因为在 PersonAdapter 的 getView() 方法中,每次都将布局重新加载了一遍;

  当 ListView 快速滚动的时候,这就会成为性能的瓶颈;

  仔细观察你会发现,getView() 方法中有一个 convertView 参数;

  这个参数用于将之前加载好的布局进行缓存,以便之后可以重用。

  修改 PersonAdapter 中的 getView() 代码,如下所示:

public class PersonAdapter extends ArrayAdapter<Person> {

    ......

    @NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
Person person = getItem(position);//获取当前项的 Person 实例
View view;
if (convertView == null) {
view = LayoutInflater.from(context).inflate(resource, parent, false);
} else {
view = convertView;
} ImageView img = view.findViewById(R.id.person_img);
TextView name = view.findViewById(R.id.person_name); img.setImageResource(person.getImgId());
name.setText(person.getName()); return view;
}
}

  可以看到,现在我们在 getView() 方法中进行了判断,如果 convertView 为 null,则使用 LayoutInflater 去加载布局;

  如果不为空,这直接对 convertView 进行重用;

  这样就大大提高了 ListView 的运行效率,在快速滚动的时候也可以表现出更好的性能。

  不过,目前我们的这份代码还是可以继续优化的;

  虽然现在已经不会再重复去加载布局,但是每次在 getView() 方法中还是会调用 View 的 findViewById() 方法来获取一次控件的实例;

  我们可以借助 ViewHolder 来对这部分性能进行优化;

  修改 PersonAdapter 中的 getView() 代码,如下所示:

public class PersonAdapter extends ArrayAdapter<Person> {

    private Context context;
private int resource; public PersonAdapter(@NonNull Context context, int resource, @NonNull List<Person> objects) {
super(context, resource, objects);
this.context = context;
this.resource = resource;
} static class ViewHolder {
ImageView img;
TextView name;
} @NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
Person person = getItem(position);//获取当前项的 Person 实例
View view;
ViewHolder viewHolder;
if (convertView == null) {
view = LayoutInflater.from(context).inflate(resource, parent, false);
viewHolder = new ViewHolder();
viewHolder.img = view.findViewById(R.id.person_img);
viewHolder.name = view.findViewById(R.id.person_name);
view.setTag(viewHolder);//将 viewHolder 存储在 View 中
} else {
view = convertView;
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.img.setImageResource(person.getImgId());
viewHolder.name.setText(person.getName()); return view;
}
}

  我们新增了一个静态内部类 ViewHolder,用于对控件的实例进行缓存。

  当 convertView 为 null 的时候,创建一个 ViewHolder 对象,并将控件的实例都存放在 viewHolder 里;

  然后调用 view 的 setTag() 方法,将 viewHolder 对象存储在 view 中;

  当 convertView 不为 null 时,则调用 view.getTag() 方法,把 viewHolder 重新取出;

  这样所有的控件的实例都缓存在了 viewHolder 里,就没有必要每次都通过 findViewById() 方法来获取控件实例了。

  另外这个修饰 ViewHolder 的 static,关于是否定义成静态,跟里面的对象数目是没有关系的;

  加静态是为了在多个地方使用这个 viewHolder 的时候,类只需加载一次,如果只是使用了一次,加不加也无所谓!

                                          ——Berial(B神)原话~

•为 ListView 设置点击事件

  修改 ArrayAdapterActivity.java 中的代码,如下所示:

public class ArrayAdapterActivity extends AppCompatActivity {

    private List<Person> personList;
private ListView listview; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_array_adapter); personList = getData();
PersonAdapter adapter = new PersonAdapter(ArrayAdapterActivity.this, R.layout.person_item, personList); listview = findViewById(R.id.lv_array_adapter);
listview.setAdapter(adapter); listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Person person = personList.get(position);
Toast.makeText(ArrayAdapterActivity.this,person.getName()+"被点击了!",Toast.LENGTH_SHORT).show();
}
});
} private List<Person> getData() {
List<Person> list = new ArrayList<>(); Person guanYu = new Person("关羽", R.drawable.guan_yu);
list.add(guanYu); Person sunShangXiang = new Person("孙尚香", R.drawable.sun_shang_xiang);
list.add(sunShangXiang); Person naKeLL = new Person("娜可露露", R.drawable.na_ke_lu_lu);
list.add(naKeLL); return list;
}
}

  可以看到,我们使用 setOnItemClickListener() 方法为 ListView 注册了一个监听器;

  当用户点击了 ListView 中的任何一个子项时,就会调用 onItemClick() 方法;

  在这个方法中可以通过 position 参数判断出用户点击的是哪一个子项,然后获取到相应的 Person 实例;

  最后通过 Toast 将其显示出来;

•运行效果

  

Android Studio 通过 ListView 学习 ArrayAdapte的更多相关文章

  1. 【转】Android Studio安装配置学习教程指南 Gradle基础--不错

    原文网址:http://www.linuxidc.com/Linux/2015-02/113890p4.htm 其实很早之前也写了一篇Gradle的基础博客,但是时间很久了,现在Gradle已经更新了 ...

  2. 【转】Android Studio安装配置学习教程指南 下载和安装--不错

    背景 相信大家对Android Studio已经不陌生了,Android Studio是Google于2013 I/O大会针对Android开发推出的新的开发工具,目前很多开源项目都已经在采用,Goo ...

  3. Android Studio 之 BaseAdapter 学习笔记

    •前行必备--ListView的显示与缓存机制 我们知道 ListView.GridView 等控件可以展示大量的数据信息. 假如下图中的 ListView 可以展示 100 条信息,但是屏幕的尺寸是 ...

  4. Android Studio调试方法学习笔记

    (注:本人所用Android Studio的Keymap已设为Eclipse copy) 1.设置断点 只有设置断点,才好定位要调试什么地方,否则找不到要调试的地方,无法调试.(调试过程中也可以增加断 ...

  5. android studio中ListView与SQLite的结合使用

    Da.java public class Db extends SQLiteOpenHelper { public Db(Context context) { super(context, " ...

  6. Android Studio IDE 简单学习和介绍

    @import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...

  7. Android studio使用git-android学习之旅(79)

    首先我参考了hello_my_show和梦痕_sky的博客,表示感谢 android studio对于git的支持是很好的,这节课我们拉讲解怎么使用git可视化工具来clone project和提交修 ...

  8. Android Studio 之 ImageView 学习笔记

    •参考资料 [1]:菜鸟教程 [2]:bilibili视频教程 •src和blackground的区别 background通常指的都是背景,而src指的是内容 当使用 src 填入图片时,是按照图片 ...

  9. Android开发学习1----AndroidStudio的安装、创建第一个Android Studio文件、Android Studio界面介绍和HelloWord!

    移动开发的工具有很多:Android Studio,eclipse,Hbuilder等,其中,现如今最火的开发工具是Android Studio,Android Studio是谷歌自己推出的一款集成开 ...

随机推荐

  1. Android vs iOS vs Web

    Android vs iOS vs Web UI view Android ViewGroup ImageView TextView iOS UIView ImageView TextView Web ...

  2. Proxifier

    Proxifier 使用教程 https://www.proxifier.com/ Proxifier允许不支持通过代理服务器工作的网络应用程序通过SOCKS或HTTPS代理和链进行操作. confi ...

  3. OLAP

    OLAP Online Analytical Processing https://en.wikipedia.org/wiki/Online_analytical_processing 在线分析处理 ...

  4. SVG 场馆图

    SVG 场馆图 https://www.infoq.cn/article/1BVg9VDSmqyHv3W3TeNH https://mp.weixin.qq.com/s/aNPAfJIHL14NFtL ...

  5. 「NGK每日快讯」12.17日NGK第44期官方快讯!

  6. 超详细Openstack核心组件——nova部署

    目录 OpenStack-nova组件部署 nova组件部署位置 计算节点Nova服务配置(CT配置) 计算节点配置Nova服务-c1节点配置 计算节点-c2(与c1相同)(除了IP地址) contr ...

  7. [转]什么是 C 和 C ++ 标准库?

    转载地址:https://www.cnblogs.com/findumars/p/9000371.html 简要介绍编写C/C ++应用程序的领域,标准库的作用以及它是如何在各种操作系统中实现的.我已 ...

  8. C++算法代码——卡片游戏

    题目来自:http://218.5.5.242:9018/JudgeOnline/problem.php?cid=1397&pid=2 题目描述 桌上有一叠牌,从第一张牌(即位于顶面的牌)开始 ...

  9. hadoop环境搭建:完全分布式

    目录 1.硬件配置 2.软件版本 3.准备工作 3.1.建立虚拟机,网络设置为桥接模式 3.2.更改主机名 3.3.绑定主机名和IP,建立各主机间的联系 3.4.关闭防火墙 3.5.配置宿主机host ...

  10. js中国标准时间转换成datetime格式

    var format = function (time, format) { var t = new Date(time); var tf = function (i) { return (i < ...