文章目录

  • 6.1 ContentProvider 概述
    • 6.1.1 数据模型
    • 6.1.2 URI 的用法
  • 6.2 预定义 Content Provider
    • 6.2.1查询数据
    • 6.2.2 增加记录
    • 6.2.3 增加新值
    • 6.2.4 批量更新记录
    • 6.2.5 删除记录
    • 6.2.6 案例 1:查询联系人ID和姓名
  • 6.3 自定义 Content Provider
    • 6.3.1 继承ContentProvider 类
    • 6.3.2 声明Content Provider
  • 6.4 应用实例
    • 6.4.1 查询联系人姓名和电话
    • 6.4.2 自动补全联系人姓名
  • 练习6

6.1 ContentProvider 概述

Content Provider 用于保存和获取数据,并使其对所有应用程序可见。这是不同应用程序间共享数据的唯一方式,因为在Android中没有提供所有应用共同访问的公共存储区域。
Content Provider内部如何保存数据由其设计者决定,但是所有的Content Provider都实现一组通用的方法, 用来提供数据的增、删、改、查功能。
客户端通常不会直接使用这些方法,大多数是通过ContentResolver对象实现对Content Provider的操作。开发
人员可以通过调用Activity或者其他应用程序组件的实现类中getContentResolver()方法来获得ContentProvider 对象,例如:

ContentResolver cr = getContentResolver();

使用ContentResolver提供的方法可以获得Content Provider中任何感兴趣的数据。
当开始查询时,Android 系统确认查询的目标Content Provider并确保它正在运行。系统会初始化所有ContentProvider类的对象,开发人员不必完成此类操作,实际上,开发人员根本不会直接使用ContentProvider类的对象。通常,每个类型的ContentProvider仅有一个单独的实例。但是该实例能与位于不同应用程序和进程的多个ContentResolver类对象通信。不同进程之间的通信由ContentProvider类和ContentResolver类处理。

6.1.1 数据模型

Content Provider使用基于数据库模型的简单表格来提供其中的数据,这里每行代表一条记录, 每列代表特定类型和含义的数据。例如,联系人的信息可能以如下的方式提供。

每条记录包含一个数值型的. JID字段,用于在表格中唯一标识该记录。 ID 能用于匹配相关表格中的记录,例如,在一个表格中查询联系人的电话,在另一表格中查询其照片。

查询返回一个Cursor对象,它能遍历各行各列来读取各个字段的值。对于各个类型的数据,它都提供了专用的方法。因此,为了读取字段的数据,开发人员必须知道当前字段包含的数据类型。

6.1.2 URI 的用法

每个Content Provider 提供公共的URI (使用Uri类包装)来唯一标识其数据集。 管理多个数据集(多个表格)的Content Provider为每个数据集提供了单独的URI。所有为provider提供的URI都以"content//"作为前缀,“content://"模 式表示数据由Content Provider来管理.

如果自定义Content Provider,则应该为其URI也定义一个常量,来简化客户端代码并让日后更新更加简洁。Android为当前平台提供的Content Provider定义了CONTENT_ URI 常量。例如,匹配电话号码到联系人表格的URI和匹配保存联系人照片表格的URI分别如下:

android.provider.Contacts.Phones.CONTENT_ _URI
android.provider.Contacts.Photos.CONTENT_ _URI

URI常量用于所有与Content Provider的交互中。每个ContentResolver方法使用URI作为其第一个参数。 它标识ContentResolver应该使用哪个provider及其中的哪个表格.

[V] A:标准的前缀,用于标识该数据由Content Provider管理,不需修改。
[V] B: URI的authority部分,用于标识该Content Provider。对于第三方应用,该部分应该是完整的类名(使用小写形式)来保证唯一性。 在 元素的authorities属性中声明authority。
[V] C: Content Provider的路径部分,用于决定哪类数据被请求。如果Content Provider仅提供一种数据类型,可以省略该部分;如果provider提供几种类型,包括子类型,这部分可以由几部分组成。
[V] D:被请求的特定记录的ID值。这是被请求记录的_ ID值。如果请求不仅限于单条记录,该部分及其前面的斜线应该删除。

6.2 预定义 Content Provider

Android系统为常用数据类型提供了很多预定义的Content Provider (声音、视频、图片、联系人等),它们大多位于android.provider包中。开发人员可以查询这些provider以获得其中包含的信息(尽管有些需要适当的权限来读取数据)。 Android 系统提供的常见Content Provider说明如下。

[V] Browser: 读取或修改书签、浏览历史或网络搜索。
[V] CalLog: 查看或更新通话历史。
[V] Contacts: 获取、修改或保存联系人信息。
[V] LiveFolders: 由Content Provider提供内容的特定文件夹。
[V] MediaStore: 访问声音、视频和图片。
[V] Setting: 查看和获取蓝牙设置、铃声和其他设备偏好。
[V] SyncStateContract: 用于使用数据数组账号关联数据的ContentProvider约束。希望使用标准方式保存数据的provider时可以使用。
[V] UserDictionary: 在可预测文本输入时,提供用户定义单词给输入法使用。应用程序和输入法能增加数据到该字典。单词能关联频率信息和本地化信息。

