【安卓】——Autofill Framework(自动填写)用法详解
本文是基于官方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(自动填写)用法详解相关推荐
- Autofill Framework(自动填写)用法详解
文/arjinmc 本文是基于官方demo来分析Autofill Framework的用法(要正常打开这个项目请使用Android Studio Preview 3.0以上版本,并下载Anroid O ...
- linux useradd(adduser)命令参数及用法详解(linux创建新用户命令)
linux useradd(adduser)命令参数及用法详解(linux创建新用户命令) useradd可用来建立用户帐号.帐号建好之后,再用passwd设定帐号的密码.而可用userdel删除帐号 ...
- jQuery 表单验证插件,jQuery Validation Engine用法详解
jQuery 表单验证插件,jQuery Validation Engine用法详解 功能强大的 jQuery 表单验证插件,适用于日常的 E-mail.电话号码.网址等验证及 Ajax 验证,除自身 ...
- EXCEL公式VLOOKUP函数用法详解
EXCEL公式VLOOKUP函数用法详解 示例下载 VLOOKUP函数 在表格或数值数组的首列查找指定的数值,并由此返回表格或数组中该数值所在行中指定列处的数值. 这里所说的"数组" ...
- android默认exported_Android android:exported = true 用法详解
Android android:exported = true 用法详解 Android相关属性的介绍:android:exported = true 在Activity中该属性用来标示:当前Acti ...
- html a href=mailto 发件人怎么设置,a标签创建mailto链接发送电子邮箱用法详解
在html5中,利用标签的mailto可以创建发送邮件到一个或多个电子邮箱的超链接功能,其用法详解如下: 标签mailto最常见用法 这个用法是最常见的用法,在大多数情况下,我们都会使用这个方式发送电 ...
- linux mount命令参数及用法详解
linux mount命令参数及用法详解 非原创,主要来自 http://www.360doc.com/content/13/0608/14/12600778_291501907.shtml. htt ...
- Linux Shell脚本入门--wget 命令用法详解
Linux Shell脚本入门--wget 命令用法详解 wget是在Linux下开发的开放源代码的软件,作者是Hrvoje Niksic,后来被移植到包括Windows在内的各个平台上.它有以下功能 ...
- 教程-Delphi中Spcomm使用属性及用法详解
Delphi中Spcomm使用属性及用法详解 Delphi是一种具有 功能强大.简便易用和代码执行速度快等优点的可视化快速应用开发工具,它在构架企业信息系统方面发挥着越来越重要的作用,许多程序员愿意选 ...
最新文章
- Linux之文件权限管理
- 「Smile」一下,轻松用Java玩转机器学习
- Android SlidingMenu 开源项目 侧拉菜单的使用(详细配置)
- 为什么忘记commit也会造成select查询的性能问题
- mysql列属性auto(mysql笔记四)
- 【lucene】lucene 高级搜索
- 为并发而生的 ConcurrentHashMap,基于 Java8 分析
- ionic 组件之二维码扫描
- POJ 1221 UNIMODAL PALINDROMIC DECOMPOSITIONS
- 未在此计算机上注册ActiveX控件!!!
- ADC芯片CS1242用在电子秤单片机上面
- 手机百度脑图app_百度脑图下载-百度脑图app手机安卓版下载-沧浪手游
- 月播放量增长2300倍,品牌如何在B站迅速打造爆品?
- Leet Code题(2)——整数反转
- 看网易的lofter,预测轻博客的未来
- iOS CoreData的使用
- 用word字体转换来代替手写作业--最详细
- Excel做数据海报
- 使用numpy计算准确率
- Chris and Magic Square CodeForces - 711B
热门文章
- Javascript-循环
- Call to undefined function bcadd()
- Hive 插入数据报错FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.exec.mr.MapRedTask.
- JavaScript - this指向以及强行改变this指向
- 【Linux进程间通信】二、pipe管道
- iPhone手机热点连接不稳定
- 腾讯X5 浏览器内核加载
- 学海泛舟系列文章开篇语
- Mac系统brew install 安装报错 Error: Failure while executing
- Zabbix 3.4配置监控项及监控Linux、Windows客户端