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

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

----------------------------------------------ListView简易用法------------------------------------------------------

创建一个ListViewTest项目,让Android Studio自动创建好活动。

然后修改activity_main.xml中的代码,如下:

<?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"> <ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"> </ListView> </LinearLayout>

引入了ListView后,即使你的ListView没有内容,可视化编辑器preview仍然会这样显示:

接下来修改MainActivity中的代码,如下所示:

public class MainActivity extends AppCompatActivity {

    private String[] data = {"肖申克的救赎", "这个杀手不太冷", "霸王别姬", "泰坦尼克号", "瓦力",
"三傻大闹宝莱坞", "放牛班的春天", "千与千寻", "鬼子来了", "星际穿越"}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
MainActivity.this, android.R.layout.simple_list_item_1, data
);
ListView listView = (ListView) findViewById(R.id.list_view);
listView.setAdapter(adapter);
}
}

直接在MainActivity中定义字符串数组虽然可以达到目的,但是不便于管理。

可以在res目录下的values文件夹下新建一个array.xml文件,内容如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="moive">
<item>肖申克的救赎</item>
<item>这个杀手不太冷</item>
<item>霸王别姬</item>
<item>泰坦尼克号</item>
<item>瓦力</item>
<item>三傻大闹宝莱坞</item>
<item>放牛班的春天</item>
<item>千与千寻</item>
<item>鬼子来了</item>
<item>星际穿越</item>
</string-array>
</resources>

通过Resources对象获得array.xml的内容:

        Resources resourse = this.getResources();
String[] data = resourse.getStringArray(R.array.moive);

最终结果如下:

-----------------------------------------------------适配器的介绍------------------------------------------------------

既然ListView是用于展示大量数据的,这些数据可以是从网上下载的,也可以是从数据库中读取的,应该视具体的应用场景来决定。

数组中的数据是无法直接传递给ListView的,还需要借助适配器来完成。

Android中提供了很多的适配器的实现类,其中最简单的就是ArrayAdapter(注意不是SimpleAdapter)。

它可以通过泛型来指定要适配的数据类型,然后在构造函数中把要适配的数据传入即可。

ArrayAdapter有多个构造函数的重载,根据需要选择最合适的一种。这里我简单介绍其中的一种:

new ArrayAdapter<>( Context context , @LayoutRes int resource , String[] objects );

Context:context表示上下文对象,参数:MainActivity.this

@LayoutRes:resource表示ListView子项布局的id,参数:android.R.layout.simple_list_item_1

(android.R.layout.simple_list_item_1这是Android内置的布局文件,里面只有一个TextView,可用于简单显示一段文本)

List<String> | String[]:objects中指定要适配的数据。

适配器构建好之后,还需要调用ListView的setAdapter()方法,将构建好的适配器对象传递进去,这样ListView和数据之间的关联就建立完成了。

--------------------------------------------定制ListView------------------------------------------------------

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

效果预览:

素材:

首先定义一个实体类,作为ListView适配器的适配类型。新建类Browser,代码如下:

public class Browser {
private String name;
private int icon; public Browser(String name, int icon) {
this.name = name;
this.icon = icon;
} public String getName() {
return name;
} public int getIcon() {
return icon;
}
}

为了尽量简单,Browser类中只有两个字段,name表示浏览器的名字,icon对应浏览器的图标。

然后需要为ListView的子项指定一个自定义的布局,在layout目录下新建browser_item.xml,代码如下:

<?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="wrap_content"> <ImageView
android:id="@+id/browser_icon"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginLeft="20dp" /> <TextView
android:id="@+id/browser_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="120dp"
android:textSize="24dp" /> </LinearLayout>

在这个布局中,我们定义了一个ImageView用于显示浏览器图标,又定义了一个TextView用于显示浏览器的名称。

为了简洁,我在ImageView和TextView没有引入内容。其实引入内容并不会对结果造成影响,而且可以在preview中预览,以便调整布局。

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

新建类BrowserAdapter,代码如下:

//自定义适配器,继承自ArrayAdapter
public class BrowserAdapter extends ArrayAdapter<Browser>{
//resourceID指定ListView的布局方式
private int resourceID;
//重写BrowserAdapter的构造器
public BrowserAdapter(Context context,int textViewResourceID , List<Browser> objects){
super(context,textViewResourceID,objects);
resourceID = textViewResourceID;
}
//自定义item资源的解析方式
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//获取当前Browser实例
Browser browser = getItem(position);
//使用LayoutInfater为子项加载传入的布局
View view = LayoutInflater.from(getContext()).inflate(resourceID,null);
ImageView browserIcon = (ImageView)view.findViewById(R.id.browser_icon);
TextView browserName = (TextView)view.findViewById(R.id.browser_name);
//引入Browser对象的属性值
browserIcon.setImageResource(browser.getIcon());
browserName.setText(browser.getName());
return view;
}
}

BrowserAdapter重写了父类的一组构造函数,用于将上下文、ListView子项布局的id和数据都传递进来。另外又重写了getView()方法,这个方法在每个子项被滚动到屏幕内的时候会被调用。

