Android家庭记账本开发第五天:ListAdapter适配器的编写

昨天讲了数据库相关的操作现在来看我们当初在MainActivity的Java文件当中的initData方法:

 1 @SuppressLint("Range")
 2     private void initData() {
 3         helper=new DBHelper(MainActivity.this);
 4         list=new ArrayList<>();
 5         SQLiteDatabase db=helper.getReadableDatabase();
 6         Cursor cursor=db.query("account",null,null,null,null,
 7                 null,null);
 8         while (cursor.moveToNext()){
 9             costList clist=new costList();//构造实例
10             clist.set_id(cursor.getInt(cursor.getColumnIndex("_id")));
11             clist.setTitle(cursor.getString(cursor.getColumnIndex("Title")));
12             clist.setDate(cursor.getString(cursor.getColumnIndex("Date")));
13             clist.setMoney(cursor.getString(cursor.getColumnIndex("Money")));
14             list.add(clist);
15         }
16         listView = findViewById(R.id.list_view);
17         //绑定适配器
18         ListAdapter mAdapter = new ListAdapter(this,list);
19         mAdapter.setOnItemClickListener(this);
20         listView.setAdapter(mAdapter);
21         db.close();
22     }

大部分代码现在应该都能看懂了,现在我们来看适配器相关的部分,首先listView是我们在前面声明的一个ListView对象,我们通过findViewById将list_view的布局传给listView,告诉它数据显示的格式,这些我们都是在之前第二天所讲到的,之后就是适配器相关的代码了,这里我们首先需要了解适配器到底是什么,为什么显示数据需要用到适配器。

首先要知道安卓开发有很多种模式,适配器模式只是其中的一种,适配器模式是一种结构型设计模式。它可以把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。适配器模式是为了解决接口不兼容问题的。比如厂商给你的接口和你现有的接口对接不起来、旧的数据和新的数据接对接不起来等。

 而我们目前需要了解的就只有适配器是用来连接列表项与数据库数据的一个桥梁,下面我们看代码:

     ListAdapter mAdapter = new ListAdapter(this,list);
     mAdapter.setOnItemClickListener(this);
     listView.setAdapter(mAdapter);

第一行我们用list对象去实例化了一个ListAdapter对象,然后第二行代码设置了一个列表项的点击时间,最后将适配器绑定到listView当中,其中前两行代码都传入了this参数,这个指的是上下文的环境,通过传入this参数使得适配器和点击监听器能够与当前类进行交互,并在需要时访问当前类的成员变量和方法。

下面我们直接看适配器的代码:

 1 package com.example.myapplication3;
 2 
 3 import android.content.Context;
 4 import android.view.LayoutInflater;
 5 import android.view.View;
 6 import android.view.ViewGroup;
 7 import android.widget.BaseAdapter;
 8 import android.widget.TextView;
 9 
10 import java.util.List;
11 
12 public class ListAdapter extends BaseAdapter {
13     private Context mContext; // 保存上下文引用
14     private List<costList> mList;
15     private LayoutInflater mLayoutInflater;
16     private OnItemClickListener mListener;
17     public interface OnItemClickListener {
18         void onItemClick(int position);
19     }
20     public void setOnItemClickListener(OnItemClickListener listener) {
21         mListener = listener;
22     }
23     public ListAdapter(Context context, List<costList> list) {
24         mContext = context; // 保存上下文引用
25         mList = list;
26         // 通过外部传来的Context初始化LayoutInflater对象
27         mLayoutInflater = LayoutInflater.from(context);
28     }
29 
30     @Override
31     public int getCount() {
32         return mList.size();
33     }
34 
35     @Override
36     public Object getItem(int position) {
37         return mList.get(position);
38     }
39 
40     @Override
41     public long getItemId(int position) {
42         return position;
43     }
44 
45     @Override
46     public View getView(int position, View convertView, ViewGroup parent) {
47         ViewHolder viewHolder;
48         if (convertView == null) {
49             // 如果convertView为空,说明当前没有可重用的视图,需要创建新的视图
50             convertView = mLayoutInflater.inflate(R.layout.list_item, parent, false);
51             viewHolder = new ViewHolder();
52             viewHolder.tvTitle = convertView.findViewById(R.id.tv_title);
53             viewHolder.tvDate = convertView.findViewById(R.id.tv_date);
54             viewHolder.tvMoney = convertView.findViewById(R.id.tv_money);
55             convertView.setTag(viewHolder);
56         } else {
57             // 如果convertView不为空,说明有可重用的视图,直接从tag中获取viewHolder
58             viewHolder = (ViewHolder) convertView.getTag();
59         }
60         costList item = mList.get(position);
61         convertView.setOnClickListener(new View.OnClickListener() {
62             @Override
63             public void onClick(View v) {
64                 if (mListener != null) {
65                     mListener.onItemClick(position);
66                 }
67             }
68         });
69         viewHolder.tvTitle.setText(item.getTitle());
70         viewHolder.tvDate.setText(item.getDate());
71         viewHolder.tvMoney.setText(item.getMoney());
72 
73         return convertView;
74     }
75 
76 
77 
78     // 内部类ViewHolder,用于保存item中的控件实例,避免重复调用findViewById
79     static class ViewHolder {
80         TextView tvTitle;
81         TextView tvDate;
82         TextView tvMoney;
83     }
84 }

