1.简介
android的自动填充功能自定义简单学习
1.1.如何实现
- 添加一个服务,继承AutofillService,需要重写两个方法,一个用来保存数据,一个用来读取数据
- 添加一个配置页面,非必须
- 清单文件配置,参考1.2
1.2.清单文件配置
- label就是自动填充服务显示的名字,不写的话就是app的名字,参考1.3.1的图片
- permission是必须的固定的
- action也是必须的固定的
- meta-data指定配置文件,非必须,指定配置页面,1.3.1的图片里会显示配置按钮,否则不显示
name=".ui.MyAutofillSettings"
android:exported="true" />
name=".service.MyAutofillService"
android:exported="true"
android:label="myautofill"
android:permission="android.permission.BIND_AUTOFILL_SERVICE">
name="android.service.autofill.AutofillService" />
name="android.autofill"
android:resource="@xml/service_configuration" />
>1.服务配置
service_configuration.xml
- 主要配置settingsActivity的值,就是我们自己写的配置页面
"1.0" encoding="utf-8"?>
<autofill-service xmlns:android="http://schemas.android.com/apk/res/android"
android:settingsActivity="com.example.myapp2023.ui.MyAutofillSettings" />
1.3.查看
settings >> Passwords&accounts >> Autofill servcie
>1.图片
- 点击1进去可以选择不同的自动填充服务,
- 点击2就进入对应的填充服务的设置页面,就是1.2.1设置的页面
1.4.使用
>1.启用
- 先参考1.3.1把autofill service改成我们自己的,也可以代码里通过Intent跳转到对应的页面设置
- 随便一个页面的编辑框,如果设置了autofill属性,那么当编辑框聚焦的时候,就会触发2.1
1.5.测试
>1.布局
- EditText 添加 autofillHints
- EditText 添加 importantForAutofill,参考小节3
android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="5dp">
id="@+id/et_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="user name"
android:autofillHints="username"
android:importantForAutofill="yes"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:hint="password"
android:autofillHints="password"
android:importantForAutofill="yes"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/et_name" />
>2.保存数据方法
有两种:
- 退出页面,如果数据发生了变化,系统会自动弹框提示的,见补充3
- 手动操作,调用autofillManager的commit方法即可。
val autofillManager =getSystemService(AutofillManager::class.java)
findViewById(R.id.btn_submit).setOnClickListener {
autofillManager.commit()
}
>3.保存数据的弹框
点击update,那么会执行2.2的方法,如果点击No thanks,那么不会走2.2的方法
2.AutofillService
open class MyAutofillService : AutofillService() {
private val fillId = HashMap()
private lateinit var sp: SharedPreferences
private var saveSp = false
override fun onCreate() {
super.onCreate()
sp = PreferenceManager.getDefaultSharedPreferences(baseContext);
}
2.1.onFillRequest
- SaveInfo就是设置我们要保存哪些数据,参考1.5.3标题会显示保存哪些数据
- Dataset就是我们要填充啥数据
- AutofillValue有四种类型,参考2.4以及3.3,这里就演示了下text的
override fun onFillRequest(request: FillRequest, cancellationSignal: CancellationSignal, callback: FillCallback) {
saveSp = false
parseStructure(request.fillContexts.last().structure)
val dataset = Dataset.Builder().apply {
var check = false
val presentation = RemoteViews(packageName, android.R.layout.simple_list_item_1).apply {
setTextViewText(android.R.id.text1, "myAutofill")
}
fillId[View.AUTOFILL_HINT_USERNAME]?.apply {
sp.getString(View.AUTOFILL_HINT_USERNAME, null)?.let {
setValue(this, AutofillValue.forText(it), presentation)
check = true
}
}
fillId[View.AUTOFILL_HINT_PASSWORD]?.apply {
sp.getString(View.AUTOFILL_HINT_PASSWORD, null)?.let {
setValue(this, AutofillValue.forText(it), presentation)
check = true
}
}
if (!check) {
setValue(fillId.values.last(), AutofillValue.forText(""))
}
}.build()
val fillResponse = FillResponse.Builder()
.setSaveInfo(SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_USERNAME or SaveInfo.SAVE_DATA_TYPE_PASSWORD,
fillId.values.toTypedArray()).build()).addDataset(dataset).build()
callback.onSuccess(fillResponse)
}
>1.提示视图
随便点击一个支持autofill的编辑框,就会弹出我们设置的提示视图,如下图
>2.填充后效果
点击提示按钮后,数据就会被自动填充,填充后有个高亮的背景显示,这时候修改下数据,背景颜色就恢复了
2.2.onSaveRequest
- 这个方法就是用来保存数据的。
- 解析所有的节点,如果支持autofill,那么我们保存对应的数据,至于数据咋保存那是你的事了。
- 我这里测试用的sp
override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) {
saveSp = true
parseStructure(request.fillContexts.last().structure)
callback.onSuccess()
}
2.3.parseStructure
private fun parseStructure(structure: AssistStructure) {
(0 until structure.windowNodeCount).forEach {
val node = structure.getWindowNodeAt(it).rootViewNode
parseNode(node)
}
}
private fun parseNode(node: ViewNode) {
if (node.autofillHints?.isNotEmpty() == true) {
node.autofillHints?.forEach {
if (saveSp) {
node.text?.apply {
sp.edit().putString(it, this.toString()).commit()
}
}
node.autofillId?.apply {
fillId.put(it, this)
}
}
}
if (node.childCount > 0) {
(0 until node.childCount).forEach {
parseNode(node.getChildAt(it))
}
}
}
2.4.AutofillValue
>1.forText
public static AutofillValue forText(@Nullable CharSequence value) {
return value == null ? null : new AutofillValue(AUTOFILL_TYPE_TEXT,
TextUtils.trimNoCopySpans(value));
}
>2.forToggle
public static AutofillValue forToggle(boolean value) {
return new AutofillValue(AUTOFILL_TYPE_TOGGLE, value);
}
>3.forList
public static AutofillValue forList(int value) {
return new AutofillValue(AUTOFILL_TYPE_LIST, value);
}
>4.forDate
public static AutofillValue forDate(long value) {
return new AutofillValue(AUTOFILL_TYPE_DATE, value);
}
3.View
3.1.autofillHint
public static final String AUTOFILL_HINT_EMAIL_ADDRESS = "emailAddress";
public static final String AUTOFILL_HINT_NAME = "name";
public static final String AUTOFILL_HINT_USERNAME = "username";
public static final String AUTOFILL_HINT_PASSWORD = "password";
public static final String AUTOFILL_HINT_PHONE = "phone";
public static final String AUTOFILL_HINT_POSTAL_ADDRESS = "postalAddress";
public static final String AUTOFILL_HINT_POSTAL_CODE = "postalCode";
public static final String AUTOFILL_HINT_CREDIT_CARD_NUMBER = "creditCardNumber";
public static final String AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE = "creditCardSecurityCode";
public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE =
"creditCardExpirationDate";
public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH =
"creditCardExpirationMonth";
public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR =
"creditCardExpirationYear";
public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY = "creditCardExpirationDay";
public static final String AUTOFILL_HINT_PASSWORD_AUTO = "passwordAuto";
3.2.IMPORTANT_FOR_AUTOFILL
public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0x0;
public static final int IMPORTANT_FOR_AUTOFILL_YES = 0x1;
public static final int IMPORTANT_FOR_AUTOFILL_NO = 0x2;
public static final int IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS = 0x4;
public static final int IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS = 0x8;
3.3.AutofillType
@IntDef(prefix = { "AUTOFILL_TYPE_" }, value = {
AUTOFILL_TYPE_NONE,
AUTOFILL_TYPE_TEXT,
AUTOFILL_TYPE_TOGGLE,
AUTOFILL_TYPE_LIST,
AUTOFILL_TYPE_DATE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface AutofillType {}
>1.View
默认的自动填充类型是NONE,其他子类覆写这个方法
public @AutofillType int getAutofillType() {
return AUTOFILL_TYPE_NONE;
}
>2.RadioGroup|AbsSpinner
public @AutofillType int getAutofillType() {
return isEnabled() ? AUTOFILL_TYPE_LIST : AUTOFILL_TYPE_NONE;
}
>3.TextView
public @AutofillType int getAutofillType() {
return isTextEditable() ? AUTOFILL_TYPE_TEXT : AUTOFILL_TYPE_NONE;
}
>4.CompoundButton
public @AutofillType int getAutofillType() {
return isEnabled() ? AUTOFILL_TYPE_TOGGLE : AUTOFILL_TYPE_NONE;
}
>5.DatePicker|TimePicker
public @AutofillType int getAutofillType() {
return isEnabled() ? AUTOFILL_TYPE_DATE : AUTOFILL_TYPE_NONE;
}
阅读全文
下载说明:
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.dandroid.cn/archives/22416,转载请注明出处。
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.dandroid.cn/archives/22416,转载请注明出处。
评论0