zoukankan      html  css  js  c++  java
  • Android-ContentProvider-UriMatcher

    注意:在ContentProvider里面写对数据库增删改查的时候,千万不能 db.close();  cursor.close(); 等操作,不然其他应用访问不到数据,也没有必要写isOpen();

    ContentProviderServer应用-->定义 MySqliteOpenHeper 数据库帮助操作类(创建数据库,创建表,初始化数据)

    定义两张表,dog表  cat表

    package liudeli.cp.server.cp;
    
    import android.content.ContentValues;
    import android.content.Context;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteOpenHelper;
    
    public class MySqliteOpenHeper extends SQLiteOpenHelper {
    
        private static final String DB_NAME = "dogDatabase.db";
        private static final int VERSON = 1;
    
        /**
         * 定义单例模式 懒汉式
         */
        private static MySqliteOpenHeper mySqliteOpenHeper;
    
        public static MySqliteOpenHeper getInstance(Context context) {
            if (null == mySqliteOpenHeper) {
                synchronized (MySqliteOpenHeper.class) {
                    if (null == mySqliteOpenHeper) {
                        mySqliteOpenHeper = new MySqliteOpenHeper(context, DB_NAME, null,VERSON);
                    }
                }
            }
            return mySqliteOpenHeper;
        }
    
        private MySqliteOpenHeper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
            super(context, name, factory, version);
        }
    
        @Override
        public void onCreate(SQLiteDatabase db) {
            // 两张表字段一模一样
            db.execSQL("create table dog(_id integer primary key autoincrement, name text, age integer);");
            db.execSQL("create table cat(_id integer primary key autoincrement, name text, age integer);");
            initDogTableData(db);
        }
    
        private void initDogTableData(SQLiteDatabase db) {
            ContentValues contentValues = new ContentValues();
    
            contentValues.put("name", "阿黄");
            contentValues.put("age", 88);
            db.insert("dog", null, contentValues);
    
            contentValues.put("name", "阿大黄");
            contentValues.put("age", 22);
            db.insert("dog", null, contentValues);
    
            contentValues.put("name", "小黄");
            contentValues.put("age", 23);
            db.insert("dog", null, contentValues);
    
            contentValues.put("name", "小白");
            contentValues.put("age", 34);
            db.insert("cat", null, contentValues);
    
            contentValues.put("name", "花白");
            contentValues.put("age", 34);
            db.insert("cat", null, contentValues);
    
            contentValues.put("name", "白白");
            contentValues.put("age", 34);
            db.insert("cat", null, contentValues);
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    
        }
    }

    ContentProviderServer应用-->定义 MyContentProvider 对数据库增删改查操作

    为什么要有 UriMatcher ?

    答:UriMatcher的出现可以解决被访问的细节,例如:我有cat表 和 dog表,我就想把cat表给暴露出去 并且只暴露cat表的:(ID查询,全部查询,ID修改,ID删除)

    package liudeli.cp.server.cp;
    
    import android.content.ContentProvider;
    import android.content.ContentUris;
    import android.content.ContentValues;
    import android.content.UriMatcher;
    import android.database.Cursor;
    import android.database.sqlite.SQLiteDatabase;
    import android.net.Uri;
    import android.text.TextUtils;
    import android.util.Log;
    
    import java.util.regex.Matcher;
    
    public class MyContentProvider extends ContentProvider {
    
        private final String TAG = MyContentProvider.class.getSimpleName();
    
        /**
         * 定义UriMatcher
         */
        private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    
        // 授权 抽取出来定义
        private static final String authority = "autho.prov.cp.MyContentProvider";
    
        /**
         * 定义给其他应用程序访问的路径
         */
        private static final String CAT = "cat"; // 不指定ID
        private static final String CAT_ID = "cat/#"; // 指定ID
    
        /**
         * 定义Code,此Code是给当前内容提供者进行switch判断使用的
         */
        private static final int CAT_CODE = 100;
        private static final int CAT_ID_CODE = 200;
    
        /**
         * 在静态代码块中添加 内容提供者(ContentProvider) 指定好的Uri
         */
        static {
            /**
             * 参数一:授权 需和AndroidManifest.xml 对外暴露的授权一致
             * 参数二:路径 应该来说是拼接的路径,例如在Web  www.baidu.com/路径 -- www.baidu.com/login
             */
            uriMatcher.addURI(authority, CAT, CAT_CODE);  // 非ID操作
            uriMatcher.addURI(authority, CAT_ID, CAT_ID_CODE); // 指定ID操作
        }
    
        /**
         * 初始化数据库错误的示范: (不仅仅是在ContentProvider不能这样,在其他的组件也不能这样)
         * MySqliteOpenHeper.getInstance(getContext()); 还没有执行 onCreate 是没有getContext的,会报错
         * MySqliteOpenHeper.getInstance(this);  还没有执行 onCreate初始化 是没有getContext的,会报错
         */
        // private MySqliteOpenHeper mySqliteOpenHeper = MySqliteOpenHeper.getInstance(getContext());
    
        private MySqliteOpenHeper mySqliteOpenHeper;
    
        /**
         * 只要在AndroidManifest.xml中配置了provider组件
         * 应用打开后,会自动启动此方法
         * @return
         */
        @Override
        public boolean onCreate() {
            Log.d(TAG, "onCreate()");
            mySqliteOpenHeper = MySqliteOpenHeper.getInstance(getContext());
            return false;
        }
    
        /**
         * 查询
         * @param uri 其他应用传递过来的Uri
         * @param projection 其他应用传递过来的查询列
         * @param selection 其他应用传递过来的查询条件
         * @param selectionArgs 其他应用传递过来的查询条件参数值
         * @param sortOrder 其他应用传递过来的排序
         * @return
         */
        @Override
        public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
    
            SQLiteDatabase db = mySqliteOpenHeper.getReadableDatabase();
            Cursor cursor = null;
    
            /**
             * uriMatcher.match(uri); 匹配Uri
             * 匹配其他应用传递过来的Uri,与我内容提供者addURI添加好的Uir进行判断
             * 判断到 如果 传递过来的Uir 和我addURI添加好的Uir一致才做处理
             */
            int code = uriMatcher.match(uri);
    
            switch (code) {
                case CAT_CODE:
                    /**
                     * 查询全部
                     */
                    cursor = db.query("cat", // 表名
                            projection, // 查询的列
                            null,   // selection 查询的条件 xxx=?
                            null, // selectionArgs 查询条件的值
                            null, // groupBy 分组
                            null, // having 分组过滤条件
                            "_id desc"); // orderBy 排序 --> 倒序
                    break;
                case CAT_ID_CODE:
                    /**
                     * ID查询
                     * 判断其他应用程序,传递过来的查询条件,是否为空
                     *                                          如果为空:就获取Uri携带的ID
                     *                                          如果不空: 就在查询条件后面加 and _id =
                     * ContentUris.parseId(uri) 解析Uri携带过来的ID
                     */
                    if (TextUtils.isEmpty(selection)) {
                        selection = " _id = " + ContentUris.parseId(uri);
                    } else {
                        selection = selection + " and _id = " + ContentUris.parseId(uri);
                    }
                    cursor = db.query("cat", projection, selection, selectionArgs, null, null, "_id desc");
                    break;
            }
    
            /**
             * 在内容提供者里面,千万不能关闭数据库,关闭游标
             */
    
            return cursor;
        }
    
        /**
         * 增加
         * @param uri 其他应用传递过来的Uri
         * @param values  其他应用传递过来的ContentValues
         * @return
         */
        @Override
        public Uri insert(Uri uri, ContentValues values) {
            SQLiteDatabase database = mySqliteOpenHeper.getWritableDatabase();
    
            /**
             * uriMatcher.match(uri); 匹配Uri
             * 匹配其他应用传递过来的Uri,与我内容提供者addURI添加好的Uir进行判断
             * 判断到 如果 传递过来的Uir 和我addURI添加好的Uir一致才做处理
             */
            int code = uriMatcher.match(uri);
    
            if (code == CAT_CODE) { // 插入数据  不需要 CAT_ID_CODE,只需CAT_CODE即可
                // 参数一:表名   参数二:其他应用传递过来的ContentValues
                long resultID = database.insert("cat", null, values);
    
    
                /**
                 * 可以返回Uri,返回的Uri给其他应用,其他应用程序就知道,insert的结果详情
                 * 既然要把insert详情,就需要加入插入的受影响行数
                 */
                uri = ContentUris.withAppendedId(uri, resultID);
            } else if (code == CAT_ID_CODE){
            }
    
            /**
             * 在内容提供者里面,千万不能关闭数据库,关闭游标
             */
            return uri;
        }
    
        /**
         * 修改
         * @param uri 其他应用传递过来的Uri
         * @param values 其他应用传递过来的ContentValues
         * @param selection 其他应用传递过来的查询条件
         * @param selectionArgs 其他应用传递过来的查询条件参数值
         * @return
         */
        @Override
        public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
            SQLiteDatabase database = mySqliteOpenHeper.getWritableDatabase();
    
            int updateResult = 0;
    
            /**
             * uriMatcher.match(uri); 匹配Uri
             * 匹配其他应用传递过来的Uri,与我内容提供者addURI添加好的Uir进行判断
             * 判断到 如果 传递过来的Uir 和我addURI添加好的Uir一致才做处理
             */
            int code = uriMatcher.match(uri);
    
            switch (code) {
                case CAT_CODE:
                    break;
                case CAT_ID_CODE: // ID 为条件的的修改方式
                    /**
                     * 判断其他应用程序,传递过来的查询条件,是否为空
                     *                                          如果为空:就获取Uri携带的ID
                     *                                          如果不空: 就在查询条件后面加 and _id =
                     * ContentUris.parseId(uri) 解析Uri携带过来的ID
                     */
                    if (TextUtils.isEmpty(selection)) {
                        selection = " _id = " + ContentUris.parseId(uri);
                    } else {
                        selection = selection + " and _id = " + ContentUris.parseId(uri);
                    }
    
                    // 参数一:表名   参数二:其他应用传递过来的ContentValues   参数三:其他应用传递过来的查询条件
                    updateResult = database.update("cat", values, selection, selectionArgs);
                    break;
            }
    
            /**
             * 在内容提供者里面,千万不能关闭数据库,关闭游标
             */
            return updateResult;
        }
    
        /**
         * 删除
         * @param uri 其他应用传递过来的Uri
         * @param selection 其他应用传递过来的查询条件
         * @param selectionArgs 其他应用传递过来的查询条件参数值
         * @return
         */
        @Override
        public int delete(Uri uri, String selection, String[] selectionArgs) {
            SQLiteDatabase database = mySqliteOpenHeper.getWritableDatabase();
    
            int deleteResult = 0;
    
            /**
             * uriMatcher.match(uri); 匹配Uri
             * 匹配其他应用传递过来的Uri,与我内容提供者addURI添加好的Uir进行判断
             * 判断到 如果 传递过来的Uir 和我addURI添加好的Uir一致才做处理
             */
            int code = uriMatcher.match(uri);
    
            switch (code) {
                case CAT_CODE:
                    break;
                case CAT_ID_CODE: // ID 为条件的的删除方式
                    /**
                     * 判断其他应用程序,传递过来的查询条件,是否为空
                     *                                          如果为空:就获取Uri携带的ID
                     *                                          如果不空: 就在查询条件后面加 and _id =
                     * ContentUris.parseId(uri) 解析Uri携带过来的ID
                     */
                    if (TextUtils.isEmpty(selection)) {
                        selection = " _id = " + ContentUris.parseId(uri);
                    } else {
                        selection = selection + " and _id = " + ContentUris.parseId(uri);
                    }
    
                    // 参数一:表名   参数二:其他应用传递过来的查询条件   参数三:其他应用传递过来的查询条件的值
                    deleteResult = database.delete("cat", selection, selectionArgs);
                    break;
            }
    
            /**
             * 在内容提供者里面,千万不能关闭数据库,关闭游标
             */
            return deleteResult;
        }
    
    
        /**
         * 得到类型 在后续的博客中会有讲解到
         * @param uri
         * @return
         */
        @Override
        public String getType(Uri uri) {
            return null;
        }
    }

    ContentProviderServer应用-->定义 在AndroidManifest.xml 中 对外暴露 MyContentProvider 

        <!--
                ContentProvider是组件需要配置
                可以把ContentProvider看作是服务器
                authorities 看作是服务器 服务器有访问的链接,authorities(授权) ,是唯一标识
                android:enabled="true" 可以被系统实例化
                android:exported="true" 允许对外输出
            -->
            <provider
                android:authorities="autho.prov.cp.MyContentProvider"
                android:name=".cp.MyContentProvider"
                android:enabled="true"
                android:exported="true"
                />

    然后运行 ContentProviderServer应用:由于在AndroidManifest.xml中配置了MyContentProvider组件,只要运行 ContentProviderServer应用,就会自动初始化onCreate()方法

    12-14 09:22:55.187 2013-2013/liudeli.cp.server D/MyContentProvider: onCreate()


    ContentProviderClient应用 --> MainActivity中调用 ContentProviderServer应用的内容提供者

    package liudeli.cp.client;
    
    import android.content.ContentResolver;
    import android.content.ContentUris;
    import android.content.ContentValues;
    import android.database.Cursor;
    import android.net.Uri;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.text.TextUtils;
    import android.view.View;
    import android.widget.CursorAdapter;
    import android.widget.EditText;
    import android.widget.ListView;
    import android.widget.SimpleCursorAdapter;
    import android.widget.Toast;
    import liudeli.cp.client.adapter.MyCursorAdapter;
    
    public class MainActivity extends AppCompatActivity {
    
        private EditText etID;
        private ContentResolver contentResolver;
        private ListView listview;
    
        private Uri uri;
        private Uri uriId;
        private Cursor cursor;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            etID = findViewById(R.id.et_id);
            contentResolver = getContentResolver();
            listview = findViewById(R.id.listview);
    
            /**
             * 需要在授权码增加,由于 ContentProviderServer应用通过UriMatcher暴露了 /cat  /cat/#
             * 所以需要在授权码后面拼接
             *
             * /cat == CAT_CODE == [全部查询, , 删除]
             *
             * /cat == CAT_CODE_ID == [ID查询,修改]
             */
            uri = Uri.parse("content://autho.prov.cp.MyContentProvider/cat");
            uriId = Uri.parse("content://autho.prov.cp.MyContentProvider/cat/#");
        }
    
        /**
         * 测试的方法
         * @param view
         */
        public void test(View view) {
            /**
             * 可以想象客户端访问服务器,需要需要用到协议HTTP
             * 而想访问ContentProvider,需要ContentResolver
             */
            ContentResolver contentProvider = getContentResolver();
    
            /**
             * 可以想象访问服务器,需要这样拼接访问地址 http://
             * 而想访问ContentProvider,需要这样拼接访问地址 content://
             * 必须拿到暴露的授权authorities="autho.prov.cp.MyContentProvider" 进行拼接
             */
            Uri uri = Uri.parse("content://autho.prov.cp.MyContentProvider");
    
            // 查询
            contentProvider.query(uri, null, null, null, null, null);
    
            // 增加
            // contentProvider.insert(uri, null);
    
            // 修改
            // contentProvider.update(uri, null, null, null);
    
            // 删除
            // contentProvider.delete(uri, null, null);
        }
    
        /**
         * 查询
         */
        public void query(View view) {
            cursor = contentResolver.query(uri,
                    new String[]{"_id", "name", "age"},
                    null, null
                    , null, null);
    
            /**
             * 使用SimpleCursorAdapter 适配器
             */
            SimpleCursorAdapter adapter = new
                    SimpleCursorAdapter(MainActivity.this, // 上下文
                    R.layout.layout_item, // Item布局
                    cursor, // Cursor 查询出来的游标 这里面有数据库里面的数据
                    new String[]{"_id", "name", "age"}, // 从哪里来,指的是 查询出数据库列名
                    new int[]{R.id.tv_id, R.id.tv_name, R.id.tv_age}, // 到哪里去,指的是,把查询出来的数据,赋值给Item布局 的控件
                    SimpleCursorAdapter.NO_SELECTION);
    
            // 给ListView设置适配器
            listview.setAdapter(adapter);
    
            // 注意:在数据展示完成后,不要关闭游标, 在Activity结束后在关闭cursor.close();
        }
    
        /**
         * ID查询单个
         * @param view
         */
        public void queryId(View view) {
    
            if (TextUtils.isEmpty(etID.getText().toString())) {
                Toast.makeText(MainActivity.this, "请输入ID", Toast.LENGTH_LONG).show();
                return;
            }
    
            Uri uriID = null;
    
            /**
             * 需要把ID带过去 uriId = ContentUris.withAppendedId(uri, Long.parseLong(etID.getText().toString()));
             * ContentUris.withAppendedId 写入ID到Uri
             * ContentUris.parseId() 解析Uri里面的携带的ID
             */
            uriID = ContentUris.withAppendedId(uriId, Long.parseLong(etID.getText().toString()));
            cursor = contentResolver.query(uriID,
                    new String[]{"_id", "name", "age"},
                    null, null
                    , null, null);
    
            /**
             * 使用CursorAdapter 适配器
             */
            CursorAdapter adapter = new MyCursorAdapter(this, cursor, CursorAdapter.IGNORE_ITEM_VIEW_TYPE);
    
            // 给ListView设置适配器
            listview.setAdapter(adapter);
    
            // 注意:在数据展示完成后,不要关闭游标, 在Activity结束后在关闭cursor.close();
        }
    
        /**
         * 增加
         */
        public void insert(View view) {
            if (TextUtils.isEmpty(etID.getText().toString())) {
                Toast.makeText(MainActivity.this, "请输入ID", Toast.LENGTH_LONG).show();
                return;
            }
            ContentValues vs = new ContentValues();
            vs.put("name", "刘新龙" + etID.getText().toString());
            vs.put("age", 90);
            contentResolver.insert(uri, vs);
    
            // 规范写法应该是:simpleCursorAdapter.notifyDataSetChanged();
            // 我这里为了测试下,就直接这样掉算了
            query(null);
        }
    
        /**
         * 修改
         * @param view
         */
        public void update(View view) {
            if (TextUtils.isEmpty(etID.getText().toString())) {
                Toast.makeText(MainActivity.this, "请输入ID", Toast.LENGTH_LONG).show();
                return;
            }
    
            // 以前的方式
            /*ContentValues vs = new ContentValues();
            vs.put("name", "王二麻子" + etID.getText().toString());
            vs.put("age", 78);
            contentResolver.update(uri, vs, "_id = ?", new String[]{etID.getText().toString()});*/
    
            /**
             * 需要把ID带过去 uriId = ContentUris.withAppendedId(uri, Long.parseLong(etID.getText().toString()));
             * ContentUris.withAppendedId 写入ID到Uri
             * ContentUris.parseId() 解析Uri里面的携带的ID
             */
            Uri uriID = ContentUris.withAppendedId(uriId, Long.parseLong(etID.getText().toString()));
    
            // 现在的写法
            ContentValues vs = new ContentValues();
            vs.put("name", "大扛粑子" + etID.getText().toString());
            vs.put("age", 78);
            // contentResolver.update(uri, vs, "_id = ?", new String[]{etID.getText().toString()});
            contentResolver.update(uriID, vs, null, null);
    
            // 规范写法应该是:simpleCursorAdapter.notifyDataSetChanged();
            // 我这里为了测试下,就直接这样掉算了
            query(null);
        }
    
        /**
         * 删除
         *
         * @param view
         */
        public void delete(View view) {
            if (TextUtils.isEmpty(etID.getText().toString())) {
                Toast.makeText(MainActivity.this, "请输入ID", Toast.LENGTH_LONG).show();
                return;
            }
    
            /**
             * 需要把ID带过去 uriId = ContentUris.withAppendedId(uri, Long.parseLong(etID.getText().toString()));
             * ContentUris.withAppendedId 写入ID到Uri
             * ContentUris.parseId() 解析Uri里面的携带的ID
             */
            Uri uriID = ContentUris.withAppendedId(uriId, Long.parseLong(etID.getText().toString()));
    
            contentResolver.delete(uriID, "_id = ?", new String[]{etID.getText().toString()});
    
            // 规范写法应该是:simpleCursorAdapter.notifyDataSetChanged();
            // 我这里为了测试下,就直接这样掉算了
            query(null);
        }
    
        /**
         * 在Activity结束后在关闭cursor.close();
         */
        @Override
        protected void onDestroy() {
            super.onDestroy();
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    ContentProviderClient应用 使用到的CursorAdapter:

    package liudeli.cp.client.adapter;
    
    import android.app.LoaderManager;
    import android.content.Context;
    import android.content.CursorLoader;
    import android.database.Cursor;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.CursorAdapter;
    import android.widget.TextView;
    
    import liudeli.cp.client.R;
    
    public class MyCursorAdapter extends CursorAdapter {
    
        // 定义布局加载器
        private LayoutInflater layoutInflater;
    
        /**
         * 构造方法
         * @param context 传入上下文
         * @param c 传入Cursor游标
         * @param flags 传入标记
         */
        public MyCursorAdapter(Context context, Cursor c, int flags) {
    
            /**
             * 主要把这些值传递给父类的构造方法,就会自动的传递到 newView(Context context, Cursor cursor, ViewGroup parent)
             *                                              bindView(View view, Context context, Cursor cursor)
             */
            super(context, c, flags);
    
            layoutInflater = LayoutInflater.from(context);
        }
    
        /**
         * Item布局文件的处理
         * @param context 传入上下文
         * @param cursor
         * @param parent
         * @return
         */
        @Override
        public View newView(Context context, Cursor cursor, ViewGroup parent) {
            // Item布局文件,要显示的Item,在这里处理
            View view = layoutInflater.inflate(R.layout.layout_item, null);
            return view;
        }
    
        /**
         * 把Cursor获取的数据和布局文件进行绑定
         * @param view 此view 是上面 newView方法返回的View
         * @param context 上下文
         * @param cursor 游标
         */
        @Override
        public void bindView(View view, Context context, Cursor cursor) {
            // 获取到布局的控件
            TextView tvId =  view.findViewById(R.id.tv_id);
            TextView tvName = view.findViewById(R.id.tv_name);
            TextView tvAge = view.findViewById(R.id.tv_age);
    
            // 获取Cursor里面的数据
            int _id = cursor.getInt(cursor.getColumnIndex("_id"));
            String name = cursor.getString(cursor.getColumnIndex("name"));
            int age = cursor.getInt(cursor.getColumnIndex("age"));
    
            // 把数据绑定到控件里面去
            tvId.setText(_id + ""); // setText(数据必须是字符串);
            tvName.setText(name); // setText(数据必须是字符串);
            tvAge.setText(age + ""); // setText(数据必须是字符串);
        }
    }

    ContentProviderClient应用 --> 布局文件

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        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">
    
        <LinearLayout
            android:id="@+id/ll_buttons"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
    
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="test"
                android:onClick="test"
                android:layout_weight="1"
    
                android:visibility="gone"
                />
    
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="查询"
                android:layout_weight="1"
                android:onClick="query"
                />
    
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="ID查询"
                android:layout_weight="1"
                android:onClick="queryId"
                />
    
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="增加"
                android:layout_weight="1"
                android:onClick="insert"
                />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="修改"
                android:layout_weight="1"
                android:onClick="update"
                />
    
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="删除"
                android:layout_weight="1"
                android:onClick="delete"
                />
    
        </LinearLayout>
    
        <LinearLayout
            android:id="@+id/ll_et_id"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/ll_buttons"
            android:layout_marginTop="10dp"
            android:paddingLeft="10dp"
            android:paddingRight="10dp">
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="请输入_id号"
                android:textSize="20sp"
                />
    
            <EditText
                android:id="@+id/et_id"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:background="@android:color/white"
                android:layout_marginLeft="10dp"
                />
    
        </LinearLayout>
    
        <ListView
            android:id="@+id/listview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@id/ll_et_id"
            android:layout_marginTop="20dp"></ListView>
    
    </RelativeLayout>

    ContentProviderClient应用 --> 布局文件 -->ListView--> Item布局文件

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp">
    
        <TextView
            android:id="@+id/tv_id"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="20sp"
            android:text="id"
            android:textColor="@android:color/black"
            />
    
        <TextView
            android:id="@+id/tv_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="20sp"
            android:text="name"
            android:textColor="@android:color/black"
            android:layout_marginTop="5dp"
            />
    
        <TextView
            android:id="@+id/tv_age"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="20sp"
            android:text="age"
            android:textColor="@android:color/black"
            android:layout_marginTop="5dp"
            />
    
    
    </LinearLayout>

    ContentProviderClient应用  操作 --->  ContentProviderServer应用的数据库

  • 相关阅读:
    8.ffmpeg-基础常用知识
    7.SwrContext音频重采样使用
    6.AVCodecContext和AVCodec
    5.AVStream和AVCodecParameters
    Discuz论坛禁止匿名发贴,却出现匿名发帖或回复?找不到发帖用户,DZ如何禁止匿名发帖修改教程
    在独立服务器上通过Ubuntu 18.04+apache2+php5.6+mysql5.7+discuz!x3.4搭建的论坛实现伪静态的正确方法
    [源码] 2200套微信小程序源码
    discuz论坛更换目录后出现头像无法显示/ucenter无法进入
    discuz 您的服务器不支持 CURL,这将会导致应用无法安装。请联系您的服务商或者网站技术人员
    Discuz在服务器配置安装时出现xml_parser_create()不支持
  • 原文地址:https://www.cnblogs.com/android-deli/p/10123815.html
Copyright © 2011-2022 走看看