如何使用 Android O 的自动填充框架
自动填充功能,通常缩短为自动填充,是浏览器多年来一直支持的功能。我们大多数人一直使用它。我认为,在填写注册表或完成结帐过程等任务中,它是必不可少的。
Android的最新版本Android O带来与Android应用程序类似的功能。换句话说,Android现在可以帮助用户填写属于他们在设备上安装的所有应用的表单。这是一个令人期待已久的功能,因为在小屏幕上使用虚拟键盘进行打字往往相当麻烦。
作为应用开发人员,我们可以使用新的自动填充框架来创建自己的自定义自动填充服务,该服务决定如何填充应用的输入字段。在本教程中,我将向展示如何使用这个框架。
要求
首先要做的准备工作,
- Android Studio 2.4 Preview 7 或者更高的版本
- 运行Android O或更高版本的模拟器或设备
1. 新建一个项目
启动Android Studio并创建一个空的活动的新项目。必须记得在“目标Android设备”对话框中选择Android 7。
该项目将需要属于设计支持库的几个小部件,因此打开应用程序模块的build.gradle文件并添加以下 compile 依赖关系:
compile 'com.android.support:design:26.+'
最后,按“Sync Now ”按钮更新项目。
2. 创建一个 Settings Activity
在本教程中,我们将创建一个包含一个非常简单的自动填充服务的应用程序,该服务仅针对用户期望输入电子邮件地址的输入字段。因为Google Play上几乎每一个其他的应用程序都要求一个电子邮件地址,这个服务将是非常有用的。我们的服务显然需要知道用户的电子邮件地址是什么。因此,现在让我们建立一个用户可以输入并保存两个电子邮件地址的活动。
第一步: 定义 Layout
你可能会期望,Activity的布局将包含两个EditText小部件,用户可以在其中键入他或她的电子邮件地址。如果你希望遵守Material Design的指导原则,将EditText小部件放在TextInputLayout容器中是一个好主意。
另外,布局必须有一个 Button 小部件,用户可以按保存电子邮件地址。
你可以随意将小部件放置在您想要的位置。不过,现在我建议你把它们放在一个LinearLayout里面,它的方向是垂直的。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/primary"
android:hint="Your primary email address"
android:inputType="textEmailAddress"/>
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/secondary"
android:hint="Your other email address"
android:inputType="textEmailAddress"/>
</android.support.design.widget.TextInputLayout>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/save_button"
style="@style/Widget.AppCompat.Button.Colored"
android:text="Save"
android:onClick="saveEmailAddresses"/>
</LinearLayout>
在上面的代码中,您可以看到Button小部件有一个指向一个方法的onClick属性。在Android Studio中单击此属性旁边的黄色灯泡,在关联的Activity类中为其生成一个存根。
public void saveEmailAddresses(View view) {
// More code will be added here
}
第二步: 存储 Email 地址
我们将使用一个名为EMAIL_STORAGE的共享首选项文件来保存我们的数据。您可以使用Activity类的getSharedPreferences()方法来访问该文件。此外,为了能够写入文件,您必须调用其edit()方法,该方法生成一个SharedPreferences.Editor对象。
因此,在saveEmailAddresses()方法中添加以下代码:
SharedPreferences.Editor editor =
getSharedPreferences("EMAIL_STORAGE", MODE_PRIVATE).edit();
要获取用户输入到EditText窗口小部件的电子邮件地址,您必须首先使用findViewById()方法获取对它们的引用,然后调用它们的getText()方法。
String primaryEmailAddress =
((EditText)findViewById(R.id.primary))
.getText().toString();
String secondaryEmailAddress =
((EditText)findViewById(R.id.secondary))
.getText().toString();
此时,您可以调用编辑器的putString()方法将电子邮件地址添加到首选项文件中作为两个键值对。执行此操作后,请勿忘记调用commit()方法以使您的更改永久。
editor.putString("PRIMARY_EMAIL", primaryEmailAddress);
editor.putString("SECONDARY_EMAIL", secondaryEmailAddress);
editor.commit();
第三步: 创建一个 Meta-Data 文件
我们在上一步中创建的设置活动目前只是一个普通的活动。要让Android平台知道这是自动填充服务的设置活动,我们必须创建一个元数据XML文件。
在项目的res / xml文件夹中创建一个名为email_address_filler.xml的新XML文件。在其中添加一个标签,并将其settingsActivity属性的值设置为Activity类的名称。
<?xml version="1.0" encoding="utf-8"?>
<autofill-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:settingsActivity="com.tutsplus.simplefill.MainActivity"/>
现在可以运行应用程序,填入两个email地址,然后按save按钮保存。
3. 创建 Autofill Service
任何扩展抽象AutoFillService类的类都可以作为自动填充服务。所以首先创建一个新的Java类,文件>新建> Java类。
在弹出的对话框中,将该类命名为EmailAddressFiller,并确保将Superclass字段的值设置为AutoFillService。
现在,Android Studio将提示您为两种抽象方法生成存根:onSaveRequest()和onFillRequest()。
在本教程中,我们将仅关注onFillRequest()方法,该方法在任何用户打开活动时自动调用任何包含应用程序的输入字段。
@Override
public void onFillRequest(AssistStructure assistStructure,
Bundle bundle,
CancellationSignal cancellationSignal,
FillCallback fillCallback) {
// More code goes here
}
步骤1: 分析视图层次结构
自动填充服务需要分析应用程序的用户界面并识别可以填写的输入字段。这就是为什么onFillRequest()方法接收到一个AssistStructure对象,其中包含当前在屏幕上可见的所有窗口小部件的详细信息。更准确地说,它包含一个ViewNode对象的树。
如果您从未见过这样的树,建议您使用Android SDK的一部分uiautomatorviewer工具来分析几个应用的布局层次结构。例如,以下是Android的默认邮件应用的布局层次结构:
当然,要分析树的所有节点,需要一个递归方法。
void identifyEmailFields(AssistStructure.ViewNode node,
List<AssistStructure.ViewNode> emailFields) {
// More code goes here
}
如您所见,此方法具有ViewNode和List作为参数。我们将使用列表来存储所有期望电子邮件地址的输入字段。您可能现在想知道如何以编程方式告诉输入字段是否需要一个电子邮件地址。那么真的没有万无一失的方法可以跟随。
现在,我们假设所有应用程序开发人员总是给他们的输入字段提供有意义的资源ID。
基于这一假设,我们可以简单地选择资源ID包含诸如“电子邮件”和“用户名”等字符串的所有输入字段。
因此,将以下代码添加到方法中:
if(node.getClassName().contains("EditText")) {
String viewId = node.getIdEntry();
if(viewId!=null && (viewId.contains("email")
|| viewId.contains("username"))) {
emailFields.add(node);
return;
}
}
接下来,每当遇到包含更多ViewNode对象的ViewNode对象时,我们必须递归调用identifyEmailFields()方法来分析其所有子节点。
以下代码显示如何:
for(int i=0; i<node.getChildCount();i++) {
identifyEmailFields(node.getChildAt(i), emailFields);
}
在这一点上,我们可以在onFillRequest()方法中调用identifyEmailFields()方法,并将视图层次结构的根节点传递给它。
// Create an empty list
List<AssistStructure.ViewNode> emailFields = new ArrayList<>();
// Populate the list
identifyEmailFields(assistStructure
.getWindowNodeAt(0)
.getRootViewNode(), emailFields);
如果我们的服务无法识别电子邮件的任何输入字段,它什么都不做。因此,添加以下代码:
if(emailFields.size() == 0)
return;
步骤2:创建和填充远程视图
如果我们的服务确实可以填写一个输入字段,那么它必须填充一个下拉列表,它将显示在输入字段的下方。然而,这样做并不简单,因为输入字段和下拉列表都不属于我们的应用程序。要填充下拉列表,我们必须使用RemoteViews对象。顾名思义,RemoteViews对象是可以在另一个应用程序中显示的视图集合。要初始化RemoteViews对象,您需要一个布局XML文件。我们现在创建一个名为email_suggestion.xml的文件。现在,它只能包含一个TextView小部件来显示一个电子邮件地址。因此,将以下代码添加到email_suggestion.xml中:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/email_suggestion_item"
android:textSize="18sp"
android:textStyle="bold"
android:padding="5dp">
</TextView>
您现在可以返回到onFillRequest()方法并创建两个RemoteViews对象:一个用于主电子邮件,另一个用于辅助邮件。
RemoteViews rvPrimaryEmail =
new RemoteViews(getPackageName(),
R.layout.email_suggestion);
RemoteViews rvSecondaryEmail =
new RemoteViews(getPackageName(),
R.layout.email_suggestion);
RemoteViews对象中的TextView窗口小部件必须显示我们之前存储在共享首选项文件中的两个电子邮件地址。要打开该文件,请再次使用getSharedPreferences()方法。一旦打开,您可以使用其getString()方法来获取电子邮件地址。最后,要设置远程TextView小部件的内容,必须使用setTextViewText()方法。
// Load the email addresses from preferences
SharedPreferences sharedPreferences =
getSharedPreferences("EMAIL_STORAGE", MODE_PRIVATE);
String primaryEmail =
sharedPreferences.getString("PRIMARY_EMAIL", "");
String secondaryEmail =
sharedPreferences.getString("SECONDARY_EMAIL", "");
// Update remote TextViews
rvPrimaryEmail.setTextViewText(R.id.email_suggestion_item,
primaryEmail);
rvSecondaryEmail.setTextViewText(R.id.email_suggestion_item,
secondaryEmail);
步骤3:创建数据集
我们现在可以使用远程视图来创建可以发送到任何应用程序的自动填充数据集。为了让本教程不要太久,我们将为我们遇到的第一个电子邮件输入字段创建数据集。以下代码显示如何仅选择第一个电子邮件输入字段:
AssistStructure.ViewNode emailField = emailFields.get(0);
自动填充数据集只不过是Dataset类的一个实例,可以使用Dataset.Builder类来构建。当用户选择我们的服务在下拉列表中显示的电子邮件地址之一时,它必须使用Dataset.Builder类的setValue()方法设置相关输入字段的内容。但是,您不能将ViewNode对象传递给setValue()方法。它实际上需要一个自动填充标识符,它必须通过调用ViewNode对象的getAutoFillId()方法获得。另外,要指定必须写入输入字段的文本,您必须使用AutoFillValue.forText()方法。以下代码显示如何:
Dataset primaryEmailDataSet =
new Dataset.Builder(rvPrimaryEmail)
.setValue(
emailField.getAutoFillId(),
AutoFillValue.forText(primaryEmail)
).build();
Dataset secondaryEmailDataSet =
new Dataset.Builder(rvSecondaryEmail)
.setValue(
emailField.getAutoFillId(),
AutoFillValue.forText(secondaryEmail)
).build();
之前您发送的数据集的应用程序,您必须将它们添加到FillResponse对象,可以使用FillResponse.Builder类来构建。调用其addDataset()方法两次以添加两个数据集。一旦FillResponse对象准备就绪,将它作为参数传递给FillCallback对象的onSuccess()方法,该对象是onFillRequest()方法的参数之一。
FillResponse response = new FillResponse.Builder()
.addDataset(primaryEmailDataSet)
.addDataset(secondaryEmailDataSet)
.build();
fillCallback.onSuccess(response);
步骤4:更新 Manifest
像所有服务一样,自动填充服务也必须在项目的AndroidManifest.xml文件中声明。在这样做时,您必须确保它受到android.permission.BIND_AUTO_FILL权限的保护。
此服务还需要一个标签,允许其响应android.service.autofill.AutoFillService操作,以及指向我们在之前的步骤中创建的元数据XML文件的标记。
因此,将以下行添加到manifest 文件中:
<service android:name=".EmailAddressFiller"
android:permission="android.permission.BIND_AUTO_FILL">
<meta-data android:name="android.autofill"
android:resource="@xml/email_address_filler"/>
<intent-filter>
<action android:name="android.service.autofill.AutoFillService"/>
</intent-filter>
</service>
我们的自动填充service和app现已准备就绪。构建项目并在您的设备上安装该应用程序。
4.激活并使用自动填充服务
要启动自动填充服务,请打开设备的“设置”应用,然后导航到“应用”
您现在可以打开任何需要电子邮件地址的应用程序来查看您的自动填充服务。例如,您将在Instagram和Pinterest的登录界面中看到以下内容:
结论
现在我们知道如何为Android创建和使用自定义自动填充服务。我们可以随意扩展它以支持其他常见字段,如名字或电话号码。还可以使用其他属性(如标签和提示)尝试识别输入字段。