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位时,自动验证密码:

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

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

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

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

相关推荐
Gracker39 分钟前
Android Weekly #202511
android
灵壹Eli2 小时前
Nginx + Docker 反向代理多个项目
运维·nginx·docker
每次的天空3 小时前
Android第二次面试总结(项目拷打实战)
android
&有梦想的咸鱼&3 小时前
Android Dagger2 框架辅助工具模块深度剖析(六)
android
随风九天5 小时前
使用 Nginx 进行前端灰度发布的策略与实践
运维·前端·nginx·前端灰度发布
二流小码农5 小时前
鸿蒙开发:权限管理之授权方式
android·ios·harmonyos
每次的天空5 小时前
kotlin中的行为组件
android·开发语言·kotlin
wangz765 小时前
Kotlin,jetpack compose,Android,MPAndroidChart,折线图示例
android·kotlin·mpandroidchart
二流小码农5 小时前
鸿蒙开发:申请授权权限
android·ios·harmonyos