作用
-
将Cursor对象中的数据与AdapterView组件(如ListView、GridView等)进行绑定。以下是CursorAdapter的主要作用:
1. 数据源绑定
- 数据源连接:CursorAdapter通过Cursor对象作为数据源,实现了从数据库或其他数据源(如ContentResolver查询结果)中读取数据的功能。这使得开发者能够轻松地将存储在数据库中的数据展示在UI组件上。
- 自动更新:当Cursor对象中的数据发生变化时,CursorAdapter能够自动感知并更新AdapterView中显示的数据,无需手动刷新。
2. 自定义显示逻辑
- 方法重写:通过继承CursorAdapter并重写其方法(如
newView()
、bindView()
、getItemId()
等),开发者可以实现自定义的显示逻辑,如数据的过滤、排序、格式化显示等。 - UI定制:结合自定义的布局文件,开发者可以定制每个列表项或网格项的UI,以满足不同的设计需求。
3. 性能优化
- 视图重用:CursorAdapter内部实现了视图的重用机制,即在滚动列表或网格时,会尽可能重用已存在的视图对象,以减少创建和销毁视图的开销,从而提高性能。
- 数据驱动:由于CursorAdapter直接绑定到数据源上,因此它能够根据数据源的变化动态地更新UI,这种数据驱动的方式有助于减少不必要的UI更新操作。
4. 简化开发
- 减少代码量:通过使用CursorAdapter,开发者可以减少编写适配器代码的工作量,因为CursorAdapter已经提供了一套处理Cursor对象和AdapterView组件之间交互的框架。
- 提高开发效率:由于CursorAdapter的灵活性和易用性,开发者可以更快地实现数据展示功能,从而提高开发效率。
参数、方法解析
-
CursorAdapter(Context context, Cursor cursor, int flags, int layout)
:CursorAdapter是一个抽象类,因此它无法被直接使用new关键词实例化,需要通过继承重写// 自定义 CursorAdapter 类 public class MyCustomCursorAdapter extends CursorAdapter { private LayoutInflater mInflater; public MyCustomCursorAdapter(Context context, Cursor cursor) { super(context, cursor, 0); // 调用父类的构造方法 mInflater = LayoutInflater.from(context); } @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { // 根据布局资源创建新的视图实例 return mInflater.inflate(R.layout.my_list_item, parent, false); } @Override public void bindView(View view, Context context, Cursor cursor) { // 将 Cursor 中的数据绑定到视图上 TextView textView = view.findViewById(R.id.my_text_view); textView.setText(cursor.getString(cursor.getColumnIndexOrThrow("my_column_name"))); // 其他绑定操作... } } // 在你的 Activity 或 Fragment 中实例化它 private MyCustomCursorAdapter adapter; // 假设你已经有了Cursor对象cursor Cursor cursor = getContentResolver().query(MyContentProvider.CONTENT_URI, null, null, null, null); // 实例化自定义的CursorAdapter adapter = new MyCustomCursorAdapter(context, cursor);
- 参数解析:
- context:应用程序的上下文,用于访问资源和布局文件等;在创建视图、加载资源等操作时需要使用
- cursor:一个指向数据库查询结果的Cursor对象,包含了要展示在列表中的数据;适配器通过Cursor遍历数据,并将其绑定到视图上
- flags(可选):用于确定适配器行为的标志,可以是
FLAG_AUTO_REQUERY
和FLAG_REGISTER_CONTENT_OBSERVER
的组合;FLAG_AUTO_REQUERY
已被弃用,但FLAG_REGISTER_CONTENT_OBSERVER
可以用于注册一个内容观察者,以便在数据变化时收到通知。然而,现代应用中更推荐使用LoaderManager
和CursorLoader
来处理数据更新 - int layout(可选):布局文件的资源ID,用于定义列表项的外观;如果指定了此参数,则适配器将使用此布局来创建新的视图。否则,需要在子类中覆盖
newView()
方法来自定义视图的创建
- 参数解析:
-
Cursor cursor = getContentResolver().query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder);
Cursor cursor = getContentResolver().query(MyContentProvider.CONTENT_URI, null, null, null, null);
- 参数解析:
- uri:这是你要查询的内容提供者的URI。这个URI应该在你的内容提供者类中定义,并指向你想要查询的数据集
- projection:这是一个字符串数组,指定了你想要从查询结果中选择的列。如果你传递null,那么将选择所有列
- selection:这是一个用于限制哪些行应该被包含在结果集中的选择条件字符串。如果你传递null,那么将选择所有行
- selectionArgs:这是一个与选择条件中使用的占位符相对应的值的数组。如果你的选择条件字符串中没有占位符(即
?
字符),或者你没有使用选择条件,那么这个参数应该是null - sortOrder:这是一个指定结果集排序方式的字符串。如果你传递null,那么结果集的顺序将是不确定的
- 参数解析:
-
adapter.getCount()
:返回Cursor中的行数,即AdapterView中项的总数(int) -
adapter.getItem(int position)
:根据位置获取Cursor中对应的数据项。然而,在CursorAdapter的实现中,这个方法通常只是简单地返回Cursor对象本身并将其位置移动到指定的位置。实际上,并不建议直接使用返回的Cursor对象,因为这会破坏Cursor的位置状态// 假设你有一个ArrayAdapter<String>,它管理着一个字符串数组 String[] mList = {"Item 1", "Item 2", "Item 3", "Item 4", "Item 5"}; // 假设你已经继承重写了CursorAdapter抽象类,让我们实例化它 MyCustomCursorAdapter adapter = new MyCustomCursorAdapter(context, cursor); int position = 0; // 假设你想获取第一个项 String item = adapter.getItem(position); //这里假设yourStringList是String类型的列表,此时,item就是位于position位置的字符串项
- 参数解析:
- position:获取指定位置的数据项
- 参数解析:
-
adapter.getView(int position, View convertView, ViewGroup parent)
:获取数据项的视图,用于在ListView或Spinner中显示数据。这个方法通常不需要直接调用,而是由系统在需要显示列表项时自动调用// 假设你已经继承重写了CursorAdapter抽象类,让我们实例化它 MyCustomCursorAdapter adapter = new MyCustomCursorAdapter(context, cursor); View view = adapter.getView(position, null, listView);
- 参数解析:
- position:要获取视图的数据项的位置(索引)
- convertView:可复用的视图对象,用于优化列表性能,如果为null,表示要获取一个新的视图对象;如果不为null,则可以重复使用该视图对象,避免重复创建
- parent:父视图,即该视图将被添加到的父容器(例如ListView)
- 参数解析:
-
newView(Context context, Cursor cursor, ViewGroup parent);
:用于创建一个新的视图来展示Cursor当前指向的数据。这个方法只会在需要时才被调用,比如在实例化CursorAdapter时,或者在数据增加导致需要更多视图时@Override public void bindView(View view, Context context, Cursor cursor) { // 假设R.layout.list_item中有一个TextView,其ID为android.R.id.text1 TextView textView = view.findViewById(android.R.id.text1); // 从游标中获取数据并设置到TextView上 // 注意:这里假设游标中有一列名为"column_name"的数据 String data = cursor.getString(cursor.getColumnIndexOrThrow("column_name")); textView.setText(data); // 如果你的布局中有其他视图需要绑定数据,也可以在这里做 }
- 参数解析:
- context:应用程序的全局信息接口,用于访问应用的资源和类加载器等。在创建视图时,可能需要使用上下文来获取布局文件、主题信息等
- cursor:游标对象,它包含了要从数据库中检索的数据。在newView方法中,Cursor已经被移动到了正确的位置(即与当前要创建的视图相关联的数据行)
- parent:新创建的视图将要附加到的父视图组。这个参数在创建视图时可能用于确定布局参数或进行其他与父视图相关的初始化
- 参数解析:
-
bindView(View view, Context context, Cursor cursor)
:将Cursor当前指向的数据绑定到传入的视图上。这个方法在绘制Item之前一定会被调用,包括在重绘时。通过这个方法,可以将Cursor中的数据填充到视图的各个组件中(如TextView、ImageView等)@Override public void bindView(View view, Context context, Cursor cursor) { // 假设你有一个自定义的列表项布局,里面有一个TextView用于显示数据 // 并且这个TextView在你的布局文件中有一个特定的ID,比如R.id.my_text_view // 通过findViewById获取TextView的引用 TextView textView = view.findViewById(R.id.my_text_view); // 从游标中获取数据。这里假设游标中有一列名为"name"的数据 // 使用getColumnIndexOrThrow来确保列存在,并获取其索引 int columnIndex = cursor.getColumnIndexOrThrow("name"); // 使用索引从游标中获取数据,并设置到TextView上 String name = cursor.getString(columnIndex); textView.setText(name); // 如果你的列表项布局中还有其他组件需要绑定数据, // 你可以按照相同的方式获取这些组件的引用,并从游标中检索相应的数据来设置它们 // 例如,如果你的布局中还有一个ImageView用于显示头像, // 你可以这样获取它并设置图片(这里只是示例,实际上设置图片会更复杂) // ImageView imageView = view.findViewById(R.id.my_image_view); // // 假设你有一个方法可以从游标的某个列中加载图片 // // imageView.setImageBitmap(loadImageFromCursor(cursor)); } // 注意:上面的loadImageFromCursor是一个假设的方法, // 你需要根据实际情况来实现从游标加载图片的逻辑。
- 参数解析:
- view:已经存在的视图,这个视图可能是之前通过newView方法创建的,也可能是通过视图重用机制(convertView不为null时)传递进来的
- context:应用程序的全局信息接口,用于访问应用的资源和类加载器等。在创建视图时,可能需要使用上下文来获取布局文件、主题信息等
- cursor:游标对象,同样包含了要从数据库中检索的数据。在bindView方法中,Cursor也已经被移动到了正确的位置
- 参数解析:
-
adapter.changeCursor(Cursor cursor)
:用于更改适配器底层的游标为新传入的游标。如果适配器当前已经有一个游标对象,那么这个方法会先关闭旧的游标,然后再将新的游标设置给适配器。这个方法通常用于在数据发生变化时刷新列表// 假设这是你的CursorAdapter子类的一个实例 MyCursorAdapter adapter = new MyCursorAdapter(context, null, 0); // 假设你有一个ListView,并且已经将myCursorAdapter设置为了它的适配器 ListView listView = findViewById(R.id.my_list_view); listView.setAdapter(adapter); // ... 在某个地方,你获取了一个新的Cursor ... // 例如,从数据库查询中 Cursor newCursor = databaseHelper.querySomeData(); // 现在,你想用新的Cursor来更新ListView显示的数据 // 你可以通过调用myCursorAdapter的changeCursor方法来实现 // 首先,检查当前是否有Cursor被设置,并且它没有被关闭 if (adapter.getCursor() != null && !adapter.getCursor().isClosed()) { // 关闭旧的Cursor(这是可选的,但通常是一个好习惯,以避免内存泄漏) adapter.getCursor().close(); } // 然后,将新的Cursor设置给适配器 adapter.changeCursor(newCursor);
- 参数解析:
- cursor:新的游标对象,用于替换当前适配器中正在使用的游标。
- 参数解析:
-
adapter.getItemId(int position)
:返回适配器中指定位置的数据行的ID。在CursorAdapter中,这个方法通常通过Cursor的getLong(mRowIDColumn)
方法来实现,其中mRowIDColumn是Cursor中用于存储行ID的列的索引。这个方法在需要唯一标识列表中每一项时非常有用,比如在使用ListView的复选框模式时long itemId = -1; // 初始化一个变量来存储ID,这里先给它一个默认值(比如-1,表示无效ID) if (adapter != null && adapter.getCursor() != null && !adapter.getCursor().isClosed()) { // 确保游标有效 int positionYouWant = 0; // 这里替换为你想要获取ID的列表项的位置 if (positionYouWant >= 0 && positionYouWant < adapter.getCount()) { // 确保位置在有效范围内 itemId = adapter.getItemId(positionYouWant); // 现在你可以获取ID了 } } // 现在你可以使用itemId变量了
- 参数解析:
- int position:要获取行ID的数据的索引位置
- 参数解析:
-
cursor.close();
:关闭游标,释放资源if (cursor != null && !cursor.isClosed()) { cursor.close(); }
代码实例
-
首先需要在
AndroidManifest.xml
中添加访问数据库的权限<uses-permission android:name="android.permission.INTERNET"/>
-
创建一个 SQLite 数据库,并添加一个表格来存储数据。创建一个名为
DatabaseHelper.java
的 SQLiteOpenHelper 子类在其中进行操作package com.example.cursoradapterexample; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.content.ContentValues; /** * 辅助类,用于管理数据库的创建和版本管理。 */ public class DatabaseHelper extends SQLiteOpenHelper { // 数据库名称和版本 private static final String DATABASE_NAME = "example.db"; private static final int DATABASE_VERSION = 1; // 表名称和列名称 public static final String TABLE_NAME = "items"; public static final String COLUMN_ID = "_id"; public static final String COLUMN_NAME = "name"; // 创建表的 SQL 语句 private static final String TABLE_CREATE = "CREATE TABLE " + TABLE_NAME + " (" + COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + COLUMN_NAME + " TEXT NOT NULL);"; // 构造函数 public DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } // 第一次创建数据库时调用 @Override public void onCreate(SQLiteDatabase db) { db.execSQL(TABLE_CREATE); // 执行创建表的 SQL 语句 } // 数据库升级时调用 @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); // 删除旧表 onCreate(db); // 重新创建表 } // 插入示例数据的方法 public void insertSampleData() { SQLiteDatabase db = getWritableDatabase(); ContentValues values = new ContentValues(); // 插入第一个条目 values.put(COLUMN_NAME, "Item 1"); db.insert(TABLE_NAME, null, values); // 插入第二个条目 values.put(COLUMN_NAME, "Item 2"); db.insert(TABLE_NAME, null, values); // 插入第三个条目 values.put(COLUMN_NAME, "Item 3"); db.insert(TABLE_NAME, null, values); } }
-
创建一个名为
MyCursorAdapter.java
的自定义 CursorAdapter 类来将数据绑定到 ListViewpackage com.example.cursoradapterexample; import android.content.Context; import android.database.Cursor; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.CursorAdapter; import android.widget.TextView; /** * 自定义 CursorAdapter,用于将数据从游标绑定到 ListView。 */ public class MyCursorAdapter extends CursorAdapter { // 构造函数 public MyCursorAdapter(Context context, Cursor cursor, int flags) { super(context, cursor, flags); } // 创建一个新视图用于每个列表项 @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { // 使用 item_layout 布局文件创建新视图 LayoutInflater inflater = LayoutInflater.from(context); return inflater.inflate(R.layout.item_layout, parent, false); } // 将数据从游标绑定到视图 @Override public void bindView(View view, Context context, Cursor cursor) { // 获取布局文件中的 TextView TextView textView = view.findViewById(R.id.textView); // 从游标中提取数据 String name = cursor.getString(cursor.getColumnIndexOrThrow(DatabaseHelper.COLUMN_NAME)); // 将数据绑定到 TextView textView.setText(name); } }
-
创建布局文件
res/layout/item_layout.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" android:orientation="horizontal" android:padding="16dp"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="18sp"/> </LinearLayout>
-
在 MainActivity 启动类中初始化数据库,插入示例数据,并将数据绑定到 ListView
package com.example.cursoradapterexample; import androidx.appcompat.app.AppCompatActivity; import android.database.Cursor; import android.os.Bundle; import android.widget.ListView; /** * 主活动,设置 ListView 并使用 CursorAdapter 绑定数据。 */ public class MainActivity extends AppCompatActivity { private DatabaseHelper dbHelper; private MyCursorAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 初始化数据库辅助类并插入示例数据 dbHelper = new DatabaseHelper(this); dbHelper.insertSampleData(); // 获取 ListView 的引用 ListView listView = findViewById(R.id.listView); // 查询数据库中的所有条目 Cursor cursor = dbHelper.getReadableDatabase().query( DatabaseHelper.TABLE_NAME, null, null, null, null, null, null ); // 创建一个新的 MyCursorAdapter 并将其设置为 ListView 的适配器 adapter = new MyCursorAdapter(this, cursor, 0); listView.setAdapter(adapter); } }
-
创建主活动的布局文件:
res/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@+id/listView" android:layout_width="match_parent" android:layout_height="match_parent"/> </RelativeLayout>
-
运行应该能看到一个列表,展示了从数据库中检索到的数据。这个示例中,我们创建了一个 SQLite 数据库,插入了一些示例数据,然后使用 CursorAdapter 将这些数据绑定到一个 ListView