这是一个最简单的List View,创建很简便,但是缺点也很明显,仅仅只能适配一个字符串。现在就来看看如何创建这个最简单的适配器
1.在布局文件中添加List View
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
2.在主活动中,绑定这个控件。在主活动的onCreate()方法里面:
ListView listView = (ListView)this.findViewById(R.id.list_view);
3.在主活动中,创建一个ArrayAdapter适配器,对其实例化,然后让其和List View进行绑定: 写在onCreate()方法里面
// 以下是很简单的通过ArrayAdapter适配器来适配一个列表,缺点是一行只能适配一个文本
String[] data= {"apple","banana","origin","pear"};
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
MainActivity.this, //上下文
android.R.layout.simple_list_item_1, //列表布局采用系统样式1
data //列表数据来源字符串数组data
);
listView.setAdapter(adapter);
①data 字符串数组,用于存放每一个子项的文本。
②实例化一个ArrayAdapter适配器,这里使用它带有3个参数的构造函数,第一个参数是context,是适配器的上下文环境,代表在哪个活动适配。 第二个参数是一个样式,这里使用系统自带的一个样式android.R.layout.simple_list_item1 第三个参数列表的数据来源,为字符串数组类型。
③适配器创建好以后,就可以将其和List View绑定到一起,利用ListView的setAdapter()方法,把这个适配器和ListView绑定到一起。这样就可以显示出上图的效果。
3-2.ArrayAdapter的另一种构造方法:
如果你不满足于系统提供给你的样式,也可以使用自己的样式。
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
MainActivity.this, //上下文
R.layout.stu_list, //列表布局文件
R.id.custom_textview, //列表布局文件里面的一个TextView控件
data //适配的数据
);
这是带四个参数的构造函数,可以发现,这里面有两个地方和上面的不一样,第二个参数是你自己写的layout布局文件,而第三个参数,是这个布局文件里面的一个控件。这样,数据源的文本就会显示到这个指定的控件上,而ListView的每一个子项,都是你这个布局文件。
这种带四个参数的构造函数,比起上面的那一个,多了一个自己的布局文件,你可以显示文本信息要写在哪一个地方,但是缺点还是只能适配一个字符串。对比上面的那个,就是单纯的好看一点吧。
4.当适配的内容发生变化的时候,通知适配器进行更新:
当你的列表数据源发生了改变,这时候就需要通知适配器所适配的内容需要改变了,具体的显示就是你可以看到新的ListView内容。
比如下面这样:
data[0] = "mmm";
adapter.notifyDataSetChanged(); //修改数据源以后更新适配器,动态刷新列表
在上例基础上,把字符串数组的第一个元素值改变为"mmm"。这时候,就可以使用适配器的notifyDataSetChanged()方法,通知适配器更新,实现动态刷新的效果。
有了ListView显示出来以后,还需要为其设置点击事件,当点击不同的子项的时候,有不同的反应。如果仅仅可以看,那有什么用呢?
在主活动的onCreate()方法里面,加入如下代码:
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
String item = adapter.getItem(i);
Toast.makeText(MainActivity.this, item, Toast.LENGTH_SHORT).show();
}
});
使用ListView的setonItemClickListerner可以实现对列表视图当个子项点击的事件监听。这上面的代码中,重写onItemClick方法,实现点击子项弹出显示对应文本。
!!注意:如果你想要获得子项的内容,比如上面我就获得了一个列表视图的数据源字符串,我建议你使用适配器的getItem(i)来获得子项。或许你也可以用List<> 的get()方法,但是并不推荐这样使用,因为如果是List的get方法,这个List你写在主活动里面,如果你进行了模糊查找等,适配器里面的List发生了改变,但是你任然使用List的getItem方法来获得,那么就会出现不对应的问题。
好像有点而绕,总之,只要记得推荐使用适配器提供的getItem方法来获得子项。这样是最好的方法。
上面的ListView,是使用最简单的适配器绑定的。它有一个不足,那就是所能适配的仅仅只有一个文本信息。但是,通常我们的子项,可能有多个TextView,有多个ImageView等,需要对不同的TextView适配不同的内容,这个时候,就需要用到自定义的适配器了。
自定义的适配器,简单来说,需要有3个文件:
1.子项的布局文件、2.所需要适配的数据源类、3.自定义的适配器类
举个示例,现在我们来实现一下下面这个图片的ListView
可以看到,每一个子项,都有一个卡号、类型、金额、日期需要进行适配。前面所用的适配,已经完全不能够满足我们的需要。那么,现在开始自定义的适配器:
1.新建一个布局文件,用于子项的布局:
<?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="120dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:orientation="vertical"
>
<TextView
android:id="@+id/tv_idCard"
android:text="卡号:1234567890123456"
android:textSize="18sp"
android:layout_width="wrap_content"
android:layout_height="20dp" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_leixing"
android:text="类型:转账"
android:textSize="18sp"
android:layout_alignParentRight="true"
android:gravity="left"
android:layout_toRightOf="@+id/tv_idCard"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
<TextView
android:id="@+id/tv_jiner"
android:text="金额:1000000.00"
android:textSize="18sp"
android:layout_alignParentRight="true"
android:gravity="left"
android:layout_toRightOf="@+id/tv_idCard"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
</LinearLayout>
<TextView
android:id="@+id/tv_day"
android:text="日期:2020-12-11-12-56-04"
android:textSize="18sp"
android:gravity="right"
android:layout_below="@+id/tv_idCard"
android:layout_toRightOf="@+id/tv_idCard"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="20dp" />
<View
android:id="@+id/vw_Line_password"
android:layout_width="match_parent"
android:layout_height="1dip"
android:background="#83738F"
android:layout_below="@id/tv_day"
android:layout_marginTop="2dp"
android:layout_marginBottom="2dp"
/>
</LinearLayout>
上面的布局文件代码,你不必要详细的看,我们只需要知道自定义的适配器如何实现。这里仅仅贴出来。
2.自定义一个数据源类:
public class JiaoYi {
private String bankId; //交易卡号
private String type; //类型
private float money; //金额
private String day; //日期
private String targetBankId=""; //目标账户卡号,仅在转账的时候会被赋值
JiaoYi(String id,String ty,float mo,String d){
this.bankId = id;
this.type = ty;
this.money = mo;
this.day = d;
}
public void setTargetBankId(String t){this.targetBankId = t;}
public String getTargetBankId(){return this.targetBankId;}
public String getBankId(){return this.bankId;}
public String getType(){return this.type;}
public float getMoney(){return this.money;}
public String getDay(){return this.day;}
}
这一个类的作用,是包括了你所需要的适配的内容。比如我们这里所需要适配的内容,有卡号、类型、金额、日期。那么我们这里就写了一个交易类,这个类保存了上述所需要的信息。
3.自定义适配器,实现绑定视图和获得适配内容:
新建一个类,继承于BaseAdapter。然后实现其6个重要的方法
右击包名–新建一个class 。手动添加继承 extends BaseAdapter ,这个时候你会发现有红色波浪线,点击波浪线处,等待一会有个小灯泡,选择小灯泡就会看到一个继承基类的方法。那些方法就是必须要实现的方法。
点击小灯泡,implement method
见名知意,
getCount 是得到适配的数量,即有多个子项
getItem 是得到子项,即返回所适配的子项
getItemId 是得到子项的id号
getView 是得到子项的视图,用于每一个子项在显示的时候如何进行显示
分析完毕,现在让我们看一下具体的代码吧:
class JiaoyiAdapter extends BaseAdapter {
private List<JiaoYi> list_jiaoyi; //用于显示数据
private List<JiaoYi> list_forRemember; //用于备份全部数据
private LayoutInflater inflater;
private JiaoyiFilter jiaoyiFilter;
private class ViewHolder{
TextView kahao,type,money,day; //卡号,类型,金额,日期
}
JiaoyiAdapter(Context context,List<JiaoYi> m){
this.list_jiaoyi = m;
this.list_forRemember = list_jiaoyi;
this.inflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return list_jiaoyi.size();
}
@Override
public Object getItem(int i) {
return list_jiaoyi.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
JiaoYi temp = list_jiaoyi.get(i);
ViewHolder viewHolder = null;
if (view == null){
view = inflater.inflate(R.layout.listview_jiaoyi,null);
viewHolder = new ViewHolder();
viewHolder.kahao = (TextView)view.findViewById(R.id.tv_idCard);
viewHolder.type = (TextView)view.findViewById(R.id.tv_leixing);
viewHolder.money = (TextView)view.findViewById(R.id.tv_jiner);
viewHolder.day = (TextView)view.findViewById(R.id.tv_day);
view.setTag(viewHolder);
}
else {
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.kahao.setText("卡号:"+temp.getBankId());
viewHolder.type.setText("类型:"+temp.getType());
viewHolder.money.setText("金额:"+String.valueOf(temp.getMoney()));
viewHolder.day.setText("日期:"+temp.getDay());
return view;
}
}
分析:
①首先来看一下构造函数,这个是必须要写的。
JiaoyiAdapter(Context context,List<JiaoYi> m){
this.list_jiaoyi = m;
this.list_forRemember = list_jiaoyi;
this.inflater = LayoutInflater.from(context);
}
一般是写一个带两个参数的构造函数即可,第一个参数是context 上下文,第二个参数是一个List类型,是你要适配的数据源。在构造函数中,把外部的context和List赋值给自己的成员变量。这里面使用到了一个成员变量LayoutInflater类型的inflater,这个是用于在后面动态加入布局。
②重写getCount,getItem,getItemId
@Override
public int getCount() {
return list_jiaoyi.size();
}
@Override
public Object getItem(int i) {
return list_jiaoyi.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
从上面的分析,可以知道,
我们要适配的数据源是List列表里面的每一个元素,那么getCount这个方法返回的适配的数量,就是这个List的size
getItem需要获得每一个适配子项,这每一个子项,就是List对应的每一个元素,可以通过List的get方法,直接获得
getItemId方法,是得到子项的Id号,因为子项的id就是List的id,所以这里直接返回i即可。
③重写getView方法,这个方法是最最至关重要的,从它的返回值是一个view类型,我们可以知道,这个方法,解决的是每一个子项的view。即这个子项是如何显示的(在哪个context里面显示?显示的布局是什么样子的?所需要适配的又是什么?)
在写这个方法之前,我们先写一个私有类ViewHolder
private class ViewHolder{
TextView kahao,type,money,day; //卡号,类型,金额,日期
}
这个私有类,里面包含该适配器所需要适配的所有控件。因为我们这里需要适配的卡号、类型、金额、日期,是分别在4个不同的TextView里面,那么我们就需要4个TextView控件。
现在,开始重写这个getView方法
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
JiaoYi temp = list_jiaoyi.get(i);
ViewHolder viewHolder = null;
if (view == null){
view = inflater.inflate(R.layout.listview_jiaoyi,null);
viewHolder = new ViewHolder();
viewHolder.kahao = (TextView)view.findViewById(R.id.tv_idCard);
viewHolder.type = (TextView)view.findViewById(R.id.tv_leixing);
viewHolder.money = (TextView)view.findViewById(R.id.tv_jiner);
viewHolder.day = (TextView)view.findViewById(R.id.tv_day);
view.setTag(viewHolder);
}
else {
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.kahao.setText("卡号:"+temp.getBankId());
viewHolder.type.setText("类型:"+temp.getType());
viewHolder.money.setText("金额:"+String.valueOf(temp.getMoney()));
viewHolder.day.setText("日期:"+temp.getDay());
return view;
}
先来看一下这个getView方法,所带有的四个参数。
1.i 这个i其实就是position位置,即现在是哪一个子项进行getView操作
2.view 视图,每一个子项,都有其自己的一个view,调用这个方法,最后返回的view,就是告诉适配器,这个子项最终显示的样子是什么样的
3.ViewGroup 视图组,这个我们这里暂时不用到。
回顾一下前面说的,在哪个context里面显示?显示的布局是什么样子的?所需要适配的又是什么?现在就来一一解决这些问题
1.view = inflater.inflate(R.layout.listview_jiaoyi,null);
通过这一行代码,为这个子项的view加入需要显示的布局,上下文context之前在构造函数中已经指明了。
2.
viewHolder = new ViewHolder();
viewHolder.kahao = (TextView)view.findViewById(R.id.tv_idCard);
viewHolder.type = (TextView)view.findViewById(R.id.tv_leixing);
viewHolder.money = (TextView)view.findViewById(R.id.tv_jiner);
viewHolder.day = (TextView)view.findViewById(R.id.tv_day);`
这一部分代码,实例化一个ViewHolder类,然后把里面的TextView和布局文件里面的控件一一绑定。
3.
JiaoYi temp = list_jiaoyi.get(i);
viewHolder.kahao.setText("卡号:"+temp.getBankId());
viewHolder.type.setText("类型:"+temp.getType());
viewHolder.money.setText("金额:"+String.valueOf(temp.getMoney()));
viewHolder.day.setText("日期:"+temp.getDay());
这一部分的代码,解决了前面4个控件所需要适配显示的具体文本信息。首先定义一个数据源类对象temp ,从List的get方法为其实例化。然后就可以进行下面的具体赋值操作了。
4.最后,看最完整的getView,为什么要使用一个私有类ViewHolder?为什么要对view进行if(view == null)
这样的判断呢?
因为在ListView显示的时候,如果有很多的数据,而我们上下滑动翻动的速度过快,那么适配器就会不停的进行getView方法,为了让效率提高,这里使用一个私有类ViewHolder,里面先绑定了需要适配的具体控件,这样就可以提高一点速度。其次,因为每一个getView,本身都会传入一个view参数,如果每一次进行getView,都要重写返回一个新的view,那样势必会造成内存的大量浪费。所以,我们可以通过view.setTag方法,可以把ViewHolder放入,当下一次调用这个子项的getView的时候,先判单一下view是否为空,如果不为空,就说明之前已经对其初始化一次了,那么就可以通过getTag方法,从里面取得ViewHolder,然后对里面的控件进行赋值操作。
如果是自定义的适配器,就需要自定义一个Filter类来实现模糊查找。
在刚刚的交易适配器类里面,再添加一个自定义类
class JiaoyiFilter extends Filter{
//在下面这个方法中定义过滤的规则
@Override
protected FilterResults performFiltering(CharSequence charSequence) {
FilterResults results = new FilterResults(); //过滤结果
List<JiaoYi> list; //用于存放交易的结果
if (TextUtils.isEmpty(charSequence)){
list = list_forRemember; //如果过滤内容为空则显示全部,把备份的数据替换进去
}else {
list = new ArrayList<JiaoYi>();
for (JiaoYi jiaoYi:list_forRemember){ //遍历的对象是备份list 这里面才是全部元素
//下面是过滤的规则 从JiaoYi对象里面获得匹配内容然后判断是否contains过滤文本
if (jiaoYi.getBankId().contains(charSequence)){
list.add(jiaoYi);
}
}
}
results.values = list; //设置value 为list
results.count = list.size(); //设置大小
return results;
}
//在下面的方法中 通知适配器更新界面
@Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
list_jiaoyi = (List<JiaoYi>) filterResults.values;
if (filterResults.count>0){
notifyDataSetChanged(); //通知适配器数据已经改变
}else {
notifyDataSetInvalidated(); //通知数据失效
}
}
}
别忘记:
class JiaoyiAdapter extends BaseAdapter implements Filterable
接着,重写getFilter方法
@Override
public Filter getFilter() {
if (jiaoyiFilter == null){
jiaoyiFilter = new JiaoyiFilter();
}
return jiaoyiFilter;
}
这里面的jiaoyiFilter
是前面适配器的成员变量private JiaoyiFilter jiaoyiFilter;
写好了以后,在主活动里面listView_jiaoyi.setTextFilterEnabled(true);
为ListView启动过滤。然后就可以在活动里面进行过滤了
adapter_jiaoyi.getFilter().filter(edt_yhk.getText().toString());
如果筛选的条件为空,那么记得使用
adapter_jiaoyi.getFilter().filter("");
把过滤清除
比如下面,我在搜索框内为其设置当输入内容发生改变的时候进行ListView的过滤
edt_yhk.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if (TextUtils.isEmpty(edt_yhk.getText().toString())){
//清除过滤
adapter_jiaoyi.getFilter().filter("");
}else {
adapter_jiaoyi.getFilter().filter(edt_yhk.getText().toString());
//list_jiaoyi = adapter_jiaoyi.getList_jiaoyi();
}
}
@Override
public void afterTextChanged(Editable editable) {
}
});
到此,基本上都已经讲完了。关于自定义的适配器,在主活动里面如何调用,这个和前面最简单的适配器其实是一模一样的,都是
list_jiaoyi = new ArrayList<JiaoYi>();
listView_jiaoyi = (ListView)findViewById(R.id.lv_jiaoyi);
adapter_jiaoyi = new JiaoyiAdapter(JiaoYiActivity.this,list_jiaoyi);
listView_jiaoyi.setAdapter(adapter_jiaoyi);
初始化所需要数据源List列表
绑定ListView控件
初始化自定义的适配器,用到context上下文以及List数据源列表两个参数
然后为这个ListView绑定适配器ListView.setAdapter。
如果要为List添加元素,可以通过List的add方法。或者remove方法来移除某个元素。再通过适配器的notifyDataSetChanged()方法动态刷新。
到此,这一篇关于ListView以及自定义适配器、模糊查找的学习笔记就结束了。本来还想着写RecyclerView的,但是内容有点长,有点啰嗦,就放在另一篇笔记里面记录吧。