本文是基于官方demo来分析Autofill Framework的用法(要正常打开这个项目请使用Android Studio Preview 3.0以上版本,并下载Anroid O模拟器镜像)。Autofill Framework最低支持SDK API 26(Android O)+。

在手机中管理autofill服务:
设置->系统->语言与输入->高级->自动填写服务->选择自己想要的服务,点击它旁边的设置按钮可以进入这个autofill的设置界面(如果有给此服务设定了设置界面)
也就是:
setting->system->languages & input -> advanced -> autofill services

在layout中通过autofillHints标记需要记录的控件节点

通过属性autofillHints在标记需要记录的节点,也就是key-value的形式的key。
例如:标记记录密码框key为password

<EditText
    android:id="@+id/passwordField"android:layout_width="200dp"android:layout_height="wrap_content"android:autofillHints="password"android:inputType="textPassword" />

在代码中可以通过view.setAutofillHints(String… autofillHints) 方法来设置这些标记。
autofillHints的值都是View类(api 26版本)中定义的一些String类型常量,目前有13种,分别是:

  • AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE 信用卡到期日期
  • AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY 信用卡到期日
  • AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH 信用卡到期月
  • AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR 信用卡到期年
  • AUTOFILL_HINT_CREDIT_CARD_NUMBER 信用卡卡号
  • AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE 信用卡安全密码
  • AUTOFILL_HINT_EMAIL_ADDRESS 邮箱地址
  • AUTOFILL_HINT_NAME 用户真名
  • AUTOFILL_HINT_PASSWORD 用户密码
  • AUTOFILL_HINT_PHONE 电话号码
  • AUTOFILL_HINT_POSTAL_ADDRESS 邮寄地址
  • AUTOFILL_HINT_POSTAL_CODE 邮寄编号
  • AUTOFILL_HINT_USERNAME 用户名

view.setImportantForAutofill(int mode)
设置autofill的重要级别也是一些常量,对应xml的标签是android:importantForAutofill,有5种模式:

  • IMPORTANT_FOR_AUTOFILL_AUTO 不管是否重要,都使用autofill
  • IMPORTANT_FOR_AUTOFILL_NO 不使用autofill,但是子view可以使用
  • IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS 不使用autofill,子view也不使用
  • IMPORTANT_FOR_AUTOFILL_YES 使用autofill,包括子view
  • IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS 使用autofill,但子view不使用

AutofillService

1.创建自定义AutofileService,继承AutofillService

重写两个方法:

  • onSaveRequest(SaveRequest request, SaveCallback callback)
    保存需要自动填入记录。
List<FillContext> context = request.getFillContexts();
//保存步骤:
//1.得到最近一条需要填写的表单(表单的所有内容)
AssistStructure structure = context.get(context.size() - 1).getStructure();
//2.解析记录的数据AssistStructure
//3.通过SharedPreferences,数据库,文件等存储方式保存下来
  • onFillRequest(FillRequest request, CancellationSignal cancellationSignal,
    FillCallback callback)
    执行自动填入记录。
//自动填写步骤:
//1.得到最近一条需要填写的表单(表单的所有内容)
AssistStructure structure = request.getFillContexts().get(request.getFillContexts().size() - 1).getStructure();
//2.获取保存的自动填写的表单的结果集Dataset放在FillResponse上
//3.通过FillCallback把FillResponse的内容展示到界面交互

在manifest中,声明AutofillService

<service
    <!-- 自己定义的autofillservie类名  -->android:name=".multidatasetservice.MyAutofillService"<!-- 申明权限  -->android:permission="android.permission.BIND_AUTOFILL"<!-- autofill的名字,随意设定,最终会显示在系统设置的autofill service上-->android:label="Multi-Dataset Autofill Service"><meta-data
        android:name="android.autofill"android:resource="@xml/multidataset_service" /><intent-filter><action android:name="android.service.autofill.AutofillService" /></intent-filter>
</service>

2.获取表单节点并解析AssistStructure