首先适配器当中需要有一个构造函数来接收参数并初始化适配器,之后的三个方法是BaseAdapterListAdapter的父类)中的重要方法,用于定义适配器的行为和提供数据。

  1. getCount():该方法返回适配器中包含的数据项的数量。在ListView或GridView等列表视图中,它确定了列表的长度,即列表将显示多少个项目。通常,它返回数据集的大小。

  2. getItem(int position):该方法根据给定的位置返回对应的数据项。在列表视图中,它用于获取特定位置上的数据对象。通常,它返回数据集中指定位置的数据对象。

  3. getItemId(int position):该方法返回给定位置上的项的ID。这个ID在列表中是唯一的,并且用于帮助列表视图管理项目的状态。通常,它返回指定位置的项目ID或者位置本身作为ID。

最重要的是下面的getView方法,我们来详细解释一下这个方法

 1 @Override
 2     public View getView(int position, View convertView, ViewGroup parent) {
 3         ViewHolder viewHolder;
 4         if (convertView == null) {
 5             // 如果convertView为空,说明当前没有可重用的视图,需要创建新的视图
 6             convertView = mLayoutInflater.inflate(R.layout.list_item, parent, false);
 7             viewHolder = new ViewHolder();
 8             viewHolder.tvTitle = convertView.findViewById(R.id.tv_title);
 9             viewHolder.tvDate = convertView.findViewById(R.id.tv_date);
10             viewHolder.tvMoney = convertView.findViewById(R.id.tv_money);
11             convertView.setTag(viewHolder);
12         } else {
13             // 如果convertView不为空,说明有可重用的视图,直接从tag中获取viewHolder
14             viewHolder = (ViewHolder) convertView.getTag();
15         }
16         costList item = mList.get(position);
17         convertView.setOnClickListener(new View.OnClickListener() {
18             @Override
19             public void onClick(View v) {
20                 if (mListener != null) {
21                     mListener.onItemClick(position);
22                 }
23             }
24         });
25         viewHolder.tvTitle.setText(item.getTitle());
26         viewHolder.tvDate.setText(item.getDate());
27         viewHolder.tvMoney.setText(item.getMoney());
28 
29         return convertView;
30     }

getView方法:负责为每个列表项提供视图,通常在此方法中设置列表项的布局和数据。

我们先解释一下之前在构造函数就有的一个未知的类LayoutInflater:LayoutInflater是一个抽象类,继承自Object,位于android.view包下,作用类似于findViewById(),但是不同的是,LayoutInflater找的是xml布局文件并且实例化,而finViewById()是找到具体的widget控件。具体想要进一步了解的这里给出链接:https://blog.csdn.net/JMW1407/article/details/116227026

我们这里只需要知道这个是用来管理布局的即可

我们看一下getView的参数

  • position:当前要显示的列表项的位置。
  • convertView:用于重用的已存在的视图(可能是之前滑出屏幕的),在第一次显示某个位置的时候,该参数为null,需要我们创建一个新的视图。之后滑出屏幕的视图会被传递给这个参数,我们可以直接对其进行重用,避免重复创建视图,提高性能。
  • parent:父视图,即包含列表项的ListView或者其他列表视图。

关于安卓开发当中的视图体系这里就不详细展开了,附上链接:https://zhuanlan.zhihu.com/p/638260835?utm_id=0

通过判断convertView是否为null来确定当前是否有可重用的视图。如果convertView为null,说明当前没有可重用的视图,需要创建新的视图。如果convertView不为null,说明有可重用的视图,直接从convertView的tag中获取之前保存的ViewHolder对象。ViewHolder是一种模式,用于在列表中存储视图的子视图引用,避免重复调用findViewById方法,提高性能。

之后就是对点击相关逻辑的处理,首先通过传入的位置参数从costList列表当中获取到点击的数据项,然后设置一个点击监听器。关于监听器的设置这里需要详细介绍一下,我这里采用的是通过使用接口的方式设立监听器:

首先定义一个监听器的接口

1     public interface OnItemClickListener {
2         void onItemClick(int position);
3     }

然后,在适配器类中添加一个成员变量来保存接口的引用,并添加一个方法用于设置点击监听器:

 1     private OnItemClickListener mListener;
 2 ...
 3     public void setOnItemClickListener(OnItemClickListener listener) {
 4         mListener = listener;
 5     }
 6 ...
 7     convertView.setOnClickListener(new View.OnClickListener() {
 8             @Override
 9             public void onClick(View v) {
10                 if (mListener != null) {
11                     mListener.onItemClick(position);
12                 }
13             }
14         });

getView方法中,当列表项被点击时,调用接口的onItemClick方法,并传递点击的位置。在setOnItemClickListener方法中,用于外部类设置点击监听器。

在MainActivity当中首先要声明接口

public class MainActivity extends AppCompatActivity implements ListAdapter.OnItemClickListener
然后再重写点击事件的逻辑
1   @Override
2     public void onItemClick(int position) {
3         // 处理点击事件的逻辑
4         Log.d("Main", "jump");
5         costList item = list.get(position);
6         Intent intent = new Intent(MainActivity.this, upgrade_cost.class);
7         intent.putExtra("item_id", item.get_id());
8         startActivityForResult(intent, 1);
9     }

这样就可以完成监听器的设置,在点击时后完成携带点击项数据的跳转

有关其他监听器设置的文章我也没有找到比较全面的文章,这里就不给出推荐了,自行寻找。

posted @ 2024-02-20 19:05  起名字真难_qmz  阅读(69)  评论(0)    收藏  举报