Android 全屏6位密码输入框:优化布局与功能实现

功能概述

自动验证:输入第6位密码后自动验证。

删除键优化:按下删除键时,如果当前输入框为空,则回退到上一个输入框并清空内容。

密码可见性切换:点击"显示密码"按钮,可以切换密码的可见性。

**震动反馈:**密码错误时提供震动反馈。

输入框抖动动画:密码错误时,输入框抖动提示用户。

实现步骤
布局文件:定义主界面和弹框布局,调整输入框间隙为 3dp。

**样式文件:**为输入框添加边框样式。

动画文件:实现输入框抖动动画。

**逻辑处理:**优化删除键逻辑、自动验证、密码可见性切换等功能。

代码结构
布局文件:

activity_my_number_edit.xml:主界面布局。

dialog_password.xml:密码输入弹框布局。

样式文件

edittext_border.xml:输入框边框样式。

动画文件:

shake.xml:输入框抖动动画。

逻辑处理:

MyNumberEditActivity.java:实现弹框显示、密码验证、删除键优化等功能。

1. 主界面布局文件 (activity_my_number_edit.xml)

主界面只有一个按钮,点击后弹出密码输入框:

复制代码
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center">

    <Button
        android:id="@+id/btnShowPasswordDialog"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="输入密码"/>
</LinearLayout>

2. 密码输入框布局文件 (dialog_password.xml)

弹框布局,密码输入框之间的间隙为 3dp,并支持全屏显示:

复制代码
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    android:gravity="center"
    android:background="@android:color/white">

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="请输入6位密码"
        android:textSize="18sp"
        android:layout_marginBottom="16dp"/>

    <LinearLayout
        android:id="@+id/passwordContainer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center">

        <EditText
            android:id="@+id/et1"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:maxLength="1"
            android:inputType="numberPassword"
            android:gravity="center"
            android:background="@drawable/edittext_border"
            android:layout_marginEnd="3dp"/>

        <EditText
            android:id="@+id/et2"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:maxLength="1"
            android:inputType="numberPassword"
            android:gravity="center"
            android:background="@drawable/edittext_border"
            android:layout_marginEnd="3dp"/>

        <EditText
            android:id="@+id/et3"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:maxLength="1"
            android:inputType="numberPassword"
            android:gravity="center"
            android:background="@drawable/edittext_border"
            android:layout_marginEnd="3dp"/>

        <EditText
            android:id="@+id/et4"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:maxLength="1"
            android:inputType="numberPassword"
            android:gravity="center"
            android:background="@drawable/edittext_border"
            android:layout_marginEnd="3dp"/>

        <EditText
            android:id="@+id/et5"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:maxLength="1"
            android:inputType="numberPassword"
            android:gravity="center"
            android:background="@drawable/edittext_border"
            android:layout_marginEnd="3dp"/>

        <EditText
            android:id="@+id/et6"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:maxLength="1"
            android:inputType="numberPassword"
            android:gravity="center"
            android:background="@drawable/edittext_border"/>
    </LinearLayout>

    <Button
        android:id="@+id/btnToggleVisibility"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="显示密码"
        android:layout_marginTop="16dp"/>
</LinearLayout>

3. 输入框边框样式 (edittext_border.xml)

res/drawable 目录下创建 edittext_border.xml,用于设置输入框的边框样式:

复制代码
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#FFFFFF"/>
    <stroke android:width="1dp" android:color="#000000"/>
    <corners android:radius="4dp"/>
</shape>

4. 抖动动画 (res/anim/shake.xml)

res/anim 目录下创建 shake.xml,用于实现输入框抖动动画:

复制代码
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="50"
    android:fromXDelta="0"
    android:toXDelta="10"
    android:repeatCount="5"
    android:repeatMode="reverse"/>

5. 逻辑处理 (MyNumberEditActivity.java)

以下是完整的逻辑处理代码:

复制代码
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.os.Vibrator;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;

public class MyNumberEditActivity extends AppCompatActivity {

