首页 > 其他分享 >Android开发 - (适配器)Adapter类中CursorAdapter实现类详细解析

Android开发 - (适配器)Adapter类中CursorAdapter实现类详细解析

时间:2024-07-31 20:17:52浏览次数:14  
标签:cursor CursorAdapter 适配器 视图 Cursor adapter null Adapter

作用

  • 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_REQUERYFLAG_REGISTER_CONTENT_OBSERVER的组合;FLAG_AUTO_REQUERY已被弃用,但FLAG_REGISTER_CONTENT_OBSERVER可以用于注册一个内容观察者,以便在数据变化时收到通知。然而,现代应用中更推荐使用LoaderManagerCursorLoader来处理数据更新
      • 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)获取数据项的视图,用于在ListViewSpinner中显示数据。这个方法通常不需要直接调用而是由系统在需要显示列表项时自动调用

    // 假设你已经继承重写了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中,这个方法通常通过CursorgetLong(mRowIDColumn)方法来实现,其中mRowIDColumnCursor中用于存储行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.javaSQLiteOpenHelper 子类在其中进行操作

    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 类来将数据绑定到 ListView

    package 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

标签:cursor,CursorAdapter,适配器,视图,Cursor,adapter,null,Adapter
From: https://www.cnblogs.com/ajunjava/p/18335383

相关文章

  • Android开发 - (适配器)Adapter类中ArrayAdapter实现类详细解析
    作用将数组数据映射到UI组件(如ListView、Spinner等)上的角色。它是BaseAdapter的一个子类,专门用于处理简单的数据集合,如数组或列表。ArrayAdapter简化了数据到视图映射的过程,使得开发者能够以更少的代码实现数据的展示。它的主要作用为以下几点:数据绑定:它能够将一组数据......
  • 设计模式:代理、装饰和适配器模式的区别
    结构对比讲实话,博主当初学习完整设计模式时,这三种设计模式单独摘哪一种都是十分清晰和明确的,但是随着模式种类的增加,在实际使用的时候竟然会出现恍惚,例如读开源代码时,遇到不以模式命名规范的代码时,一时难以说清具体是使用的这三种里的哪一种。之所以会出现混淆的原因是,三种模式......
  • ComfyUI插件:IPAdapter_plus(新版)节点
    ComfyUI插件:IPAdapter_plus(新版)节点前言:学习ComfyUI是一场持久战,而IPAdapter_plus是常用且便捷有效的风格迁移模型,可以通过提供参考图像去进行图像的生成,比如风格迁移,风格融合,人物脸部模拟等各种工作,请大家重点关注本篇内容,深刻理解节点用法!!祝大家学习顺利,早日成为ComfyUI的高手......
  • 设计模式总结:适配器、桥接、组合和迭代器模式
    在之前的对话中,我们讨论了五种常见的Java设计模式:单例、工厂、策略、装饰器和观察者模式。现在,让我们继续探索其他四种设计模式:适配器、桥接、组合和迭代器模式。适配器模式概念:适配器模式是一种结构型设计模式,用于将一个类的接口转换为另一个类期望的接口。适配器模式......
  • oss模块设计之适配器模式改造minio
    在进行本节的笔记之前,我们先进行对oss服务与minio做一个简单介绍,方便大家更便于理解;OSS服务(ObjectStorageService)OSS服务,即对象存储服务,是一种用于云端的大规模、高可用、安全、低成本的数据存储服务。它主要用于存储非结构化的数据,如图片、音频、视频、文档等文件。OSS服务具......
  • Android 14 适配之— BluetoothAdapter、JobScheduler、 Tiles
    1. BluetoothAdapter改动:在BluetoothAdapter中必须加入 BLUETOOTH_CONNECT权限 Android14(API级别34)或更高版本为目标的App,在调用函数 BluetoothAdapter getProfileConnectionState() 时,需要 BLUETOOTH_CONNECT 权限,<uses-permissionandroid:name="android......
  • 设计模式之适配器模式(学习笔记)
    定义 适配器模式是一种结构型设计模式,它允许将一个类的接口转换为客户端希望的另一个接口。适配器使得原本由于接口不兼容而不能一起工作的类可以协同工作。通过创建适配器类,可以将现有类的接口转换成目标接口,从而使这些类能够在一起工作。为什么使用适配器模式兼容性适......
  • Stable Diffusion【进阶篇】:真人漫改之IP-Adapter实现
    大家好,今天我分享真人漫改实现方式:借助ControlNet的IP-Adapter控制模型,IP-Adapter控制模型是由腾讯研究院出品的一个新的ControlNet模型,关于该模型可以理解为图片提示词,类似于MD垫图的效果,但是比tagger标签器提取出图片的元素构成效果更好。它不仅参考图片的风格、光影特效......
  • Stable Diffusion ControlNet垫图:IP-Adapter实现图片风格迁移
    IP-Adapter实现的SD垫图功能对我们的图片处理非常有用,后面我们会进行一系列IP-Adapter的应用分享,通过具体的实例真正看到IP-Adapter的强大。文章使用的AI工具SD整合包、各种模型插件、提示词、AI人工智能学习资料都已经打包好放在网盘中了,无需自行查找,有需要的小伙伴下方扫......
  • Stable Diffusion|IP-Adapter 图片风格迁移
    文末扫码可获取IP-Adapter插件!前段时间腾讯发布了一个新的ControlNet模型叫“IP-Adapter”,它的作用就是把上传的图像转化为图像提示词,简单的来说就是跟Midjourney的垫图功能差不多。IP-Adapter能够精准地识别参考图的风格特征,并且可以很好的适配其他的模型以及ControlNet......