android 8 时 增加了一个自动填充框架,它是可以是我们在填写表单是更加容易减少出错,还可以我们减少填写表单的时间,填写表单是一项耗时且容易出错的任务,用户可能很容易对需要这些类型任务的应用感到沮丧,自动填充框架通过提供以下优势来改善用户体验:
- 更少的时间用于填充字段 自动填充功能可以帮助用户避免重新输入信息
- 最大限度地减少用户输入错误 打字很容易出错,特别是在移动设备中删除输入信息的必要性也可以消除随之而来的错误
所以自动填充框架可以是我的操作变得简单容易,提升用户在使用时的方便。下面带大家看看如何实现自动填充框架,我们创建一个登陆的demo,loginDemo,下面看一下登陆页面的布局
<?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/username"
android:hint="Your primary email address"
android:inputType="textEmailAddress"
android:importantForAutofill="yes"/>
</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/password"
android:hint="numberPassword"
android:importantForAutofill="yes"
android:inputType="numberPassword"/>
</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="登陆"
android:onClick="userLogin"/>
</LinearLayout>
复制代码
布局很简单,里面只有一个android:importantForAutofill属性,它是重点。为了是android系统可以识别某个控件是否支持自动填充服务,我们首先需要对支持此服务的控件添加一个标识,这个标识用来启发自动填充框架。
1. 为控件添加自动填充标识
android:importantForAutofill
复制代码
这个属性的取值有一下几种
- auto 系统默认值,这个标识有系统决定是否触发自动填充服务
- no 此控件不支持自动填充服务
- noExcludeDescendants 此控件及其子控件都不支持自动填充服务
- yes 次视图支持自动填充服务
- yesExcludeDescendants 此控件支持,但是它的子控件不支持 然后我们在看看MainActivity的逻辑
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAutofillManager = this.getSystemService(AutofillManager.class);
mAutofillCallback = new MyAutofillCallback();
}
public void userLogin(View view) {
String uName = ((EditText) findViewById(R.id.username)).getText().toString();
String pw = ((EditText) findViewById(R.id.password)).getText().toString();
SharedPreferences.Editor editor = getSharedPreferences("EMAIL_STORAGE", MODE_PRIVATE)
.edit();
editor.putString("PRIMARY_EMAIL", uName);
editor.putString("USER_PW", pw);
editor.commit();
}
}
复制代码
这个没啥,就是获取用户名密码保存在SharedPreferences里,看到这大家就得没啥干货呀,别急现在我们只是为之后的是做个数据准备。
2 AutofillService 服务
写代码之前先介绍一下这个类,AutofillService是一个能帮助我们实现自动填充屏幕内容的服务类,它里面两个方法需要我们进行重写。
@Override
public void onFillRequest(@NonNull FillRequest request, @NonNull CancellationSignal cancellationSignal,
@NonNull FillCallback fillCallback) {
}
public void onSaveRequest(@NonNull SaveRequest request, @NonNull SaveCallback callback) {
}
复制代码
-
onFillRequest 当页面内容有可用自动填充时系统调用该方法
FillRequest参数 自动填充服务的请求对象,使用它我们可以获取屏幕上视图
FillCallback参数 处理正在填充的服务,可以显示自动填充的数据
-
onSaveRequest 当用户请求服务保存屏幕字段时调用
它的参数和第一个基本一样的
大致了一下AutofullService这个类的作用,我们在创建一个类MyAutofillService让它继承AutofillService这个类
public class MyAutofillService extends AutofillService {
public void traverseStructure(AssistStructure structure, List<AssistStructure.ViewNode> emailFields) {
int nodes = structure.getWindowNodeCount();
for (int i = 0; i < nodes; i++) {
AssistStructure.WindowNode windowNode = structure.getWindowNodeAt(i);
AssistStructure.ViewNode viewNode = windowNode.getRootViewNode();
traverseNode(viewNode, emailFields);
}
}
public void traverseNode(AssistStructure.ViewNode viewNode, List<AssistStructure.ViewNode> emailFields) {
if (viewNode == null || viewNode.getClassName() == null)
return;
if (viewNode.getClassName().contains("EditText")) {
String viewId = viewNode.getIdEntry();
if (viewId != null && (viewId.contains("email") || viewId.contains("username") || viewId.contains("password"))) {
emailFields.add(viewNode);
return;
}
}
for (int i = 0; i < viewNode.getChildCount(); i++) {
AssistStructure.ViewNode childNode = viewNode.getChildAt(i);
traverseNode(childNode, emailFields);
}
}
@Override
public void onFillRequest(@NonNull FillRequest request, @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback fillCallback) {
// Get the structure from the request
//1 获取自动填充的上下文
List<FillContext> context = request.getFillContexts();
//获取屏幕快照数据
AssistStructure structure = context.get(context.size() - 1).getStructure();
//保存自动填充的节点
List<AssistStructure.ViewNode> emailFields = new ArrayList<>();
//遍历获取目标空间
traverseStructure(structure, emailFields);
if (emailFields.size() < 2) {
fillCallback.onSuccess(null);
return;
}
SharedPreferences sharedPreferences = getSharedPreferences("EMAIL_STORAGE", MODE_PRIVATE);
String primaryEmail = sharedPreferences.getString("PRIMARY_EMAIL", "");
String secondaryEmail = sharedPreferences.getString("SECONDARY_EMAIL", "");
//创建自动提示的view
RemoteViews rvPrimaryEmail = new RemoteViews(getPackageName(), R.layout.user_suggestion);
rvPrimaryEmail.setTextViewText(R.id.email_suggestion_item, primaryEmail);
rvPrimaryEmail.setImageViewResource(R.id.icon, R.mipmap.user);
RemoteViews rvSecondaryEmail = new RemoteViews(getPackageName(), R.layout.user_suggestion);
rvSecondaryEmail.setTextViewText(R.id.user_suggestion_item, primaryEmail);
rvSecondaryEmail.setImageViewResource(R.id.icon, R.mipmap.pw);
AssistStructure.ViewNode userField = emailFields.get(0);
AssistStructure.ViewNode passField = emailFields.get(1);
//为自动填充视图构造数据
Dataset primaryEmailDataSet = new Dataset.Builder(rvPrimaryEmail)
.setValue(userField.getAutofillId(), AutofillValue.forText(primaryEmail)
).build();
Dataset secondaryEmailDataSet = new Dataset.Builder(rvSecondaryEmail)
.setValue(passField.getAutofillId(), AutofillValue.forText(secondaryEmail)
).build();
//封装响应数据
FillResponse response = new FillResponse.Builder()
.addDataset(primaryEmailDataSet)
.addDataset(secondaryEmailDataSet)
// .addDataset(msecondaryEmailDataSet)
// .setSaveInfo(new SaveInfo.Builder(//只有添加SaveInfo对象才会回调onSaveRequest
// SaveInfo.SAVE_DATA_TYPE_USERNAME | SaveInfo.SAVE_DATA_TYPE_EMAIL_ADDRESS,
// new AutofillId[]{emailField.getAutofillId(), emailField.getAutofillId()})
// .build())
.build();
//通知自动填充框架
fillCallback.onSuccess(response);
}
// 保存需要自动填入记录。
@Override
public void onSaveRequest(@NonNull SaveRequest request, @NonNull SaveCallback callback) {
List<FillContext> context = request.getFillContexts();
AssistStructure structure = context.get(context.size() - 1).getStructure();
List<AssistStructure.ViewNode> emailFields = new ArrayList<>();
traverseStructure(structure, emailFields);
callback.onSuccess();
}
}
复制代码
我们在onFillRequest方法里获取当前界面的空间信息,之后遍历判断是否还有我们目标的空间,将它保存在一个list中,代码中已将详细注释了, 下面是user_suggestion布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/user_suggestion_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:textSize="18sp"
android:textStyle="bold"></TextView>
<ImageView
android:id="@+id/icon"
android:layout_gravity="center"
android:layout_height="wrap_content"
android:layout_marginEnd="?android:attr/listPreferredItemPaddingEnd"
android:layout_width="wrap_content"
android:src="@mipmap/ic_launcher" />
</LinearLayout>
复制代码
这个服务类完了之后,我们需要配置一个它
<service android:name=".MyAutofillService"
android:label="Mtx Autofill Service"
android:permission="android.permission.BIND_AUTOFILL_SERVICE">
<meta-data android:name="android.autofill"
android:resource="@xml/login_filler"/>
<intent-filter>
<action android:name="android.service.autofill.AutofillService"/>
</intent-filter>
</service>
复制代码
android:label=“XXX”这个服务的名称,随便起,声明命一个android.permission.BIND_AUTOFILL_SERVICE权限, 下面是login_filler
<?xml version="1.0" encoding="utf-8"?>
<autofill-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:settingsActivity="com.mtx.login.MainActivity"/>
复制代码
最后手机中设置一下:设置 > 系统 > 语言和输入 > 高级 > 输入帮助 > 自动填充服务>Mtx Autofill Service