在getView方法中,首先通过getItem()方法得到当前项的Browser实例,然后使用LayoutInflater来为这个子项加载我们传入的布局,接着调用View的findViewById()方法分别获取到ImageView和TextView的实例,并分别调用它们的setImageResource()和setText()方法来设置显示的图片和文字,最后将布局返回,自定义适配器就完成了。

下面修改MainActivity中的代码,如下所示:

public class MainActivity extends AppCompatActivity {

    //Browser实体集合
private List<Browser> browsers = new ArrayList<>(); @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); //初始化数据
Browser ie = new Browser("IE", R.drawable.ie);
Browser chrome = new Browser("Chrome", R.drawable.chrome);
Browser firefox = new Browser("Firefox", R.drawable.firefox);
Browser saferi = new Browser("Saferi", R.drawable.safari); browsers.add(ie);
browsers.add(chrome);
browsers.add(firefox);
browsers.add(saferi); //初始化适配器
BrowserAdapter adapter = new BrowserAdapter(MainActivity.this, R.layout.browser_item, browsers);
ListView listView = (ListView) findViewById(R.id.list_view);
listView.setAdapter(adapter); }
}

虽然目前我们定制的界面还是很简单,但是如果你领悟到了诀窍,只要修改browser_item.xml中的内容,就可以定制出各种复杂的界面了。

--------------------------------------------提升ListView的运行效率----------------------------------------------

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

目前我们的ListView运行效率是很低的,因为在BrowserAdapter的getView()方法中每次都将布局重新加载了一遍,当ListView快速滚动的时候这就会成为性能的瓶颈。

getView()中还有一个convertView参数,这个参数用于将之前加载好的布局进行缓存,以便之后可以进行重用。

修改BrowserAdapter中的setView()代码,如下所示:

    public View getView(int position, View convertView, ViewGroup parent) {
Browser browser = getItem(position);
View view;
//convertView为空则加载布局,不为空则重用
if(convertView == null){
view = LayoutInflater.from(getContext()).inflate(resourceID,null);
}else{
view =
convertView;
}

ImageView browserIcon = (ImageView)view.findViewById(R.id.browser_icon);
TextView browserName = (TextView)view.findViewById(R.id.browser_name);
browserIcon.setImageResource(browser.getIcon());
browserName.setText(browser.getName());
return view; }

现在我们在getView()方法中进行了判断,如果convertView为空,则使用LayoutInflater去加载布局,如果不为空则直接对convertView进行重用。这样就大大提高了ListView的运行效率,在快速滚动的时候可以表现出更好的性能。

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

我们可以借助一个ViewHolder内部类来对这部分性能进行优化,修改BrowserAdapter中的setView()方法,如下所示:

//自定义适配器,继承自ArrayAdapter
public class BrowserAdapter extends ArrayAdapter<Browser>{
//resourceID指定ListView的布局方式
private int resourceID;
//重写BrowserAdapter的构造器
public BrowserAdapter(Context context,int textViewResourceID , List<Browser> objects){
super(context,textViewResourceID,objects);
resourceID = textViewResourceID;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Browser browser = getItem(position);
View view;
ViewHolder viewHolder;
if(convertView == null){
view = LayoutInflater.from(getContext()).inflate(resourceID,null);
viewHolder = new ViewHolder();
viewHolder.browserIcon = (ImageView)view.findViewById(R.id.browser_icon);
viewHolder.browserName = (TextView)view.findViewById(R.id.browser_name);
//将ViewHolder存储在View中
view.setTag(viewHolder);
}else {
view = convertView;
viewHolder = (ViewHolder)view.getTag();
}
viewHolder.browserIcon.setImageResource(browser.getIcon());
viewHolder.browserName.setText(browser.getName());
return view;
} class ViewHolder{
ImageView browserIcon;
TextView browserName;
}
}

我们新增了一个内部类ViewHolder,用于对控件的实例进行缓存。当convertView为空的时候,创建一个ViewHolder对象,并将控件的实例都存放在ViewHolder里,然后调用View的setTag()方法,将ViewHolder对象存储在View中。当convertView不为空的时候调用View的getTag()方法,把ViewHolder重新取出。这样所有的控件的实例都缓存在了ViewHolder里,就没有必要每次都通过findViewById()方法来获取控件实例了。

经过这两步的优化之后,ListView的运行效率完全可以满足我们的需要了。

----------------------------------------------ListView的点击事件-----------------------------------------------------

使用setOnItemClickListener()方法来为ListView注册一个监听器,当用户点击了ListView中的任何一个子项时都会回调onItemClick()方法,在这个方法中可以通过position参数判断出用户点击的是哪一个子项,然后执行相应的程序。

        BrowserAdapter adapter = new BrowserAdapter(MainActivity.this, R.layout.browser_item, browsers);
ListView listView = (ListView) findViewById(R.id.list_view);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Browser browser = browsers.get(position);
Toast.makeText(MainActivity.this,browser.getName().toString(),Toast.LENGTH_SHORT).show();
}
});
listView.setAdapter(adapter);