    private final String CORRECT_PASSWORD = "123456"; // 正确的密码

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my_number_edit);

        findViewById(R.id.btnShowPasswordDialog).setOnClickListener(v -> showPasswordDialog());
    }

    private void showPasswordDialog() {
        // 创建Dialog
        final Dialog dialog = new Dialog(this, android.R.style.Theme_NoTitleBar_Fullscreen); // 全屏样式
        dialog.setContentView(R.layout.dialog_password);
        dialog.setCancelable(true);

        // 自动弹出软键盘
        dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);

        // 获取Dialog中的控件
        EditText[] etPassword = new EditText[6];
        etPassword[0] = dialog.findViewById(R.id.et1);
        etPassword[1] = dialog.findViewById(R.id.et2);
        etPassword[2] = dialog.findViewById(R.id.et3);
        etPassword[3] = dialog.findViewById(R.id.et4);
        etPassword[4] = dialog.findViewById(R.id.et5);
        etPassword[5] = dialog.findViewById(R.id.et6);

        Button btnToggleVisibility = dialog.findViewById(R.id.btnToggleVisibility);

        // 清空输入框
        for (EditText et : etPassword) {
            et.setText("");
        }

        // 设置输入监听
        setupTextWatchers(etPassword, dialog);

        // 切换密码可见性
        btnToggleVisibility.setOnClickListener(v -> togglePasswordVisibility(etPassword, btnToggleVisibility));

        // 显示Dialog
        dialog.show();
    }

    private void setupTextWatchers(EditText[] etPassword, Dialog dialog) {
        for (int i = 0; i < etPassword.length; i++) {
            final int currentIndex = i;
            etPassword[i].addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    if (s.length() == 1 && currentIndex < etPassword.length - 1) {
                        etPassword[currentIndex + 1].requestFocus(); // 自动跳到下一个输入框
                    }

                    // 当第6位输入完成时,自动验证密码
                    if (currentIndex == etPassword.length - 1 && s.length() == 1) {
                        String inputPassword = getPassword(etPassword);
                        if (inputPassword.equals(CORRECT_PASSWORD)) {
                            Toast.makeText(MyNumberEditActivity.this, "密码正确,进入下一步", Toast.LENGTH_SHORT).show();
                            dialog.dismiss();
                            // 这里可以跳转到下一步
                        } else {
                            Toast.makeText(MyNumberEditActivity.this, "密码错误,请重试", Toast.LENGTH_SHORT).show();
                            vibrate(); // 震动反馈
                            shakeInputs(etPassword); // 输入框抖动动画
                            clearInput(etPassword); // 清空输入框
                            etPassword[0].requestFocus(); // 焦点回到第一个输入框
                        }
                    }
                }

                @Override
                public void afterTextChanged(Editable s) {}
            });

            // 处理删除键
            etPassword[i].setOnKeyListener((v, keyCode, event) -> {
                if (keyCode == KeyEvent.KEYCODE_DEL && event.getAction() == KeyEvent.ACTION_DOWN) {
                    if (etPassword[currentIndex].getText().length() == 0 && currentIndex > 0) {
                        etPassword[currentIndex - 1].requestFocus(); // 回退到上一个输入框
                        etPassword[currentIndex - 1].setText(""); // 清空上一个输入框的内容
                        return true; // 拦截删除键事件
                    }
                }
                return false;
            });
        }
    }

    private String getPassword(EditText[] etPassword) {
        StringBuilder password = new StringBuilder();
        for (EditText et : etPassword) {
            password.append(et.getText().toString());
        }
        return password.toString();
    }

    private void clearInput(EditText[] etPassword) {
        for (EditText et : etPassword) {
            et.setText("");
        }
    }

    private void vibrate() {
        Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
        if (vibrator != null) {
            vibrator.vibrate(200); // 震动200毫秒
        }
    }

    private void shakeInputs(EditText[] etPassword) {
        Animation shake = AnimationUtils.loadAnimation(this, R.anim.shake);
        for (EditText et : etPassword) {
            et.startAnimation(shake);
        }
    }

    private void togglePasswordVisibility(EditText[] etPassword, Button btnToggleVisibility) {
        boolean isVisible = etPassword[0].getInputType() == (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
        for (EditText et : etPassword) {
            if (isVisible) {
                et.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
            } else {
                et.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
            }
        }
        btnToggleVisibility.setText(isVisible ? "显示密码" : "隐藏密码");
    }
}

运行效果

点击主界面的"输入密码"按钮,弹出全屏密码输入框。

依次输入6位数字,输入内容显示为圆点。

当输入第6位时,自动验证密码:

如果密码正确,提示"密码正确,进入下一步",并关闭弹框。

如果密码错误,提示"密码错误,请重试",清空输入框并震动反馈,同时输入框抖动。

支持删除键回退到上一个输入框,并清空上一个输入框的内容。

点击"显示密码"按钮,可以切换密码的可见性。

相关推荐
qqty12171 分钟前
Nginx反向代理出现502 Bad Gateway问题的解决方案
运维·nginx·gateway
Android技术之家3 分钟前
Android Studio 专属AI智能体+Skills完整版提示词(可直接复制使用)
android·ide·人工智能·android studio
xiegwei14 分钟前
Android 原生项目添加 Flutter Activity 示例及常见报错解决方案
android·flutter
于慨22 分钟前
Flutter Android gradle 8.14 file lock, incompatibility issue
android·flutter·issue
千百元25 分钟前
HBuildX 打包成apk完整过程
android
NGINX开源社区3 小时前
使用 NGINX 作为 AI Proxy
大数据·人工智能·nginx
流星白龙9 小时前
【MySQL】7.MySQL基本查询(2)
android·mysql·adb
mldlds10 小时前
MySQL加减间隔时间函数DATE_ADD和DATE_SUB的详解
android·数据库·mysql
智算菩萨12 小时前
MP3音频编码原理深度解析与Python全参数调优实战:从心理声学模型到LAME编码器精细控制
android·python·音视频
studyForMokey13 小时前
【Android面试】Activity生命周期专题
android·面试·职场和发展