android13#autofill

1.简介

android的自动填充功能自定义简单学习

1.1.如何实现

  • 添加一个服务,继承AutofillService,需要重写两个方法,一个用来保存数据,一个用来读取数据
  • 添加一个配置页面,非必须
  • 清单文件配置,参考1.2

1.2.清单文件配置

  • label就是自动填充服务显示的名字,不写的话就是app的名字,参考1.3.1的图片
  • permission是必须的固定的
  • action也是必须的固定的
  • meta-data指定配置文件,非必须,指定配置页面,1.3.1的图片里会显示配置按钮,否则不显示
ini 复制代码
<activity
    android:name=".ui.MyAutofillSettings"
    android:exported="true" />

<service
    android:name=".service.MyAutofillService"
    android:exported="true"
    android:label="myautofill"
    android:permission="android.permission.BIND_AUTOFILL_SERVICE">
    <intent-filter>
        <action android:name="android.service.autofill.AutofillService" />
    </intent-filter>
    <meta-data
        android:name="android.autofill"
        android:resource="@xml/service_configuration" />
</service>

>1.服务配置

service_configuration.xml

  • 主要配置settingsActivity的值,就是我们自己写的配置页面
xml 复制代码
<?xml version="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
ini 复制代码
<androidx.constraintlayout.widget.ConstraintLayout xmlns: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">

    <EditText
        android: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" />

    <EditText
        android: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" />

    <Button
        android:id="@+id/btn_submit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="submit"
        android:layout_marginTop="10dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/et_password" />
</androidx.constraintlayout.widget.ConstraintLayout>

>2.保存数据方法

有两种:

  1. 退出页面,如果数据发生了变化,系统会自动弹框提示的,见补充3
  2. 手动操作,调用autofillManager的commit方法即可。
scss 复制代码
val autofillManager =getSystemService(AutofillManager::class.java)
findViewById<View>(R.id.btn_submit).setOnClickListener {
    autofillManager.commit()
}

>3.保存数据的弹框

点击update,那么会执行2.2的方法,如果点击No thanks,那么不会走2.2的方法

2.AutofillService

  • 自定义的服务
kotlin 复制代码
open class MyAutofillService : AutofillService() {
//key就是autofillHint的值,value就是编辑框的id,
    private val fillId = HashMap<String, AutofillId>()

    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的
scss 复制代码
override fun onFillRequest(request: FillRequest, cancellationSignal: CancellationSignal, callback: FillCallback) {
    saveSp = false
    //只处理填充上下文集合的最后一条数据,就是我们当前的页面数据
    parseStructure(request.fillContexts.last().structure)//参考2.3

    val dataset = Dataset.Builder().apply {
        var check = false
        //这个就是提示视图,参考补充1
        val presentation = RemoteViews(packageName, android.R.layout.simple_list_item_1).apply {
            setTextViewText(android.R.id.text1, "myAutofill")
        }
        //找到username类型的AutofillId,如果本地有保存数据的话,那么设置数据
        fillId[View.AUTOFILL_HINT_USERNAME]?.apply {
            sp.getString(View.AUTOFILL_HINT_USERNAME, null)?.let {
            //这里就是自动填充的数据,第一个是控件id,第二个是要填充的数据,第三个是提示框
                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
            }
        }
        //数据为空,build方法会报错,所以这里加了个空数据
        //你也可以先判断是否有已保存的数据再决定是否创建dataset
        if (!check) {
            setValue(fillId.values.last(), AutofillValue.forText(""))
        }
    }.build()

    val fillResponse = FillResponse.Builder()
    //SaveInfo,第一个参数是支持保存的数据类型,第二个参数是要保存的节点AutofillId集合
        .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
kotlin 复制代码
override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) {
    saveSp = true
    //参考2.3,和2.1一样都是解析所有的节点,这里就是保存下数据
    parseStructure(request.fillContexts.last().structure)
    callback.onSuccess()
}

2.3.parseStructure

kotlin 复制代码
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)//把节点的autofillHint和id放入map
            }
        }
    }