6.2.1查询数据

要查询Content Provider中的数据,需要以下3个信息:
[V]标识该Content Provider的URI.
[V]需要查询的数据字段名称。
[V]字段中数据的类型。

如果查询特定的记录,则还需要提供该记录的ID值。
为了查询Content Provider中的数据,开发人员需要使用ContentResolver.query()或Activity.managedQuery()方法。这两个方法使用相同的参数,并且都返回Cursor对象。但是managedQuery()方法导致Activity管理Cursor的生命周期。托管的Cursor处理所有的细节,如当Activity 暂停时卸载自身,当Activity重启时加载自身。调用Activity.startManagingCursor()方法可以让Activity管理未托管的Cursor对象
query0和managedQuery0方法的第一-个参 数是provider的URI,即标识特定ContentProvider和数据集的CONTENT_ URI常量。
为了限制仅返回一条记录,可以在URI结尾增加该记录的_ ID 值,即将匹配ID值的字符串作为URI路径部分的结尾片段。例如,ID值是10, URI将是:

content://.. ./10

有些辅助方法,特别是ContentUrs.withAppendedId()和Uri.withAppendedPath()方法,能轻松地将ID增加到URI。这两个方法都是静态方法,并返回一个增加了ID的Uri对象。

query()和managedQuery()方法的其他参数用来更加细致地限制查询结果,它们是:
[V]应该返回的数据列名称。null 值表示返回全部列;否则,仅返回列出的列。全部预定义Content Provider为其列都定义了常量。例如,android.provider.Contacts.Phones类定义了_ ID、NUMBER、
NUMBER_ KEY、NAME等常量。

[V]决定哪些行被返回的过滤器,格式类似SQL的WHERE语句(但是不包含WHERE自身)。null值表
示返回全部行(除非URI限制查询结果为单行记录)。
[V]选择参数。
[V]返回记录的排序器,格式类似SQL的ORDER BY语句(但是不包含ORDERBY自身)。null 值表示以默认顺序返回记录,这可能是无序的。

查询返回一组0条或多条数据库记录。列名、默认顺序和数据类型对每个Content Provider都是特别的。但是每个provider都有一个_ ID 列,它为每条记录保存唯一的数值 ID。每个provider也能使用_ COUNT报告返回结果中记录的行数,该值在各行都是相同的。
获得数据使用Cursor对象处理,它能向前或向后遍历整个结果集。开发人员可以使用Cursor对象来读取数据,而增加、修改和删除数据则必须使用ContentResolver对象。

6.2.2 增加记录

为了向Content Provider中增加新数据,首先需要在ContentValues对象中建立键值对映射,这里每个键匹配Content Provider中列名,每个值是该列中希望增加的值。然后调用ContentResolver. insert()方法并传递给它provider的URI参数和ContentValues映射。该方法返回新记录的完整URI,即增加了新记录ID的URI。开发人员可以使用该URI来查询并获取该记录的Cursor,以便修改该记录。

6.2.3 增加新值

一旦记录存在,开发人员可以向其中增加新信息或者修改已经存在的信息。增加记录到Contacts数据库的最佳方式是增加保存新数据的表名到代表记录的URI,然后使用组装好的URI来增加新数据。每个Contacts表格以CONTENT_ DIRECTORY常量的方式提供名称。
开发人员可以调用使用byte数组作为参数的ContentV alues.put()方法向表格中增加少量二进制数据,这适用于类似小图标的图片、短音频片段等。然而,如果需要增加大量二进制数据,如图片或者完整的歌曲等,则需要保存代表数据content:URI 到表格,然后使用文件URI调用ContentResolver. openOutputStream()方法。这导致ContentProvider保存數据到文件并在记录的隐藏字段保存文件路径。

6.2.4 批量更新记录