//从AssistStructure获取view的节点
private void parse(){}int nodes = mStructure.getWindowNodeCount();for (int i = 0; i < nodes; i++) {//得到每一个view节点WindowNode node = mStructure.getWindowNodeAt(i);ViewNode view = node.getRootViewNode();parseLocked(view);}
}//遍历保存具有autofillHints属性的view节点
private void parseLocked(ViewNode viewNode) {if (viewNode.getAutofillHints() != null && viewNode.getAutofillHints().length > 0) {//用一个map来保存这些有标记autofillHints属性的view节点,key-values的形式//key:AutofillHints - String[] //values:AutofillValue - viewNode的内容值,也就是数据内容//但是AutofillValue不能直接使用,需要封装一个对象来存储它的内容//在这里是FilledAutofillFieldmFilledAutofillFieldCollection.setAutofillValuesForHints(viewNode.getAutofillHints(), new FilledAutofillField(viewNode);}int childrenSize = viewNode.getChildCount();if (childrenSize > 0) {for (int i = 0; i < childrenSize; i++) {parseLocked(viewNode.getChildAt(i));}}
}

2.1 AutofillValue

AutofillValue就是实际记录了要自动填写的内容,看源码可见,它区分了几种类型:

  • CharSequence 字符串,list类型是CharSequence的list,也就是字符串
  • boolean 布尔类型
  • long 日期时间

AutofillValue.java

public final class AutofillValue implements Parcelable {public static final Creator<AutofillValue> CREATOR = null;AutofillValue() {throw new RuntimeException("Stub!");}public CharSequence getTextValue() {throw new RuntimeException("Stub!");}public boolean isText() {throw new RuntimeException("Stub!");}public boolean getToggleValue() {throw new RuntimeException("Stub!");}public boolean isToggle() {throw new RuntimeException("Stub!");}public int getListValue() {throw new RuntimeException("Stub!");}public boolean isList() {throw new RuntimeException("Stub!");}public long getDateValue() {throw new RuntimeException("Stub!");}public boolean isDate() {throw new RuntimeException("Stub!");}public int hashCode() {throw new RuntimeException("Stub!");}public boolean equals(Object obj) {throw new RuntimeException("Stub!");}public String toString() {throw new RuntimeException("Stub!");}public int describeContents() {throw new RuntimeException("Stub!");}public void writeToParcel(Parcel parcel, int flags) {throw new RuntimeException("Stub!");}public static AutofillValue forText(CharSequence value) {throw new RuntimeException("Stub!");}public static AutofillValue forToggle(boolean value) {throw new RuntimeException("Stub!");}public static AutofillValue forList(int value) {throw new RuntimeException("Stub!");}public static AutofillValue forDate(long value) {throw new RuntimeException("Stub!");}
}

为了方便区分这些类型,官网给的demo对这些类型再做了一层封装,详情见com.example.android.autofillframework.multidatasetservice.model.FilledAutofillField类,判断AutofillValue的内容类型并获取它的值value,得到的是string,boolean,long类型的值。

3.获取表单节点并自动填入


//从AssistStructure获取表单的所有view节点
private void parse() {int nodes = mStructure.getWindowNodeCount();for (int i = 0; i < nodes; i++) {WindowNode node = mStructure.getWindowNodeAt(i);ViewNode view = node.getRootViewNode();parseLocked(view);}
}//遍历保存具有autofillHints属性的view节点,并得到view节点的属性值
private void parseLocked(ViewNode viewNode) {if (viewNode.getAutofillHints() != null && viewNode.getAutofillHints().length > 0) {//用一个map来记录,表单的信息,key-values的形式//key:AutofillHints - String[]//values:viewNode - viewNode自己 <strong>注意这里跟保存模块不一样</strong>//下面这个代码是官网的demo,mAutofillFields是一个转载器//AutofillFieldMetadata封装了viewNode一些必要的属性://autofillHints和autofillType,autofillId等,但不包括viewNode本身//实际用途如上述key-value模式。mAutofillFields.add(new AutofillFieldMetadata(viewNode));}int childrenSize = viewNode.getChildCount();if (childrenSize > 0) {for (int i = 0; i < childrenSize; i++) {parseLocked(viewNode.getChildAt(i));}}
}

3.1准备一个RemoteView对象给自动填入list item的布局

demo中用的是textview。
xml

<TextView xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/text1"android:layout_width="fill_parent"android:layout_height="fill_parent"android:background="#ffffffff"android:gravity="center_vertical"android:minHeight="?android:attr/listPreferredItemHeightSmall"android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"android:paddingStart="?android:attr/listPreferredItemPaddingStart"android:textAppearance="?android:attr/textAppearanceListItemSmall" />
//给这个布局item显示一个名字remoteViewsText
public static RemoteViews newRemoteViews(String packageName, String remoteViewsText) {RemoteViews presentation = new RemoteViews(packageName, R.layout.multidataset_service_list_item);presentation.setTextViewText(R.id.text1, remoteViewsText);return presentation;}

3.2装载数据Dataset

把定义的RemoteView作为Dataset的布局。

Dataset.Builder datasetBuilder = new Dataset.Builder(newRemoteViews(context.getPackageName(), datasetName));

从存储的SharedPreferences,数据库,文件等存储方式中取出需要自动填入的数据,然后按照autofillHints的标记,分别Dataset传递数据,也就是进行赋值:
datasetBuilder.setValue(autofillHints的标记, 数据内容);

public boolean applyToFields(AutofillFieldMetadataCollection autofillFieldMetadataCollection,Dataset.Builder datasetBuilder) {boolean setValueAtLeastOnce = false;List<String> allHints = autofillFieldMetadataCollection.getAllHints();for (int hintIndex = 0; hintIndex < allHints.size(); hintIndex++) {String hint = allHints.get(hintIndex);List<AutofillFieldMetadata> fillableAutofillFields = autofillFieldMetadataCollection.getFieldsForHint(hint);if (fillableAutofillFields == null) {continue;}for (int autofillFieldIndex = 0; autofillFieldIndex < fillableAutofillFields.size(); autofillFieldIndex++) {FilledAutofillField filledAutofillField = mHintMap.get(hint);if (filledAutofillField == null) {continue;}AutofillFieldMetadata autofillFieldMetadata = fillableAutofillFields.get(autofillFieldIndex);AutofillId autofillId = autofillFieldMetadata.getId();int autofillType = autofillFieldMetadata.getAutofillType();switch (autofillType) {case View.AUTOFILL_TYPE_LIST:int listValue = autofillFieldMetadata.getAutofillOptionIndex(filledAutofillField.getTextValue());if (listValue != -1) {datasetBuilder.setValue(autofillId, AutofillValue.forList(listValue));setValueAtLeastOnce = true;}break;case View.AUTOFILL_TYPE_DATE:Long dateValue = filledAutofillField.getDateValue();if (dateValue != null) {datasetBuilder.setValue(autofillId, AutofillValue.forDate(dateValue));setValueAtLeastOnce = true;}break;case View.AUTOFILL_TYPE_TEXT:String textValue = filledAutofillField.getTextValue();if (textValue != null) {datasetBuilder.setValue(autofillId, AutofillValue.forText(textValue));setValueAtLeastOnce = true;}break;case View.AUTOFILL_TYPE_TOGGLE:Boolean toggleValue = filledAutofillField.getToggleValue();if (toggleValue != null) {datasetBuilder.setValue(autofillId, AutofillValue.forToggle(toggleValue));setValueAtLeastOnce = true;}break;case View.AUTOFILL_TYPE_NONE:default:Log.w(TAG, "Invalid autofill type - " + autofillType);break;}}}return setValueAtLeastOnce;
}

3.3展示给UI选择自动填入

FillResponse.Builder responseBuilder = new FillResponse.Builder();
//把每一个Dataset都存进FillResponse包住
responseBuilder.addDataset(dataset);
//传递给FillCallback的onSuccess
callback.onSuccess(responseBuilder.build());

扩展
在自动填入之前,可以在Dataset中加入一些安全密码的认证,以防autofill的数据被盗用。

datasetBuilder.setAuthentication(new Intent(安全码认证的activity));

回调监听

1.获取Autofill管理器

AutofillManager mAutofillManager = getSystemService(AutofillManager.class);

要注意Autofill的回调在Activity的生命周期情况

@Override
protected void onResume() {super.onResume();mAutofillManager.registerCallback(mAutofillCallback);
}@Override
protected void onPause() {super.onPause();mAutofillManager.unregisterCallback(mAutofillCallback);
}

2.Autofill的回调

AutofillManager.AutofillCallback mAutofillCallback = new AutofillManager.AutofillCallback() {@Overridepublic void onAutofillEvent(View view, int event) {super.onAutofillEvent(view, event);if (view instanceof AutoCompleteTextView) {switch (event) {//当autofill不可用case AutofillManager.AutofillCallback.EVENT_INPUT_UNAVAILABLE:break;//当autofill隐藏case AutofillManager.AutofillCallback.EVENT_INPUT_HIDDEN:break;//当autofill显示case AutofillManager.AutofillCallback.EVENT_INPUT_SHOWN:break;default:Log.d(TAG, "Unexpected callback: " + event);}}}@Overridepublic void onAutofillEvent(View view, int virtualId, int event) {super.onAutofillEvent(view, virtualId, event);//事件类型同onAutofillEvent(View view, int event)}
});

自定义View加入autofill的支持

除了要在指定的子view中设置autofillHints标签以外,还需要重写两个View的方法:

  • onProvideAutofillVirtualStructure(ViewStructure structure,int flags)
    保存autofill的数据
@Override
public void onProvideAutofillVirtualStructure(ViewStructure structure,int flags) {super.onProvideAutofillVirtualStructure(structure, flags);//创建一个VirtualStructure对象structure.setClassName(getClass().getName());//mVirtualViews是一个装载子view属性的集合int childrenSize = mVirtualViews.size();int index = structure.addChildCount(childrenSize);//关联autofill和viewstructurefor (int i = 0; i < childrenSize; i++) {Item item = mVirtualViews.valueAt(i);//创建ViewStructure跟子view的属性关联ViewStructure child = structure.newChild(index);child.setAutofillId(structure.getAutofillId(), item.id);child.setAutofillHints(item.hints);child.setAutofillType(item.type);child.setDataIsSensitive(!item.sanitized);child.setText(item.text);child.setAutofillValue(AutofillValue.forText(item.text));child.setFocused(item.focused);child.setId(item.id, getContext().getPackageName(), null, item.line.idEntry);child.setClassName(item.getClassName());index++;}//contentIsSetFromResources是否为resouces的静态数据boolean sensitive = !contentIsSetFromResources();// 设置是否为敏感信息,默认是child.setDataIsSensitive(sensitive);
}
  • autofill(SparseArray values)
    将自动填写的信息展示到自定义view中,通过

AutofillValue value = values.valueAt(i);
//得到自定义autofill用户选中的值
customView.item.setXXX(value.getXXXValue());

其他


强制autofill

public void eventHandler(View view) {AutofillManager afm = context.getSystemService(AutofillManager.class);if (afm != null) {afm.requestAutofill();}
}

检查autofill是否可用


AutofillManager afm = context.getSystemService(AutofillManager.class);
if(afm.isEnable()){//可用
}

【安卓】——Autofill Framework(自动填写)用法详解相关推荐

  1. Autofill Framework(自动填写)用法详解

    文/arjinmc 本文是基于官方demo来分析Autofill Framework的用法(要正常打开这个项目请使用Android Studio Preview 3.0以上版本,并下载Anroid O ...

  2. linux useradd(adduser)命令参数及用法详解(linux创建新用户命令)

    linux useradd(adduser)命令参数及用法详解(linux创建新用户命令) useradd可用来建立用户帐号.帐号建好之后,再用passwd设定帐号的密码.而可用userdel删除帐号 ...

  3. jQuery 表单验证插件,jQuery Validation Engine用法详解

    jQuery 表单验证插件,jQuery Validation Engine用法详解 功能强大的 jQuery 表单验证插件,适用于日常的 E-mail.电话号码.网址等验证及 Ajax 验证,除自身 ...

  4. EXCEL公式VLOOKUP函数用法详解

    EXCEL公式VLOOKUP函数用法详解 示例下载 VLOOKUP函数 在表格或数值数组的首列查找指定的数值,并由此返回表格或数组中该数值所在行中指定列处的数值. 这里所说的"数组" ...

  5. android默认exported_Android android:exported = true 用法详解

    Android android:exported = true 用法详解 Android相关属性的介绍:android:exported = true 在Activity中该属性用来标示:当前Acti ...

  6. html a href=mailto 发件人怎么设置,a标签创建mailto链接发送电子邮箱用法详解

    在html5中,利用标签的mailto可以创建发送邮件到一个或多个电子邮箱的超链接功能,其用法详解如下: 标签mailto最常见用法 这个用法是最常见的用法,在大多数情况下,我们都会使用这个方式发送电 ...

  7. linux mount命令参数及用法详解

    linux mount命令参数及用法详解 非原创,主要来自 http://www.360doc.com/content/13/0608/14/12600778_291501907.shtml. htt ...

  8. Linux Shell脚本入门--wget 命令用法详解

    Linux Shell脚本入门--wget 命令用法详解 wget是在Linux下开发的开放源代码的软件,作者是Hrvoje Niksic,后来被移植到包括Windows在内的各个平台上.它有以下功能 ...

  9. 教程-Delphi中Spcomm使用属性及用法详解

    Delphi中Spcomm使用属性及用法详解 Delphi是一种具有 功能强大.简便易用和代码执行速度快等优点的可视化快速应用开发工具,它在构架企业信息系统方面发挥着越来越重要的作用,许多程序员愿意选 ...

最新文章

  1. Linux之文件权限管理
  2. 「Smile」一下,轻松用Java玩转机器学习
  3. Android SlidingMenu 开源项目 侧拉菜单的使用(详细配置)
  4. 为什么忘记commit也会造成select查询的性能问题
  5. mysql列属性auto(mysql笔记四)
  6. 【lucene】lucene 高级搜索
  7. 为并发而生的 ConcurrentHashMap,基于 Java8 分析
  8. ionic 组件之二维码扫描
  9. POJ 1221 UNIMODAL PALINDROMIC DECOMPOSITIONS
  10. 未在此计算机上注册ActiveX控件!!!
  11. ADC芯片CS1242用在电子秤单片机上面
  12. 手机百度脑图app_百度脑图下载-百度脑图app手机安卓版下载-沧浪手游
  13. 月播放量增长2300倍,品牌如何在B站迅速打造爆品?
  14. Leet Code题(2)——整数反转
  15. 看网易的lofter,预测轻博客的未来
  16. iOS CoreData的使用
  17. 用word字体转换来代替手写作业--最详细
  18. Excel做数据海报
  19. 使用numpy计算准确率
  20. Chris and Magic Square CodeForces - 711B

热门文章

  1. Javascript-循环
  2. Call to undefined function bcadd()
  3. Hive 插入数据报错FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.exec.mr.MapRedTask.
  4. JavaScript - this指向以及强行改变this指向
  5. 【Linux进程间通信】二、pipe管道
  6. iPhone手机热点连接不稳定
  7. 腾讯X5 浏览器内核加载
  8. 学海泛舟系列文章开篇语
  9. Mac系统brew install 安装报错 Error: Failure while executing
  10. Zabbix 3.4配置监控项及监控Linux、Windows客户端