10、人脸识别UI定制:自定义认证对话框、使用PromptBuilder、设置动画与主题
说实话,BiometricPrompt 自带的那个对话框,长得确实有点「原生」。圆角、图标、按钮样式,都是系统默认的。我刚开始做项目时,产品经理看了一眼就说:「这对话框跟我们 App 的风格完全不搭啊。」
嗯,这确实是个问题。但好消息是,Android 给了我们足够的定制空间。今天我们就来聊聊,怎么把人脸识别的 UI 做得跟 App 浑然一体。
10.1 系统默认对话框长什么样?
先看看默认的 BiometricPrompt 对话框。它分两种模式:
- 系统弹窗模式:一个居中的圆角卡片,顶部是生物识别图标,中间是提示文字,底部是「取消」按钮
- 全屏模式:用于 Android 10+,在折叠屏或平板上体验更好
默认的样式其实不丑,但问题是——它没法改。你不能直接改那个对话框的背景色、字体、按钮样式。那怎么办?
我个人的做法是:完全放弃系统对话框,自己写一个。
10.2 使用 PromptBuilder 定制
BiometricPrompt.PromptBuilder 提供了几个关键方法,能让我们「半定制」对话框:
BiometricPrompt.PromptBuilder builder = new BiometricPrompt.PromptBuilder(context)
.setTitle("验证身份")
.setSubtitle("请面向手机,完成人脸识别")
.setDescription("用于登录和支付操作")
.setNegativeButton("取消", executor, (dialog, which) -> {
// 用户点击取消
})
.setConfirmationRequired(false); // 是否需要手动确认
这里有个坑,我踩过。setConfirmationRequired 这个参数,默认是 true。什么意思?就是人脸识别成功后,系统还会弹一个「确认」按钮,让用户手动点一下。你想想看,人脸识别都成功了,还要再点一下,这体验多割裂。
所以我的建议是:如果 minSdk 低于 30,就别依赖这个参数了,直接用自定义对话框。
10.3 完全自定义认证对话框
这才是真正的「定制」。思路很简单:
- 自己写一个 Activity 或 DialogFragment
- 在里面调用 BiometricManager 进行认证
- 把认证结果通过回调传给调用方
来看代码:
public class CustomBiometricDialog extends DialogFragment {
private BiometricPrompt biometricPrompt;
private BiometricCallback callback;
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// 完全自定义布局
View view = LayoutInflater.from(getContext())
.inflate(R.layout.dialog_biometric, null);
AlertDialog dialog = new AlertDialog.Builder(getContext())
.setView(view)
.create();
// 设置圆角背景
dialog.getWindow().setBackgroundDrawable(
new ColorDrawable(Color.TRANSPARENT));
initBiometric();
return dialog;
}
private void initBiometric() {
Executor executor = ContextCompat.getMainExecutor(getContext());
biometricPrompt = new BiometricPrompt(this, executor,
new BiometricPrompt.AuthenticationCallback() {
@Override
public void onAuthenticationSucceeded(
BiometricPrompt.AuthenticationResult result) {
// 成功:关闭对话框,回调结果
dismiss();
callback.onSuccess();
}
@Override
public void onAuthenticationError(int errorCode,
CharSequence errString) {
// 错误:显示错误信息
showError(errString.toString());
}
@Override
public void onAuthenticationFailed() {
// 失败:人脸不匹配
showError("人脸识别失败,请重试");
}
});
// 启动认证
BiometricPrompt.PromptInfo promptInfo =
new BiometricPrompt.PromptInfo.Builder()
.setTitle("") // 标题为空,因为我们自己显示
.setSubtitle("")
.setDescription("")
.setNegativeButtonText("取消")
.build();
biometricPrompt.authenticate(promptInfo);
}
}
布局文件 dialog_biometric.xml 就完全由你掌控了:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="280dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@drawable/bg_dialog_rounded"
android:padding="24dp">
<ImageView
android:id="@+id/iv_face_icon"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_gravity="center"
android:src="@drawable/ic_face_scan" />
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="验证身份"
android:textSize="18sp"
android:textStyle="bold"
android:gravity="center"
android:layout_marginTop="16dp" />
<TextView
android:id="@+id/tv_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="请面向手机"
android:textSize="14sp"
android:gravity="center"
android:layout_marginTop="8dp" />
<Button
android:id="@+id/btn_cancel"
android:layout_width="match_parent"
android:layout_height="48dp"
android:text="取消"
android:layout_marginTop="24dp"
android:backgroundTint="@color/btn_cancel" />
</LinearLayout>
10.4 设置动画与主题
自定义对话框的动画,主要分两块:
- 对话框进出动画:用 Dialog 的 window 设置
- 人脸扫描动画:用 Lottie 或帧动画
对话框动画示例:
// 在 DialogFragment 的 onStart 中设置
@Override
public void onStart() {
super.onStart();
Dialog dialog = getDialog();
if (dialog != null) {
Window window = dialog.getWindow();
if (window != null) {
window.setWindowAnimations(R.style.DialogAnimation);
}
}
}
// styles.xml
<style name="DialogAnimation">
<item name="android:windowEnterAnimation">@anim/slide_in_up</item>
<item name="android:windowExitAnimation">@anim/slide_out_down</item>
</style>
至于主题,我建议在 Application 或 Activity 的主题中,统一设置对话框样式:
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="android:alertDialogTheme">@style/CustomDialogTheme</item>
</style>
<style name="CustomDialogTheme" parent="ThemeOverlay.MaterialComponents.Dialog">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowMinWidthMajor">85%</item>
<item name="android:windowMinWidthMinor">90%</item>
</style>
10.5 核心逻辑流程图
下面这张图,是我自己整理的自定义认证流程,你看一眼就明白了:
10.6 避坑指南
做自定义对话框时,有几个坑我替你们踩过了:
我曾经在 DialogFragment 的 onCreateView 里初始化 BiometricPrompt,结果发现对话框还没显示,认证就开始了。正确的做法是在 onStart 或 onResume 里初始化。
自定义对话框在横竖屏切换时,默认会重建。如果你不处理,认证状态就丢了。我习惯在 onSaveInstanceState 里保存一个标志位,重建后重新初始化。
嗯,UI 定制这块就聊这么多。说白了,核心思路就是:用系统能力做认证,用自己的 UI 做展示。两者结合,既安全又好看。
本节要点回顾:
- PromptBuilder 支持半定制:标题、副标题、描述、取消按钮
- 完全自定义:用 DialogFragment + BiometricPrompt 组合
- 动画用 Lottie 或帧动画,主题用 AlertDialog 主题覆盖
- 注意生命周期和横竖屏切换的坑
公众号:蓝海资料掘金营,微信deep3321