要批量更新数据(例如,将全部字段中“NY”替换成“New York"),可使用ContentResolver.update()方法并提供需要修改的列名和值。

6.2.5 删除记录

如果需要删除单条记录,可调用ContentResolver.delete()方法并提供特定行的URI。
如果需要删除多条记录,可调用 ContentResolver. delete()方法并提供删除记录类型的URI (如android.provider.Contacts.People.CONTENT URI)和一个SQL WHERE语句,它定义哪些行需要删除。
注意:请确保提供 了一个合适的WHERE语句,否则可能删除全部数据。

6.2.6 案例 1:查询联系人ID和姓名

main.xml

<?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="match_parent"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/result"android:textColor="@android:color/black"android:textSize="25dp"/>
</LinearLayout>

MainActivity.java

package com.jingyi.dataproj;import androidx.appcompat.app.AppCompatActivity;import android.content.ContentResolver;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.Contacts;
import android.provider.ContactsContract;
import android.widget.TextView;public class MainActivity extends AppCompatActivity {private String[] columns={ContactsContract.Contacts._ID,//ID值ContactsContract.Contacts.DISPLAY_NAME,//姓名};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);TextView textView = (TextView) findViewById(R.id.result);textView.setText(getQueryData());}private String getQueryData() {StringBuilder stringBuilder = new StringBuilder();ContentResolver contentResolver = getContentResolver();Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, columns, null, null, null);//查询记录int idIndex = cursor.getColumnIndex(columns[0]);//获取ID记录的索引值int nameIndex = cursor.getColumnIndex(columns[1]);//姓名记录的索引for (cursor.moveToFirst();!cursor.isAfterLast();cursor.moveToNext()){int id = cursor.getInt(idIndex);String name = cursor.getString(nameIndex);stringBuilder.append("id:"+id+","+"name:"+name);}cursor.close();//关闭Cursorreturn  stringBuilder.toString();//返回查询结果}
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.jingyi.dataproj"><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.Application"><activityandroid:name=".MainActivity"android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application><!--读取联系人记录权限--><uses-permission android:name="android.permission.READ_CONTACTS"/>
</manifest>

6.3 自定义 Content Provider

如果开发人员希望共享自己的数据,则有以下两个选择:
[V]创建自定义的Content Provider (一 个ContentProvider类的子类)。
[V] 如果有预定义的provider, 管理相同的数据类型并且有写入权限,则可以向其中增加数据。前面已经详细介绍了如何使用系统预定义的Content Provider,下 面将介绍如何自定义Content Provider。如果自定义Content Provider,开发人员需要完成以下操作:
[V]建立数据存储系统。大多数Content Provider使用Android文件存储方法或者SQLite数据库保存数据,但是开发人员可以使用任何方式存储。Android 提供了SQLiteOpenHelper 类帮助创建数据库,SQLiteDatabase类帮助管理数据库。
[V]继承ContentProvider类来提供数据访问方式
[V]在应用程序的AndroidManifest文件中声明Content Provider.
下面主要介绍继承ContentProvider类和声明Content Provider的操作。

6.3.1 继承ContentProvider 类

开发人员定义ContentProvider类的子类,以便使用ContentResolver和Cursor类来共享数据。原则上,这意味着需要实现ContentProvider类定义的6个抽象方法,其语法格式如下:

public boolean onCreate()
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortorder)
public Uri insert(Uri uri, ContentValues values)
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
public int delete(Uri uri, String selection, String[] selectionArgs)
public String getType(Uri uri)

query0方法必须返回Cursor对象,用于遍历查询结果。Cursor 自身是一个接口,Android 提供了该接口的一些实现类,例如,SQLiteCursor 能遍历存储在SQLite 数据库中的数据。通过调用SQLiteDatabase类的query0方法可以获得Cursor对象,它们都位于android.database包中,其继承 关系如图所示。

圆角矩形表示接口,矩形表示类

由于这些ContentProvider方法能被位于不同进程和线程的不同ContentResolver对象调用,它们必须以线程安全的方式实现。
此外,开发人员也可以调用ContentResolver.notifyChange0方法,以便在 数据修改时通知监听器。除了定义子类自身,还应采取一些措施以简化客户端工作并让类更加易用:

1.定义public static final Uri CONTENT_ URI变量(CONTENT URI 是变量名称)。该字符串表示自定义的Content Provider处理的完整content:URI。开发人员必须为该值定义唯一的字符串 。最佳的解决方式是使用ContentProvider的完整类名(小写)。例如,LearmProvider 的URI可能按如下方式定义:

public static final Uri CONTENT_URI= Uri. parse("content://com.android.learnprovider");

如果provider包含子表,也应该为各个子表定义URI。这些URI应该有相同authority ( 因为它标识Content:Provider)使用路径进行区分,例如:

content://com.android.Learnprovider/dba
content://com.android.Learnprovider/programmer
content://com.android.Learnprovider/ceo

2.定义Content Provider将返回给客户端的列名。如果开发人员使用底层数据库,这些列名通常与SQL数据库列名相同。同样,定义public static String常量,客户端用它们来指定查询中的列和其他指令。确保包含名为“ID"的整数列来作为记录的ID值。无论记录中其他字段是否唯一,如URL,开发人员都应该包含该字段。如果打算使用SQLite数据库,_ ID 字段类型如下:

INTEGER PRIMARY KEY AUTOINCR EMENT

3.仔细注释每列的数据类型,客户端需要使用这些信息来读取数据。
4.如果开发人员正在处理新数据类型,则必须定义新的MIME类型,以便在ContentProvider. getType)方法实现中返回。
5.如果开发人员提供的byte 数据太大而不能放到表格中,如bitmap文件,提供给客户端的字段应该包含content:URI字符串。

6.3.2 声明Content Provider

为了让Android系统知道开发人员编写的Content Provider,应该在应用程序的AndroidManifest.xml文件中定义元素。没有在配置文件中声明的自定义Content Provider,对于Android系统不可见。
Name属性的值是ContentProvider类的子类的完整名称; authorities 属性是provider定义的content:URI中authority部分; ContentProvider 的子类是LearnProvider。 元素应该如下:

<provider
android :name="com.android.LearnProvider"
android :authorities="com.android.learnprovider"
...></ provider>

注意: authorities 属性删除了content:URI 中的路径部分。

其他属性能设置读写数据的权限、提供显示给用户的图标或文本、启用或禁用provider等。如果数据不需要在多个运行的Content Provider间同步,则设置multiprocess为true。这允许在各个客户端进程创建一个provider实例,从而避免执行IPC。

6.4 应用实例

6.4.1 查询联系人姓名和电话

main.xml

<?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="match_parent"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/result"android:textColor="@android:color/black"android:textSize="25dp"/>
</LinearLayout>

MainActivity.java

package com.jingyi.dataproj;import androidx.appcompat.app.AppCompatActivity;import android.content.ContentResolver;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.Contacts;
import android.provider.ContactsContract;
import android.widget.TextView;public class MainActivity extends AppCompatActivity {private String[] columns={ContactsContract.Contacts._ID,//ID值ContactsContract.Contacts.DISPLAY_NAME,//姓名ContactsContract.CommonDataKinds.Phone.NUMBER,//电话号码ContactsContract.CommonDataKinds.Phone.CONTACT_ID,};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);TextView textView = (TextView) findViewById(R.id.result);textView.setText(getQueryData());}private String getQueryData() {StringBuilder stringBuilder = new StringBuilder();ContentResolver contentResolver = getContentResolver();Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);//查询记录while(cursor.moveToNext()){int idIndex = cursor.getColumnIndex(columns[0]);//获取ID记录的索引值int nameIndex = cursor.getColumnIndex(columns[1]);//姓名记录的索引int id = cursor.getInt(idIndex);String name = cursor.getString(nameIndex);Cursor phone = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, columns[3] + "=" + id, null, null);while (phone.moveToNext()){int phoneColumnIndex = phone.getColumnIndex(columns[2]);//电话的索引String phoneString = phone.getString(phoneColumnIndex);//电话数据stringBuilder.append("name:"+name+","+"phone:"+phoneString);}}cursor.close();//关闭Cursorreturn  stringBuilder.toString();//返回查询结果}
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.jingyi.dataproj"><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.Application"><activityandroid:name=".MainActivity"android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application><!--读取联系人记录权限--><uses-permission android:name="android.permission.READ_CONTACTS"/>
</manifest>
6.4.2 自动补全联系人姓名

main.xml

<?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="match_parent"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/title"android:layout_gravity="center"android:text="标题"android:textColor="@android:color/black"android:textSize="30dp"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/textView"android:layout_margin="5dp"android:text="姓名"android:textColor="@android:color/black"android:textSize="25dp"/><AutoCompleteTextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:id="@+id/edit"android:completionThreshold="1"android:textColor="@android:color/black"><requestFocus/></AutoCompleteTextView></LinearLayout>
</LinearLayout>

ContactListAdapter.java

public class ContactListAdapter extends CursorAdapter implements Filterable {private ContentResolver resolver;private String[] columns=new String[]{ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME};public  ContactListAdapter(Context context,Cursor con){super(context,con);resolver = context.getContentResolver();}@Overridepublic View newView(Context context, Cursor cursor, ViewGroup parent) {LayoutInflater inflater = LayoutInflater.from(context);TextView view = (TextView) inflater.inflate(android.R.layout.simple_dropdown_item_1line, parent, false);return view;}@Overridepublic void bindView(View view, Context context, Cursor cursor) {((TextView)view).setText(cursor.getString(1));}@Overridepublic CharSequence convertToString(Cursor cursor) {return cursor.getString(1);}@Overridepublic Cursor runQueryOnBackgroundThread(CharSequence constraint) {FilterQueryProvider filter = getFilterQueryProvider();if (filter!=null){return  filter.runQuery(constraint);}Uri uri= Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_FILTER_URI,Uri.encode(constraint.toString()));return resolver.query(uri,columns,null,null,null);}}

MainActivity.java

package com.jingyi.dataproj;import androidx.appcompat.app.AppCompatActivity;import android.content.ContentResolver;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.Contacts;
import android.provider.ContactsContract;
import android.widget.AutoCompleteTextView;
import android.widget.TextView;public class MainActivity extends AppCompatActivity {private String[] columns={ContactsContract.Contacts._ID,//ID值ContactsContract.Contacts.DISPLAY_NAME,//姓名};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);ContentResolver resolver = getContentResolver();Cursor cursor = resolver.query(ContactsContract.Contacts.CONTENT_URI, columns, null, null, null);ContactListAdapter adapter = new ContactListAdapter(this, cursor);AutoCompleteTextView autoCompleteTextView = (AutoCompleteTextView) findViewById(R.id.edit);autoCompleteTextView.setAdapter(adapter);}}

练习6

登录后主界面列表选择第五个选项后,
跳转到另一个Activity显示记账本,其含一个 记账项输入、金额输入、添加按钮和一个列表,添加记账功能往自定义Content Provider写入,数据可以存储在自定义数据库,或者xml文件里,或者最简单的存在列表变量里面;列表显示从Content Provider查询历史记账信息,如果有兴趣可以增加搜索统计功能。

build.gradle

dependencies {implementation 'com.hjq:titlebar:5.0'
}repositories {google()mavenCentral()// JitPack 远程仓库:https://jitpack.iomaven { url 'https://jitpack.io' }jcenter() // Warning: this repository is going to shut down soon
}

list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="horizontal"android:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid:id="@+id/tv_type"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="10dp"android:gravity="center"android:textSize="15dp"android:text="出行"/><TextViewandroid:id="@+id/tv_date"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="10dp"android:gravity="center"android:textSize="15dp"android:text="2015-09-07"/><TextViewandroid:id="@+id/tv_title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="10dp"android:gravity="center"android:textSize="15dp"android:text="朋友聚餐"/><TextViewandroid:id="@+id/tv_cost_type"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="80dp"android:gravity="center"android:textSize="17dp"android:text="出:"/><TextViewandroid:id="@+id/tv_money"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="5dp"android:gravity="center"android:text="50"android:textSize="17dp" /></LinearLayout>

create_cos_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/tablelayout"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:gravity="center_vertical"android:stretchColumns="0,3"><TextView/><TableRowandroid:id="@+id/tableRow01"android:layout_width="wrap_content"android:layout_height="wrap_content"><TextView/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="消费类型"/><Spinnerandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/sp_type"android:entries="@array/types"/><TextView/></TableRow><TableRowandroid:id="@+id/tableRow02"android:layout_width="wrap_content"android:layout_height="wrap_content"><TextView/><TextViewandroid:text="消费详情"android:id="@+id/textView2"android:layout_height="wrap_content"/><EditTextandroid:id="@+id/ed_title"android:hint="请输入您的消费详情"android:layout_height="wrap_content"android:layout_width="wrap_content"/><TextView/></TableRow><TableRowandroid:id="@+id/tableRow03"android:layout_width="wrap_content"android:layout_height="wrap_content"><TextView/><Spinnerandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/sp_cos_type"android:entries="@array/cosType"/><EditTextandroid:id="@+id/ed_money"android:hint="¥消费金额"android:layout_height="wrap_content"android:layout_width="wrap_content"/><TextView/></TableRow><DatePickerandroid:id="@+id/dp_cost_date"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="8dp"android:datePickerMode="spinner"android:calendarViewShown="false"/><Buttonandroid:id="@+id/submitBtn"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="确认"android:textSize="20dp"android:textColor="#fff"android:background="#ffbd27"android:layout_marginLeft="40dp"android:layout_marginRight="40dp"/><TextView/>
</TableLayout>

arrays.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="names"><item>图片管理</item><item>手机设置</item><item>网络监控</item><item>手机设置</item><item>记账本</item>
</string-array>
<string-array name="types"><item>餐饮</item><item>交通</item><item>服饰</item><item>购物</item><item>服务</item><item>教育</item><item>娱乐</item><item>运动</item><item>生活缴费</item><item>旅行</item><item>宠物</item><item>医疗</item><item>转账</item><item>其他</item>
</string-array>
<string-array name="cosType"><item>出</item><item>入</item>
</string-array>
</resources>

keeping.xml

<?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"android:layout_width="match_parent"android:layout_height="match_parent"><com.hjq.bar.TitleBarandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="20dp"android:background="@drawable/bg_titlebar"app:barStyle="transparent"app:leftTitle="返回"app:title="记账本"app:titleSize="17dp"android:id="@+id/titleBar"/><ListViewandroid:id="@+id/listView"android:layout_width="wrap_content"android:layout_height="480dp"android:layout_below="@id/titleBar" /><ImageButtonandroid:id="@+id/Add"android:onClick="addAccount"android:layout_width="80dp"android:layout_height="80dp"android:scaleType="centerInside"android:layout_centerHorizontal="true"android:background="#00FF0000"android:src="@drawable/plus"android:layout_below="@+id/listView" /></RelativeLayout>

accountItem.java

package com.jingyi.newwork;/*** @ClassName accountItem* @Description TODO* @Author Yu* @Date 2022/6/9 10:06* @Version 1.0**/
public class accountItem {private String _id;private String Type;private String Date;private String Title;private String CosType;private String Money;public String get_id() {return _id;}public void set_id(String _id) {this._id = _id;}public String getType() {return Type;}public void setType(String type) {Type = type;}public String getDate() {return Date;}public void setDate(String date) {Date = date;}public String getTitle() {return Title;}public void setTitle(String title) {Title = title;}public String getCosType() {return CosType;}public void setCosType(String cosType) {CosType = cosType;}public String getMoney() {return Money;}public void setMoney(String money) {Money = money;}
}

ListAdapter.java

package com.jingyi.newwork;import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;import java.util.List;/*** @ClassName ListAdapter* @Description TODO* @Author Yu* @Date 2022/6/9 10:08* @Version 1.0**/
public class ListAdapter extends BaseAdapter {private List<accountItem> cosList;private LayoutInflater layoutInflater;public ListAdapter(List<accountItem> list) {cosList=list;}public ListAdapter(Context context, List<accountItem> list) {cosList=list;layoutInflater=LayoutInflater.from(context);}@Overridepublic int getCount() {return cosList.size();}@Overridepublic Object getItem(int position) {return cosList.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {View view = layoutInflater.inflate(R.layout.list_item, null);TextView tv_type = (TextView) view.findViewById(R.id.tv_type);TextView tv_date = (TextView) view.findViewById(R.id.tv_date);TextView tv_title = (TextView) view.findViewById(R.id.tv_title);TextView tv_cost_type = (TextView) view.findViewById(R.id.tv_cost_type);TextView tv_money = (TextView) view.findViewById(R.id.tv_money);tv_type.setText(cosList.get(position).getType());tv_date.setText(cosList.get(position).getDate());tv_title.setText(cosList.get(position).getTitle());String type=cosList.get(position).getCosType();tv_cost_type.setText(type);if ("出".equals(type)){tv_money.setText("-"+cosList.get(position).getMoney());//支出标红tv_money.setTextColor(Color.rgb(255,0,0));}else{tv_money.setText("+"+cosList.get(position).getMoney());//收入标绿tv_money.setTextColor(Color.rgb(0,255,0));}return view;}
}

DB_Manager.java

package com.jingyi.newwork;import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;/*** @ClassName DB_Manager* @Description TODO* @Author Yu* @Date 2022/6/9 10:13* @Version 1.0**/
public class DB_Manager extends SQLiteOpenHelper {private  static  String DB_name="myAccount";public DB_Manager(Context context) {super(context, DB_name, null, 1);}@Overridepublic void onCreate(SQLiteDatabase db) {String sql="create table account(_id integer primary key autoincrement,"+//主键"Type varchar(10),"+//消费类型"Date varchar(20),"+//消费日期"Title varchar(60),"+//消费详情"CosType char(4),"+//收支类型"Money varchar(20))";//消费金额db.execSQL(sql);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}
}

createCos.java

package com.jingyi.newwork;import android.app.Activity;
import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Toast;import androidx.annotation.Nullable;/*** @ClassName CreateCos* @Description TODO* @Author Yu* @Date 2022/6/9 10:25* @Version 1.0**/
public class createCos extends Activity {private DB_Manager db_manager;private Spinner type;private EditText title;private Spinner cos_type;private EditText money;private DatePicker date;private Button submit;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);/*LayoutInflater inflater = LayoutInflater.from(getApplication());View view = inflater.inflate(R.layout.create_cos_activity, null);AlertDialog.Builder builder=new AlertDialog.Builder(createCos.this);builder.setView(view);AlertDialog dialog=builder.create();dialog.show();*/setContentView(R.layout.create_cos_activity);db_manager=new DB_Manager(createCos.this);type=findViewById(R.id.sp_type);title=findViewById(R.id.ed_title);cos_type=findViewById(R.id.sp_cos_type);money=findViewById(R.id.ed_money);date=findViewById(R.id.dp_cost_date);submit=findViewById(R.id.submitBtn);final String[] typeStr = new String[1];final String[] cos_typeStr=new String[1];type.getSelectedItem();cos_type.getSelectedItem();type.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {@Overridepublic void onItemSelected(AdapterView<?> parent, View view, int position, long id) {typeStr[0] = parent.getItemAtPosition(position).toString();//获取消费类型}@Overridepublic void onNothingSelected(AdapterView<?> parent) {}});cos_type.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {@Overridepublic void onItemSelected(AdapterView<?> parent, View view, int position, long id) {cos_typeStr[0]=parent.getItemAtPosition(position).toString();//获取收支类型}@Overridepublic void onNothingSelected(AdapterView<?> parent) {}});submit.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {String titleStr=title.getText().toString().trim();//消费详情String moneyStr=money.getText().toString().trim();//消费金额String dateStr = date.getYear() + "-" + (date.getMonth() + 1) + "-"+ date.getDayOfMonth();//日期if (dateStr.equals("")){//金额为必填项Toast.makeText(createCos.this, "金额为必填项!", Toast.LENGTH_SHORT).show();}else{SQLiteDatabase db = db_manager.getWritableDatabase();ContentValues values = new ContentValues();values.put("Type", typeStr[0]);values.put("Date",dateStr);values.put("Title",titleStr);values.put("CosType",cos_typeStr[0]);values.put("Money",moneyStr);long l = db.insert("account", null, values);if (l>0){Toast.makeText(createCos.this, "添加成功!", Toast.LENGTH_SHORT).show();setResult(0x2);finish();}else {Toast.makeText(createCos.this, "添加失败,请重试", Toast.LENGTH_SHORT).show();db.close();}setResult(0x2);finish();}}});}}

Bookkeeping.java

package com.jingyi.newwork;import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ListView;import androidx.annotation.Nullable;import com.hjq.bar.OnTitleBarListener;
import com.hjq.bar.TitleBar;import java.util.ArrayList;
import java.util.List;/*** @ClassName Bookkeeping* @Description TODO* @Author Yu* @Date 2022/6/9 10:29* @Version 1.0**/
public class Bookkeeping extends Activity {private TitleBar mTitleBar;private DB_Manager db_manager;private ListView listView;private ImageButton button;private List<accountItem> list;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.keeping);mTitleBar=findViewById(R.id.titleBar);db_manager = new DB_Manager(Bookkeeping.this);listView = findViewById(R.id.listView);button = (ImageButton) findViewById(R.id.Add);initList();mTitleBar.setOnTitleBarListener(new OnTitleBarListener() {public void onLeftClick(View v) {finish();}@Overridepublic void onTitleClick(View v) {}@Overridepublic void onRightClick(View v) {}});}@SuppressLint("Range")public void  initList(){list = new ArrayList<>();SQLiteDatabase db = db_manager.getReadableDatabase();Cursor cursor = db.query("account", null, null, null, null, null, null);while(cursor.moveToNext()){//从数据库库中取出数据初始化listaccountItem item = new accountItem();item.set_id(cursor.getString(cursor.getColumnIndex("_id")));item.setType(cursor.getString(cursor.getColumnIndex("Type")));item.setDate(cursor.getString(cursor.getColumnIndex("Date")));item.setTitle(cursor.getString(cursor.getColumnIndex("Title")));item.setCosType(cursor.getString(cursor.getColumnIndex("CosType")));item.setMoney(cursor.getString(cursor.getColumnIndex("Money")));list.add(item);}listView.setAdapter(new ListAdapter(this,list));//绑定适配器db.close();}//跳转到添加页面public void addAccount(View view){Intent intent = new Intent(Bookkeeping.this, createCos.class);startActivityForResult(intent,0x1);}//添加页回调函数@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (requestCode==0x1&&resultCode==0x2){this.initList();}}
}

MainActivity.java

package com.jingyi.newwork;import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.Toast;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @ClassName MainActivity* @Description TODO* @Author Yu* @Date 2022/5/13 16:11* @Version 1.0**/
public class MainActivity extends Activity {public ListView mListView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mListView = findViewById(R.id.listView);String[] name = {"图库", "音量与亮度", "网络监控", "手机设置", "我的账单"};int[] nums = {1, 2, 3, 4, 5};List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();for (int i = 0; i < name.length; i++) {Map<String, Object> map = new HashMap<>();map.put("name", name[i]);map.put("index", nums[i]);list.add(map);}SimpleAdapter adapter = new SimpleAdapter(this, list, R.layout.item, new String[]{"name", "index"}, new int[]{R.id.name_item, R.id.index_item});mListView.setAdapter(adapter);mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {if(position==0){Intent intent = new Intent(MainActivity.this, imagesActivity.class);startActivity(intent);}if(position==1){Intent intent = new Intent(MainActivity.this, AdjustActivity.class);Bundle bundle = new Bundle();bundle.putString("sentence","请在这里进行详细调节");intent.putExtras(bundle);startActivity(intent);}if (position==2){//点击第三项Bean bean = new Bean();bean.setFunctionName("网络监控");bean.setFunctionDescription("监控网络连接状态");Intent intent = new Intent();Byte aByte = new Byte((byte) 33);intent.putExtra("intNum",3);intent.putExtra("byteNum",aByte);intent.putExtra("Info",bean);intent.setClass(MainActivity.this,NetActivity.class);startActivity(intent);}if (position==3){//点击第四项Intent intent = new Intent(MainActivity.this,PhoneSetActivity.class);startActivity(intent);}if(position==4){//点击第五项Intent intent = new Intent(MainActivity.this,Bookkeeping.class);startActivity(intent);}Toast.makeText(MainActivity.this, "您选中的是第"+(position+1)+"项", Toast.LENGTH_SHORT).show();}});}
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"package="com.jingyi.newwork"><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.TSNewApplication"><activityandroid:name=".MainActivity"></activity><activity android:name=".LoginActivity"android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><activity android:name=".imagesActivity"></activity><activity android:name=".AdjustActivity"android:label="系统调节"></activity><activityandroid:name=".NetActivity"android:label="网络监控"></activity><activity android:name=".PhoneSetActivity"android:label="手机设置"></activity><activity android:name=".createCos"></activity><activity android:name=".Bookkeeping"></activity></application><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>




(六)ContentProvider 实现数据共享相关推荐

  1. Android中ContentProvider组件数据共享

    ContentProvider的功能和意义: 主要用于对外共享数据,也就是通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProvider对指定应用中 ...

  2. [2021.11.20]使用ContentProvider实现数据共享

    Content Provider用于在不同的应用程序之间实现数据共享.应用程序使用一个ContentResolver对象去操作指定数据. 1 Content Provider概述 Content Pr ...

  3. android中的ContentProvider实现数据共享

    为了在应用程序之间交换数据,android中提供了ContentProvider,ContentProvider是不同应用程序之间进行数据交换的标准API.当一个应用程序需要把自己的数据暴露给其他程序 ...

  4. Android学习笔记十七.使用ContentProvider实现数据共享(四).操作系统(联系人)的ContentProvider

        Android系统本身提供了大量的ContentProvider,例如联系人信息.系统的多媒体信息等,我们开发的应用程序主要是通过ContentResolver来调用系统的ContentPro ...

  5. oracle row 00033,00033-ContentProvider实现数据共享[总结]

    ContentProvider实现数据共享[总结] OK,ContextProvider测试完成,总结下. 这个东西出现的意义是为了一个系统中,多应用间共享数据? 那不有文件,xml形式吗?嗯,当然, ...

  6. Android(六)——Android第一周学习

    这里写自定义目录标题 1.Android系统框架 1.1 应用层 1.2 应用框架层 1.3 系统运行层 1.4 Linux内核层 2. Android四大组件及作用 2.1 activity 活动 ...

  7. android content item,Android中ContentProvider的应用实例

    一.ContentProvider简介 当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据.虽然使用其他方法也可以对外共享数据,但数据访问方 ...

  8. contentprovider的学习实例总结

    一.ContentProvider简介        当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据.虽然使用其他方法也可以对外共享数据 ...

  9. Android——四大组件、六大布局、五大存储

    一.android四大组件 (一)android四大组件详解 Android四大组件分别为activity.service.content provider.broadcast receiver. 1 ...

最新文章

  1. MapReduce学习总结之Combiner、Partitioner、Jobhistory
  2. cocos2d-x初探学习笔记(17)--瓦片地图集
  3. 大连学业水平考试计算机,2016年大连市普通高中学生学业水平考试模拟题(一)
  4. Redis对象的refcount与lru属性(内存回收、对象共享、空转时长)
  5. spring boot使用logback实现多环境日志配置
  6. FreeRTOS源码获取
  7. oracle中的合并查询
  8. 鼠绘漫画 for wp8.1
  9. 叫板抖音,运营商入局短视频
  10. DELL 1420 笔记本 BIOS设置
  11. 计算任意2个日期内的工作日(没有考虑到国定假日
  12. redis desktop manager安装以及使用教程
  13. NewStarCTF 公开赛wp
  14. python文本聚类 词云图_有哪些软件可以进行中文词频分析?
  15. 知道一点怎么设直线方程_不知道怎么购买普洱茶?来积累一点硬知识!
  16. excel 基础笔记整理
  17. 受制裁,即 Github 之后,Adobe 也开始大量封禁账号和服务了!
  18. 最高效的七个云原生开发原则
  19. 《通信原理》(2):信息量及平均信息量
  20. 02 资源搜索-全面、快速查找全网你想要的任何信息、情报

热门文章

  1. Keras深度学习实战(41)——语音识别
  2. Allegro专题【4】——通孔焊盘的制作
  3. 做知识图谱遇到的环境问题合集【spacy、gensim、keras_contrib等】
  4. 数据收集之Fluentd
  5. ChatGPT 的安全风险,我们应该信任它吗?
  6. Java 时间,时间戳 往 前/后 加/减 一个小时
  7. 给路人的单反上手教程
  8. iOS软件开发 设置UITextView输入内容位置从左上角开始
  9. OSChina周五乱弹——妹纸被盗,谁有防盗妙招?
  10. SAP销售单抬头文本内容写入和读取(bapi:SAVE_TEXT 、READ_TEXT)