(六)ContentProvider 实现数据共享
文章目录
- 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 实现数据共享相关推荐
- Android中ContentProvider组件数据共享
ContentProvider的功能和意义: 主要用于对外共享数据,也就是通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProvider对指定应用中 ...
- [2021.11.20]使用ContentProvider实现数据共享
Content Provider用于在不同的应用程序之间实现数据共享.应用程序使用一个ContentResolver对象去操作指定数据. 1 Content Provider概述 Content Pr ...
- android中的ContentProvider实现数据共享
为了在应用程序之间交换数据,android中提供了ContentProvider,ContentProvider是不同应用程序之间进行数据交换的标准API.当一个应用程序需要把自己的数据暴露给其他程序 ...
- Android学习笔记十七.使用ContentProvider实现数据共享(四).操作系统(联系人)的ContentProvider
Android系统本身提供了大量的ContentProvider,例如联系人信息.系统的多媒体信息等,我们开发的应用程序主要是通过ContentResolver来调用系统的ContentPro ...
- oracle row 00033,00033-ContentProvider实现数据共享[总结]
ContentProvider实现数据共享[总结] OK,ContextProvider测试完成,总结下. 这个东西出现的意义是为了一个系统中,多应用间共享数据? 那不有文件,xml形式吗?嗯,当然, ...
- Android(六)——Android第一周学习
这里写自定义目录标题 1.Android系统框架 1.1 应用层 1.2 应用框架层 1.3 系统运行层 1.4 Linux内核层 2. Android四大组件及作用 2.1 activity 活动 ...
- android content item,Android中ContentProvider的应用实例
一.ContentProvider简介 当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据.虽然使用其他方法也可以对外共享数据,但数据访问方 ...
- contentprovider的学习实例总结
一.ContentProvider简介 当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据.虽然使用其他方法也可以对外共享数据 ...
- Android——四大组件、六大布局、五大存储
一.android四大组件 (一)android四大组件详解 Android四大组件分别为activity.service.content provider.broadcast receiver. 1 ...
最新文章
- MapReduce学习总结之Combiner、Partitioner、Jobhistory
- cocos2d-x初探学习笔记(17)--瓦片地图集
- 大连学业水平考试计算机,2016年大连市普通高中学生学业水平考试模拟题(一)
- Redis对象的refcount与lru属性(内存回收、对象共享、空转时长)
- spring boot使用logback实现多环境日志配置
- FreeRTOS源码获取
- oracle中的合并查询
- 鼠绘漫画 for wp8.1
- 叫板抖音,运营商入局短视频
- DELL 1420 笔记本 BIOS设置
- 计算任意2个日期内的工作日(没有考虑到国定假日
- redis desktop manager安装以及使用教程
- NewStarCTF 公开赛wp
- python文本聚类 词云图_有哪些软件可以进行中文词频分析?
- 知道一点怎么设直线方程_不知道怎么购买普洱茶?来积累一点硬知识!
- excel 基础笔记整理
- 受制裁,即 Github 之后,Adobe 也开始大量封禁账号和服务了!
- 最高效的七个云原生开发原则
- 《通信原理》(2):信息量及平均信息量
- 02 资源搜索-全面、快速查找全网你想要的任何信息、情报
热门文章
- Keras深度学习实战(41)——语音识别
- Allegro专题【4】——通孔焊盘的制作
- 做知识图谱遇到的环境问题合集【spacy、gensim、keras_contrib等】
- 数据收集之Fluentd
- ChatGPT 的安全风险,我们应该信任它吗?
- Java 时间,时间戳 往 前/后 加/减 一个小时
- 给路人的单反上手教程
- iOS软件开发 设置UITextView输入内容位置从左上角开始
- OSChina周五乱弹——妹纸被盗,谁有防盗妙招?
- SAP销售单抬头文本内容写入和读取(bapi:SAVE_TEXT 、READ_TEXT)