Android列表控件ListView详解的更多相关文章

  1. 【Android】12.0 UI开发(三)——列表控件ListView的简单实现2

    1.0 由于书上内容,已经和实际编程的兼容性已经不太友好,重写了项目,用于进一步学习列表控件ListView. 2.0 新建项目ListViewTest,其中文件目录如下: 3.0 ActivityC ...

  2. WebBrowser控件使用详解

    原文:WebBrowser控件使用详解 方法 说明 GoBack 相当于IE的“后退”按钮,使你在当前历史列表中后退一项 GoForward 相当于IE的“前进”按钮,使你在当前历史列表中前进一项 G ...

  3. 串口通信-MSComm控件使用详解

    串口通信-MSComm控件使用详解 2012年11月13日 09:35:45 他山之石可以攻玉 阅读数:37952更多 个人分类: 控件编程Delphi编程   MSComm 控件通过串行端口传输和接 ...

  4. Github上star数超1000的Android列表控件

    Android开发中,列表估计是最最常使用到的控件之一了.列表相关的交互如下拉刷新,上拉更多,滑动菜单,拖动排序,滑动菜单,sticky header分组,FAB等等都是十分常见的体验.Github中 ...

  5. Flash播放控件属性详解

    Flash 播放控件属性详解 一.属性篇 1.AlignMode(读写)  语法:AlignMode As Long  说明:对齐方式(与SAlign 属性联动).当控件的长宽比例与影片不一致且WMo ...

  6. Android基础控件ListView基础操作

    1.简介 基于Android基础控件ListView和自定义BaseAdapter适配器情况下,对ListView的数据删除和添加操作: public boolean add(E e) {//添加数据 ...

  7. Android UI组件----AppWidget控件入门详解

    Widget引入 我们可以把Widget理解成放置在桌面上的小组件(挂件),有了Widget,我们可以很方便地直接在桌面上进行各种操作,例如播放音乐. 当我们长按桌面时,可以看到Widget选项,如下 ...

  8. Android开发之基本控件和详解四种布局方式

    Android中的控件的使用方式和iOS中控件的使用方式基本相同,都是事件驱动.给控件添加事件也有接口回调和委托代理的方式.今天这篇博客就总结一下Android中常用的基本控件以及布局方式.说到布局方 ...

  9. 控件CListCtr详解

    1.CListCtrl控件 CListCtrl控件在数据库编程中是用得比较多的控件之一,也是Window控件中较难掌握的一个控件.他可以有四显示方式,Report.List.Icon.SmallIco ...

随机推荐

  1. vim 快捷键 以及技巧

    [root@centos01 biji]# vim + 1.txt 打开文件,光标定位到最后一行[root@centos01 biji]# vim +5 1.txt 打开文件,光标定位到第5行[roo ...

  2. Java递归算法——阶乘

    import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.i ...

  3. Windows 无法自动将 IP 协议堆栈绑定到网络适配器。解

    Windows 无法自动将 IP 协议堆栈绑定到网络适配器.解  昨天断网了,所以把珍藏已久的无线网卡拿出来蹭网.我系统是Windows 7 但是装上去东显示已启用,就是用不了,用windows诊断是 ...

  4. 图片延迟加载jquery插件imgLazyLoad(三)

    此Jquery插件是在图片加载前显示一个加载图片,当图片下载完毕后显示图片出来,可对图片进行是否自动缩放功能,此Jquery插件使用时可让页面先加载,而图片后加载的方式,解决了平时使用时要在图片显示出 ...

  5. Request.GetOwinContext()打不到

    Although it's in the Microsoft.Owin.Host.SystemWeb assembly it is an extension method in the System. ...

  6. Effective Objective-C 2.0 — 第8条:理解“对象等同性”这一概念

    第8条:理解“对象等同性”这一概念 若想检测对象的等同性,请提供“isEqual”与 hash 方法 相同的对象必须具有相同哈希码,但是两个哈希码相同的对象却未必相同. 不要盲目地逐个检测每条属性,而 ...

  7. WebGrid Helper with Check All Checkboxes

    WebGrid Helper with Check All Checkboxes myEvernote Link Tuesday, September 13, 2011ASP.NET ASP.NET ...

  8. 您的应用静态链接到的 OpenSSL 版本有多个安全漏洞。建议您尽快更新 OpenSSL

    安全提醒 您的应用静态链接到的 OpenSSL 版本有多个安全漏洞.建议您尽快更新 OpenSSL. 在开头为 1.0.1h.1.0.0m和 0.9.8za的 OpenSSL 版本中这些漏洞已得到修复 ...

  9. mouse scrollings and zooming operations in linux & windows are opposite

    mouse scrollings and zooming operations in linux & windows are opposite. windows中, 鼠标滚动的方向是: 查看页 ...

  10. linux shell中判断bash脚本输入的参数个数

    看下面的一段程序. #!/bin/bash ]; then echo "参数个数为$#个" else echo "没有参数" fi