android13#autofill

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设置的页面
    image.png

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.保存数据方法

有两种:

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

>3.保存数据的弹框

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

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的编辑框,就会弹出我们设置的提示视图,如下图
image.png

>2.填充后效果

点击提示按钮后,数据就会被自动填充,填充后有个高亮的背景显示,这时候修改下数据,背景颜色就恢复了
image.png

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,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?