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;
    }
相关推荐
Kapaseker5 小时前
你不看会后悔的2025年终总结
android·kotlin
alexhilton8 小时前
务实的模块化:连接模块(wiring modules)的妙用
android·kotlin·android jetpack
ji_shuke9 小时前
opencv-mobile 和 ncnn-android 环境配置
android·前端·javascript·人工智能·opencv
sunnyday042611 小时前
Spring Boot 项目中使用 Dynamic Datasource 实现多数据源管理
android·spring boot·后端
幽络源小助理12 小时前
下载安装AndroidStudio配置Gradle运行第一个kotlin程序
android·开发语言·kotlin
inBuilder低代码平台12 小时前
浅谈安卓Webview从初级到高级应用
android·java·webview
豌豆学姐12 小时前
Sora2 短剧视频创作中如何保持人物一致性?角色创建接口教程
android·java·aigc·php·音视频·uniapp
白熊小北极13 小时前
Android Jetpack Compose折叠屏感知与适配
android
HelloBan13 小时前
setHintTextColor不生效
android
洞窝技术15 小时前
从0到30+:智能家居配网协议融合的实战与思考
android