//迭代解析
    if (node.childCount > 0) {
        (0 until node.childCount).forEach {
            parseNode(node.getChildAt(it))
        }
    }
}

2.4.AutofillValue

>1.forText

less 复制代码
public static AutofillValue forText(@Nullable CharSequence value) {

    return value == null ? null : new AutofillValue(AUTOFILL_TYPE_TEXT,
            TextUtils.trimNoCopySpans(value));
}

>2.forToggle

typescript 复制代码
public static AutofillValue forToggle(boolean value) {
    return new AutofillValue(AUTOFILL_TYPE_TOGGLE, value);
}

>3.forList

arduino 复制代码
public static AutofillValue forList(int value) {
    return new AutofillValue(AUTOFILL_TYPE_LIST, value);
}

>4.forDate

arduino 复制代码
public static AutofillValue forDate(long value) {
    return new AutofillValue(AUTOFILL_TYPE_DATE, value);
}

3.View

3.1.autofillHint

java 复制代码
//邮件地址
public static final String AUTOFILL_HINT_EMAIL_ADDRESS = "emailAddress";

//a user's real name.
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";

//信用卡过期时间,月份,建议用数字,从1开始
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";

//自动密码,就是说用户没有设置上边的AUTOFILL_HINT_PASSWORD,系统自己添加的这个
public static final String AUTOFILL_HINT_PASSWORD_AUTO = "passwordAuto";

3.2.IMPORTANT_FOR_AUTOFILL

arduino 复制代码
//系统自己决定
public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0x0;

//当前view以及遍历它的child都是支持autofill
public static final int IMPORTANT_FOR_AUTOFILL_YES = 0x1;

//此视图不需要自动填充,但是遍历它的child
public static final int IMPORTANT_FOR_AUTOFILL_NO = 0x2;

//自己支持自动填充,不包括它的child
public static final int IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS = 0x4;

//此视图不支持自动填充,不包括它的child
public static final int IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS = 0x8;

3.3.AutofillType

less 复制代码
@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,其他子类覆写这个方法

csharp 复制代码
   public @AutofillType int getAutofillType() {
        return AUTOFILL_TYPE_NONE;
    }

>2.RadioGroup|AbsSpinner

csharp 复制代码
    public @AutofillType int getAutofillType() {
        return isEnabled() ? AUTOFILL_TYPE_LIST : AUTOFILL_TYPE_NONE;
    }

>3.TextView

csharp 复制代码
    public @AutofillType int getAutofillType() {
        return isTextEditable() ? AUTOFILL_TYPE_TEXT : AUTOFILL_TYPE_NONE;
    }

>4.CompoundButton

csharp 复制代码
    public @AutofillType int getAutofillType() {
        return isEnabled() ? AUTOFILL_TYPE_TOGGLE : AUTOFILL_TYPE_NONE;
    }

>5.DatePicker|TimePicker

csharp 复制代码
    public @AutofillType int getAutofillType() {
        return isEnabled() ? AUTOFILL_TYPE_DATE : AUTOFILL_TYPE_NONE;
    }
相关推荐
服装学院的IT男8 分钟前
【Android 14源码分析】Activity启动流程-1
android
拉玛干39 分钟前
gradle的入门及kotlin的了解
android·开发语言·kotlin
Jay的小提琴3 小时前
第十章 XML
android·xml·java·开发语言·笔记·idea
GEEKVIP3 小时前
升级 Windows 后如何恢复丢失的文件
android·windows·安全·macos·智能手机·电脑·笔记本电脑
Junerver5 小时前
在 Jetpack Compose 中扩展 useRequest 实现自定义数据处理、异常回滚
android·前端·android jetpack
龚礼鹏6 小时前
android SELinux权限适配
android
L72566 小时前
Android12的netd分析
android
Lucky小小吴7 小时前
C语言解析软链接,获得真实路径
android·c语言·开发语言
图王大胜8 小时前
Android build子系统(01)Ninja构建系统解读
android·gn·ninja·编译子系统·kati·soong