自动表单填充(通常简称为自动填充)是浏览器多年来支持的功能。 我们大多数人一直在使用它。 我认为,在填写注册表或完成结帐流程之类的任务期间,它是必不可少的。
Android的最新版本Android O为Android应用程序带来了类似的功能。 换句话说,Android现在可以帮助用户填写属于他们在设备上安装的所有应用程序的表格。 这是一个人们期待已久的功能,因为在小屏幕上使用虚拟键盘打字通常很麻烦。
作为应用程序开发人员,您可以使用新的自动填充框架来创建自己的自定义自动填充服务,该服务决定如何填充应用程序的输入字段。 在本教程中,我将向您展示如何进行。
先决条件
要遵循本教程,您需要:
- Android Studio 2.4 Preview 7或更高版本
- 运行Android O或更高版本的模拟器或设备
1.创建一个新项目
启动Android Studio并创建一个活动为空的新项目。 当然,您必须记住在“ 目标Android设备”对话框中选择Android 7+ 。
该项目将需要一些属于Design Support Library的小部件,因此打开app
模块的build.gradle文件,并向其添加以下compile
依赖项:
compile 'com.android.support:design:26.+'
最后,按立即同步按钮以更新项目。
2.创建一个设置活动
在本教程中,我们将创建一个包含非常简单的自动填充服务的应用程序,该服务仅针对希望用户输入电子邮件地址的那些输入字段。 由于今天Google Play上几乎所有其他应用都要求提供电子邮件地址,因此该服务将非常有用。
我们的服务显然需要知道用户的电子邮件地址是什么。 因此,现在让我们建立一个活动,用户可以输入并保存两个电子邮件地址。
步骤1:定义布局
如您所料,活动的布局将包含两个EditText
小部件,用户可以在其中键入他或她的电子邮件地址。 如果您希望它遵循Material Design的准则,则将EditText
小部件放置在TextInputLayout
容器内是个好主意。
此外,该布局必须具有用户可以按下以保存电子邮件地址的“ Button
小部件。
您可以随意将小部件放置在所需的任何位置。 不过,现在,我建议您将它们全部放置在垂直方向的LinearLayout
。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="https://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
}
步骤2:保存电子邮件地址
我们将使用一个名为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();
步骤3:建立中继资料档案
我们在上一步中创建的settings活动目前仅是一个普通活动。 为了让Android平台知道这是自动填充服务的设置活动,我们必须创建一个元数据XML文件。
在项目的res / xml文件夹中创建一个名为email_address_filler.xml的新XML文件。 在其中添加<autofill-service>
标记,并将其settingsActivity
属性的值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"/>
现在,您可以运行该应用程序,输入两个电子邮件地址,然后按保存按钮以保存它们。
3.创建自动填充服务
任何扩展抽象AutoFillService
类的类都可以用作自动填充服务。 因此,首先使用File> New> Java Class创建一个新的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
对象树。
如果您从未见过这样的树,建议您使用uiautomatorviewer工具(它是Android SDK的一部分)来分析一些应用程序的布局层次结构。 例如,以下是Android默认邮件应用的布局层次结构:
自然地,要分析树的所有节点,您需要一种递归方法。 现在创建一个:
void identifyEmailFields(AssistStructure.ViewNode node,
List<AssistStructure.ViewNode> emailFields) {
// More code goes here
}
如您所见,此方法有一个ViewNode
和一个List
作为其参数。 我们将使用List
存储所有需要电子邮件地址的输入字段。
您现在可能想知道如何以编程方式判断输入字段是否需要电子邮件地址。 好吧,实际上没有可以遵循的万无一失的方法。 现在,我们将假定所有应用程序开发人员始终向其输入字段提供有意义的资源ID。 基于该假设,我们可以简单地选择资源ID包含诸如“ email”和“ username”之类的字符串的所有输入字段。
因此,将以下代码添加到该方法:
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()
方法内调用onFillRequest()
方法,并将视图层次结构的根节点传递给它。
// 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()
方法来获取该ViewNode
。
此外,要指定必须写入输入字段的文本,必须使用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
对象是准备好了,把它作为一个参数传递给onSuccess()
的方法FillCallback
对象,这是的参数之一onFillRequest()
方法。
FillResponse response = new FillResponse.Builder()
.addDataset(primaryEmailDataSet)
.addDataset(secondaryEmailDataSet)
.build();
fillCallback.onSuccess(response);
步骤4:更新清单
与所有服务一样,自动填充服务也必须在项目的AndroidManifest.xml文件中声明。 这样做时,必须确保它受到android.permission.BIND_AUTO_FILL
权限的保护。
此服务还需要一个<intent-filter>
标记,以使其能够响应android.service.autofill.AutoFillService
操作,以及一个<meta-data>
标记,该标记指向我们在上一步中创建的元数据XML文件。 。
因此,将以下行添加到清单文件中:
<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>
我们的自动填充服务和应用现已准备就绪。 生成项目并在您的设备上安装该应用程序。
4.激活并使用自动填充服务
要激活自动填充服务,请打开设备的“设置”应用,然后导航至“ 应用和通知”>“高级”>“默认应用”>“自动填充”应用 。 在下一个屏幕中,从可用的自动填充应用列表中选择您的应用。
现在,您可以打开任何要求输入电子邮件地址的应用程序,以查看自动填充服务的运行情况。 例如,这是您在Instagram和Pinterest的登录屏幕上看到的内容:
结论
现在,您知道如何为Android创建和使用自定义自动填充服务。 随意扩展它以支持其他常见字段,例如名字或电话号码。 您也可以尝试使用其他属性(例如标签和提示)来标识输入字段。
翻译自: https://code.tutsplus.com/tutorials/how-to-use-android-os-autofill-framework--cms-28811