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;
    }
相关推荐
凯文的内存31 分钟前
Android14 OTA升级速度过慢问题解决方案
android·ota·update engine·系统升级·virtual ab
VinRichard35 分钟前
Android 常用三方库
android
Aileen_0v02 小时前
【玩转OCR | 腾讯云智能结构化OCR在图像增强与发票识别中的应用实践】
android·java·人工智能·云计算·ocr·腾讯云·玩转腾讯云ocr
江上清风山间明月5 小时前
Flutter DragTarget拖拽控件详解
android·flutter·ios·拖拽·dragtarget
debug_cat7 小时前
AndroidStudio Ladybug中编译完成apk之后定制名字kts复制到指定目录
android·android studio
编程洪同学11 小时前
Spring Boot 中实现自定义注解记录接口日志功能
android·java·spring boot·后端
氤氲息14 小时前
Android 底部tab,使用recycleview实现
android
Clockwiseee14 小时前
PHP之伪协议
android·开发语言·php
小林爱14 小时前
【Compose multiplatform教程08】【组件】Text组件
android·java·前端·ui·前端框架·kotlin·android studio
小何开发15 小时前
Android Studio 安装教程
android·ide·android studio