不知道各位初学的宝宝有没有特别头疼ListView,他的属性跟咋们前面接触的别的控件不太一样;他的设值也跟别的控件不太一样……等等让我们初学的宝宝们也是焦头烂额啊~这一篇我就给咋们理一下这个控件的使用思路和一些常见的属性以及她的优化。
好了直奔主题~先看她(可以把它当作一个女孩子,,,这样就不会太累了)的属性——xml操作。这边咋就先不涉及java的操作了。
基本属性:
跟着我一起做吧,先新建一个项目,把它的activity_main的父布局改成线性布局
然后删掉默认生成的TextView控件,添加上咋们要用的ListView控件,如图,我让她填充满父布局,这样我们看到的就是整个ListView了,我们做了什么改变也会更直观,后面学熟练了你就可以做你自己想要的界面和效果了~
一、entries属性
我们首先让他出来数据然后看看效果吧,不然大家也都急得慌,半天连ListView是什么样子都不知道。当然我们首先做的是静态数据展示啦~这就用到我们的这个属性了:entries;它可以加载我们自定义的布局(这个后面会说),我们这儿先用它来加载我们要显示的数据。首先准备数据,在我们存放字符资源的string.xml文件里面写入我们要显示的数据,如图:
然后在activity_main里面加入我们要用到的属性entries,并且引用我们的数据;这里引用我们准备好的数据我是这样写的android:entries="@array/list_itmes"
细心的宝宝可能已经发现了,我刚才写的数据放在了string.xml文件的string-array标签里面,这就说明我的资源是字符串数组,也就是好多个字符串,而我们这边引用的话就用了@array然后/我们的name,就是我们那边给我们的字符串数组给的名字。同理在安卓中所有的引用资源都可以用@来引用,语法是@文件夹/文件名,比如要引用一张mipmap下的图片a.jpg的话就可以这样写:@mipmap/a.jpg;这样就引用到我们的这个图片的资源了,嘿嘿,是不是特别简单~言归正传,
操作如图:
之后我们运行程序就可以看到我们引入数据的效果了;当然因为我准备的数据不够多,所以一下子全部显示完了,不然的话是可以滚动显示的哦~这就是ListView的用处了;
我把数据多复制了一遍,现在你可以看到左边多了一个滚动条,他是可以滑动的哦~上下摸着滑动的那种哦
至此咋们的数据显示也是完成了,而且第一个属性也会用了;数据显示出来咋们的脚步就可以快一点了~
来继续看下一个属性吧:
二、listSelector属性
这个属性就是当我们ListView中的数据条被选中时的颜色了,默认是灰色的哦,你可以设置你喜欢的颜色呢。为了直观展示,我也就不管美不美了~我直接设置成了红色
,这边我们设置颜色的时候可以用三种方法
1.直接写他的十六进制颜色值,什么色的都可以度度的到的,比如红色的值是#ff0000绿色的是#00ff00等;
2.是引用我们自己的颜色资源文件,其实也是在color.xml中自己定义好颜色,然后就跟前文讲的引用资源是一样的@color/name;
3.就是引用系统给我们设置好的啦~人家官方大大早就把常用的色设置好给我们存起来等我们用了,方法也跟前文差不多@一下安卓官方大大然后冒号color/你想要的颜色,具体写法为:@android:color/holo_red_light
这个是系统的红颜色了。
操作如图:我直接设置了红色,是调用咋们官方大大的
看效果,当显示数据的条条被选中之后就会显示红色了~
三、divider、dividerHeight和属性
这两个属性可以当作一个来记,因为他们本来就是一起用的,分开没什么好的效果;divider是设置每一个数据条之间数据分割线的颜色的属性,dividerHeight是设置每个数据条之间分割线的高度(宽度的属性),我设置了一个高10dp的蓝色分割线,,,,嗯~真的不要太丑了,不过没事,咋们学操作嘛,嘿嘿嘿,实际操作如图:
效果如图:为10dp的蓝色分割线
四、scrollbars设置滚动条的显示;可选值有水平(horizontal)、垂直(vertical)和none,当然如果不想然她显示滚动条设置成none就好了
五、fastScrollEnabled设置是否快速滚动,可选值有true/false,设成true滑起来简直不要太爽~
别的xml属性就先不做介绍了,这几个是我觉得会用的着和用的最多的几个属性了;接下来我们才要开始干正事了~前面说的是静态设置值的;即在string.xml里面把要用的值存起来,然后引用,但一般我们不会这样做,你想想啊,谁没事会把数据提前存起来然后重复显示呢?当然是动态的显示啦,可以随时更新数据的那种,一般我们的数据会从服务器上拉下来,然后再显示上去喽,这就比较符合逻辑啦,那我们就来学习 学习咋们动态的展示数据吧;其实就是在java代码里面操作了;
言归正传,我们的ListView要动态的设置数据的话就得用到适配器啦~而我们的数据是各种各样的,而且显示的样式啊什么的都没办法自己设置,这时候我们的适配器就可以起到作用了,把我们的各种各样的数据通过适配器就可以显示成我们自己想要的样式了;所以我们在学ListView动态显示数据前先看看适配器,我们变看适配器边做;这边我介绍三个适配器:
ArrayAdapter 数组适配器,最简单的一个Adapter,只能展现一行文字,支持泛型操作。
SimpleAdpter 简单的适配器,具有多种扩展性,可以自定义多种效果。
BaseAdapter 基本适配器的抽象类,实际开发中我们会继承它,用的最多的适配器了。
先来看最简单的吧:
ArrayAdapter 数组适配器
先给她设置上ID属性,然后切换到java文件上,实例化ListView控件,再给它设置适配器,用setAdapter方法;当然重新设置数据我们就得把旧数据删掉,删除我们引用了string—array的属性entries属性就好了;实际操作如图:
到java文件里面实例化控件,并且写上设置适配器的方法:如图:
可以看到我们的setAdapter方法需要传入一个Adapter适配器,而现在我们还没有,所以我们写一个适配器:ArrayAdapter;写法为:ArrayAdapter<String> adapter = new ArrayAdapter<String>();
它需要传入四个参数,第一个参数是上下文Contecxt;第二个参数是显示数据的布局,就是规定咋们的数据怎么显示的布局,第三个参数是数据具体往哪个控件上设,第四个就是我们要设置上去的数据了,具体看我写法:
我这边第一个参数的话就设置成了MainActivity.this这个上下文,第二个参数是显示数据的布局;我这边用的是系统自带的布局‘support_simple_spinner_dropdown_item’这个布局是一个自带一个TextView控件的布局,他就是一个TextView占一行的布局,而我们的ArrayAdapter适配器只能设置一行数据,用这个布局就够了,第三个参数就是support_simple_spinner_dropdown_item布局里面id为text1的textView控件了,也就是我们最终要往上去显示数据的控件,第四个参数data就是我自己写的数据字符串数组了,这些数据目前虽然也是静态的,但是它在java文件里面,动态的改起来也很方便。
最后再把我们做好的适配器放到刚才的setAdapter方法里面就好啦,然后我们看效果和之前是一模一样的啦~
哈~第一个适配器就做完了,效果也实现了 ,是不是特别简单呢,思路也是特别清晰的,嘿嘿,那就接着搞下一个喽:
SimpleAdapter 简单适配器
她的扩展性很好,可以自定义布局啦,往里面放Button、EditText、CheckBox、ImageView等等,直接开始操作吧!
先准备我们要用的数据:28个条目,28个标题,28个图片,然后我们通过适配器做成28条数据显示在咋们的ListView上;
如图准备数据、导入图片:
然后开始做咋们的SimpleAdapter适配器,写法还是:SimpleAdapter adapter2 = new SimpleAdapter();
,新建咋们要往上去显示数据的布局,布局特别简单,里面就三个控件,用于显示图标和咋们的数据;
然后完善SimpleAdapter适配器的参数,第一个参数是上下文,这没什么好解释的喽,第二个参数是显示数据的集合,就是放咋们所有数据的集合,我是先把每条数据通过循环都放到Map集合里面以键值对的形式,然后把Map集合添加到List集合里面,最后把这个List集合作为第二个参数传入适配器参数里面;第三个参数是数据显示样式的布局,就是前面咋们自己新建的my_list布局文件,第四个参数是数据从哪里来,第五个参数是第四个参数里面来的数据到哪里去,我们的数据肯定是从第二个参数List里面来了,怎么拿到她呢?很简单,就是通过我们放入数据时的key名称来拿到集合里面的数据,我的三个key分别为:icon、title、data;我只需要通过这三个key的集合就可以拿到数据了,然后往哪里去就是每个数据分别往哪里设置的意思了,我们刚才的my_list中有三个控件ImageView(id是icon)、两个TextView(id分别是title、data);我们当然是要把数据设置到这三个控件上去喽,所以啊,最后一个参数写上他们id的集合就好了,最后把这个适配器给ListView通过setAdapter方法设置上去就好了:
如图:
哎呀呀~终于搞完了,数据量还是有点大的哦,当然现在我们都做完就可以看到效果了,美美哒,嘿嘿嘿,,,上图:
至此你已经学会了ListView的基本操作和使用啦~接下来我们就要学习她的高级操作啦,更强大的BaseAdapter适配器,以及ListView的优化,赶紧开始吧:
BaseAdapter 基本适配器类
这个是正真开发的时候最常用的,也是最实用的。可以将任何复杂组合的数据和资源,以任何你想要的显示的效果展示给客户。
直接开始操作:首先新建一个适配器的类MyAdapter,然后让他继承自BaseAdapter,然后看到报错,如图,Alt+回车,选择第一个 ,实现她的四个方法:
之后再点ok就好啦,Studio就帮我们把基本的东西做好了;
里面她默认生成了四个方法:getCount、getItem、getItemId、getView;这四个方法分别是getCount获得显示多少条的方法,getView就是直接获得提前定义好的布局文件和控件,然后返回一个View显示出来,只要写好这两个方法就已经可以按任何我们自己的要求显示出来了,getItem和getItemId方法将会在调用ListView的响应方法的时候被调用到,这个后面专门说;第一步准备工作完成后就是第二步了,我们准备一个实体类用于处理数据的,新建java文件名叫Messag.java,并且给定数据适配属性:一个用于处理图片的icon,两个String,然后Alt+insert选择Getter and Setter生成他们的get和set方法;
然后我们需要一个保存数据的集合,我们写上她的构造方法,让她传一个数据集合进来,然后我们根据数据集合的长短确定显示的条目数量,让getCount反回集合的长度;操作如图:
然后准备数据,往MyAdapter中传入数据集合、上下文,操作如图:
数据的话我们还是复用的之前写好的数据,当然我们可以set别的数据进去,这就看我们需求了,我这边演示的话还是我们之前一直用的数据,当然布局也用之前写好的my_list布局;然后加载布局,实例化控件,解析集合数据,并且设置到相应的控件上去,最后返回View,就好了,操作如图:
好了,完事看效果吧,跟之前一模一样,,,这是因为咋们的数据和布局都没有变,只是换了个方法显示出来喽~嘿嘿嘿,而且这样写出来还不是太好,上下滑动的时候你会发现一卡一卡的,这就涉及到咋们后面性能优化的问题了;效果如图:
那到此为止,你就所有的关于ListView适配器设置数据的都会喽,接下来要做的当然是性能的优化了:
性能优化需要注意这么几点;第一,引入布局的时候尽量简化布局的结构关系,不要设置太复杂的结构关系会影响ListView的渲染,第二,在她的getView中尽量少使用逻辑;第三,别在滑动的时候加载图片;第四,使用ViewHolder,复用convertView,我们这边重点实现第四条;
现在回头看看我们刚才写的代码,直接在滑动的时候加载控件,而且每次滚动到那儿都要加载,这样大大的降低了性能,我们可以使用ViewHolder和复用convertView,来优化我们的程序;看操作:
我先把我之前写的都注释掉,然后再新建一个ViewHolder的内部类,把我们要用到的控件全部声明在这个类里面;
然后判断convertView是否为空,如果为空就实例化控件,否则直接加载,如图:
然后我们就可以用咋们的ViewHolder.声明的控件名拿到控件,进行设值了~如图,最后在将convertView返回就ok了:
这样的话ListView的优化我们也就做好了,是不是特别简单呢~现在看效果,比之前流畅了百倍不止呢,嘿嘿;
好了,至此为止,我们已经做完了ListView加载数据的各种操作,基本上就涵盖完了咋们这儿;嗯~但是还有一点问题喽,那就是交互啦,没有交互的软件是没有灵魂的嘛对不对,接下来就是我们最后的一点点啦::交互的实现;
好了赶紧来看看吧:首先我们刚才做的前两个适配器也是可以实现交互的,最后一个适配器做的那更能实现喽;我们把我们ListViewd的适配器先回去换成ArrayAdapter适配器;然后开始做交互:给ListView设置OnItemClickListener,这个是条目点击监听器,也就是说当我们ListView上的Item被点击之后就会执行这个监听器里面的方法;操作如图:
::小提示,写的时候里面的监听器直接new 大写的O就出来了,选择带{}的那个回车就好了
然后看里面四个参数,第一个是父布局,第二个是当前item的view实例,第三个是点击的item的位置,第几个;第四个是点击的item的id;这边我们就让他点击之后拿到上面的值就行了,还是利用咋们的Message类以及数据集合messageList,其余你们想要实现什么功能可以自己写;操作如图:
这样就写好了,来看一下效果吧:
看,点击之后值也拿到了,显示出来了,但是需要注意的是这个下表是从0开始的,所以我们点击的是第七条数据,她显示的是6,如果有需要的话加1就可以了~我这边再就没做,你们可以自己试试,改完美;
然后我们试试SimpleAdapter这样写交互行不行,以及BaseAdpter设置上之后这样写交互行不行,你们可以自己去试试,我这边就不贴了,答案是无论设置什么适配器都可以这样写点击事件。那么大家还记不记得刚开始地时候BaseAdapter有两个属性也是做交互用的,getItem、getItemID;接下来我们用BaseAdapter实现更加高级的交互操作,让item上每一个控件都可以点击;
首先当然是把适配器换成我们的MyAdapter了,如图
我直接做那个小图标的点击事件,别的都一样,你们自己试着做就行了,原理和方法一样。
直接上图:
我是先通过ViewHolder的对象来拿到icon控件的实例,然后设置她的点击事件,并且设置AlertDialog显示出来点击的是第几个,前面说过,因为我们的position是从0开始的,所以我这儿给她加个1,显示她的实际值。
最后贴上所有源码:
MainActivity.java
package com.hw.listviewtest;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MainActivity extends AppCompatActivity {
// 声明要用的listView控件
private ListView mListView;
String[] data = {"这是第一条数据","这是第二条数据,我很帅!","我也很可爱,这是第三条数据","我喜欢唱歌~这是第四条数据","这是第五条数据55555",
"这是第六条数据666666","这是第七7777777条数据","这是第八8888888条数据","这是第九9999999条数据","这是第十10101010101条数据",
"这是第十一条数据","这是第十二22222条数据","这是第十三条数据","这是第十四条数据","这是第一条数据",
"这是第二条数据,我很帅!","我也很可爱,这是第三条数据","我喜欢唱歌~这是第四条数据","这是第五条数据55555",
"这是第六条数据666666","这是第七7777777条数据","这是第八8888888条数据","这是第九9999999条数据","这是第十10101010101条数据",
"这是第十一条数据","这是第十二22222条数据","这是第十三条数据","这是第十四条数据"};
String[] title = {"第一","第二","第三","第四","第五","第六","第七0","第八","第九","第十",
"第十一","第十二","第十三","第十四","第十五","第十六","第十七", "第十八","第十九","第二十",
"第二十一","第二十二","第二十三","第二十四","第二十五","第二十六","第二十七","第二十八"};
int[] icon = {R.mipmap.icon_1,R.mipmap.icon_2,R.mipmap.icon_3,R.mipmap.icon_4,R.mipmap.icon_5,R.mipmap.icon_6,
R.mipmap.icon_7,R.mipmap.icon_8,R.mipmap.icon_9,R.mipmap.icon_10,R.mipmap.icon_11,
R.mipmap.icon_12,R.mipmap.icon_13,R.mipmap.icon_14,R.mipmap.icon_15,R.mipmap.icon_16,
R.mipmap.icon_17,R.mipmap.icon_18,R.mipmap.icon_19,R.mipmap.icon_20,R.mipmap.icon_21,
R.mipmap.icon_22,R.mipmap.icon_23,R.mipmap.icon_24,R.mipmap.icon_25,R.mipmap.icon_26,
R.mipmap.icon_27, R.mipmap.icon_28,};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 实例化
mListView = (ListView) findViewById(R.id.listView);
// 制造适配器
ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this,R.layout.support_simple_spinner_dropdown_item,android.R.id.text1,data);
// 准备数据集合
List<Map<String,Object>> itemList = new ArrayList<>();
for (int i=0;i<data.length;i++){
Map<String,Object> map = new HashMap<>();
map.put("icon",icon[i]);
map.put("title",title[i]);
map.put("data",data[i]);
itemList.add(map);
}
// 制造simple适配器
SimpleAdapter adapter2 = new SimpleAdapter(MainActivity.this,itemList,R.layout.my_list, new String[]{"icon","title","data"},new int[]{R.id.icon,R.id.title,R.id.data});
// 设置适配器
final List<Message> messageList = new ArrayList<>();
for(int a=0; a<data.length;a++){
Message message = new Message();
message.setIcon(icon[a]);
message.setTitle(title[a]);
message.setData(data[a]);
messageList.add(message);
}
mListView.setAdapter(new MyAdapter(messageList,MainActivity.this));
// mListView.setAdapter(adapter2);
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Message message = messageList.get(position);
String title = message.getTitle();
String data = message.getData();
Toast.makeText(MainActivity.this,"你点击了第"+position+"条,Title是:---"+title+",data是——"+data,Toast.LENGTH_SHORT).show();
}
});
}
}
activity_main.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"
tools:context=".MainActivity">
<ListView
android:id="@+id/listView"
android:entries="@array/list_itmes"
android:listSelector="@android:color/holo_red_light"
android:divider="@android:color/holo_blue_light"
android:dividerHeight="10dp"
android:fastScrollEnabled="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
</LinearLayout>
MyAdapter.java
package com.hw.listviewtest;
import android.app.AlertDialog;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
/**
* @name ListViewTest
* @class name:com.hw.listviewtest
* @anthor 户伟 QQ:1373577355
* @class describe
* @time 2019/12/9 10:37
* @chang change time
*/
public class MyAdapter extends BaseAdapter {
List<Message> list;
Context context;
public MyAdapter(List<Message> list,Context context) {
this.list = list;
this.context = context;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if (convertView == null){
// 判断如果convertView为空的话就实例化控件
viewHolder = new ViewHolder();
convertView = LinearLayout.inflate(context,R.layout.my_list,null);
viewHolder.icon = convertView.findViewById(R.id.icon);
viewHolder.title = convertView.findViewById(R.id.title);
viewHolder.data = convertView.findViewById(R.id.data);
convertView.setTag(viewHolder);
}else {
// 否则直接加载
viewHolder = (ViewHolder) convertView.getTag();
}
Message message = list.get(position);
viewHolder.icon.setImageResource(message.getIcon());
viewHolder.title.setText(message.getTitle());
viewHolder.data.setText(message.getData());
viewHolder.icon.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 这里面写icon的点击事件,点击之后干什么你自己写就行了
// 我这边让她点击之后弹出来一个提示框
new AlertDialog.Builder(context)
.setTitle("您点击了icon")
.setMessage("你点击了第"+(position+1)+"个ICON")
.setPositiveButton("好的",null)
.show();
}
});
加载布局文件
// View v = LinearLayout.inflate(context,R.layout.my_list,null);
实例化控件
// ImageView icon = v.findViewById(R.id.icon);
// TextView title = v.findViewById(R.id.title);
// TextView data = v.findViewById(R.id.data);
通过集合获得Message的对象
// Message message = list.get(position);
通过Message的对象给控件设置值
// icon.setImageResource(message.getIcon());
// title.setText(message.getTitle());
// data.setText(message.getData());
return convertView;
}
}
class ViewHolder{
ImageView icon;
TextView title,data;
}
Message.java
package com.hw.listviewtest;
/**
* @name ListViewTest
* @class name:com.hw.listviewtest
* @anthor 户伟 QQ:1373577355
* @class describe
* @time 2019/12/9 10:54
* @chang change time
*/
public class Message {
int icon;
String title,data;
public int getIcon() {
return icon;
}
public void setIcon(int icon) {
this.icon = icon;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
}
my_list.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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@mipmap/icon_1" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="TextView" />
<TextView
android:id="@+id/data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="TextView" />
</LinearLayout>
</LinearLayout>
string.xml
<resources>
<string name="app_name">ListViewTest</string>
<string-array name="list_itmes">
<item>这是第一条数据</item>
<item>这是第二条数据,我很帅!</item>
<item>我也很可爱,这是第三条数据</item>
<item>我喜欢唱歌~这是第四条数据</item>
<item>这是第五条数据55555</item>
<item>这是第六条数据666666</item>
<item>这是第七7777777条数据</item>
<item>这是第八8888888条数据</item>
<item>这是第九9999999条数据</item>
<item>这是第十10101010101条数据</item>
<item>这是第十一条数据</item>
<item>这是第十二22222条数据</item>
<item>这是第十三条数据</item>
<item>这是第十四条数据</item>
<item>这是第一条数据</item>
<item>这是第二条数据,我很帅!</item>
<item>我也很可爱,这是第三条数据</item>
<item>我喜欢唱歌~这是第四条数据</item>
<item>这是第五条数据55555</item>
<item>这是第六条数据666666</item>
<item>这是第七7777777条数据</item>
<item>这是第八8888888条数据</item>
<item>这是第九9999999条数据</item>
<item>这是第十10101010101条数据</item>
<item>这是第十一条数据</item>
<item>这是第十二22222条数据</item>
<item>这是第十三条数据</item>
<item>这是第十四条数据</item>
</string-array>
</resources>
这是所有源码,如果要复制看效果的把图片路径改成你自己的,包名改一下就完全可以正常运行。或者要我的图片的话@我就可以啦!