(二十五)安卓开发一个完整的登录页面-支持密码登录和手机验证码登录

下面将详细讲解如何在 Android 中开发一个完整的登录页面,支持密码登录和手机验证码登录。以下是实现过程的详细步骤,从布局设计到逻辑实现,再到用户体验优化,逐步展开。


1. 设计登录页面布局

首先,我们需要设计一个用户友好的登录页面布局,包含以下核心元素:

  • 用户名/手机号输入框:用于输入用户名或手机号。
  • 密码输入框:用于密码登录。
  • 验证码输入框:用于手机验证码登录,默认隐藏。
  • 登录按钮:触发登录操作。
  • 切换登录方式按钮:在密码登录和验证码登录之间切换。
  • 发送验证码按钮:发送验证码到用户手机,默认隐藏。

我们可以使用 LinearLayoutConstraintLayout 来布局这些元素。以下是一个简单的 XML 布局示例:

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<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">

    <EditText
        android:id="@+id/et_username"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="用户名/手机号" />

    <EditText
        android:id="@+id/et_password"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="密码"
        android:inputType="textPassword" />

    <EditText
        android:id="@+id/et_verification_code"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="验证码"
        android:visibility="gone" />

    <Button
        android:id="@+id/btn_send_verification_code"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="发送验证码"
        android:visibility="gone" />

    <Button
        android:id="@+id/btn_login"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="登录" />

    <Button
        android:id="@+id/btn_switch_login_type"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="切换到手机验证码登录" />

</LinearLayout>

说明

  • android:visibility="gone" 表示验证码输入框和发送验证码按钮默认隐藏。
  • android:inputType="textPassword" 确保密码输入框显示为密码格式。

2. 处理登录逻辑

接下来,我们在 Activity 中实现登录逻辑。这里以 Kotlin 为例,使用 AppCompatActivity 实现。

初始化视图和监听器
kotlin 复制代码
class LoginActivity : AppCompatActivity() {

    private lateinit var etUsername: EditText
    private lateinit var etPassword: EditText
    private lateinit var etVerificationCode: EditText
    private lateinit var btnSendVerificationCode: Button
    private lateinit var btnLogin: Button
    private lateinit var btnSwitchLoginType: Button

    private var isPasswordLogin = true // 默认使用密码登录

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)

        // 初始化视图
        etUsername = findViewById(R.id.et_username)
        etPassword = findViewById(R.id.et_password)
        etVerificationCode = findViewById(R.id.et_verification_code)
        btnSendVerificationCode = findViewById(R.id.btn_send_verification_code)
        btnLogin = findViewById(R.id.btn_login)
        btnSwitchLoginType = findViewById(R.id.btn_switch_login_type)

        // 设置登录按钮点击事件
        btnLogin.setOnClickListener {
            if (isPasswordLogin) {
                performPasswordLogin()
            } else {
                performVerificationCodeLogin()
            }
        }

        // 设置切换登录方式按钮点击事件
        btnSwitchLoginType.setOnClickListener {
            switchLoginType()
        }

        // 设置发送验证码按钮点击事件
        btnSendVerificationCode.setOnClickListener {
            val phoneNumber = etUsername.text.toString()
            sendVerificationCode(phoneNumber)
        }
    }
}

说明

  • isPasswordLogin 是一个布尔变量,用于跟踪当前登录方式。
  • 点击登录按钮时,根据 isPasswordLogin 的值决定执行密码登录还是验证码登录。
切换登录方式
kotlin 复制代码
private fun switchLoginType() {
    if (isPasswordLogin) {
        // 切换到手机验证码登录
        etPassword.visibility = View.GONE
        etVerificationCode.visibility = View.VISIBLE
        btnSendVerificationCode.visibility = View.VISIBLE
        btnSwitchLoginType.text = "切换到密码登录"
        isPasswordLogin = false
    } else {
        // 切换到密码登录
        etPassword.visibility = View.VISIBLE
        etVerificationCode.visibility = View.GONE
        btnSendVerificationCode.visibility = View.GONE
        btnSwitchLoginType.text = "切换到手机验证码登录"
        isPasswordLogin = true
    }
}

说明

  • 通过改变视图的可见性(View.VISIBLEView.GONE)和按钮文本,实现登录方式的切换。

3. 实现密码登录

密码登录需要向服务器发送用户名和密码,并处理响应。我们使用 Retrofit 库进行网络请求。

定义 API 接口
kotlin 复制代码
interface ApiService {
    @POST("login")
    fun login(@Body request: LoginRequest): Call<LoginResponse>
}

data class LoginRequest(val username: String, val password: String)
data class LoginResponse(val token: String)
实现密码登录逻辑
kotlin 复制代码
private fun performPasswordLogin() {
    val username = etUsername.text.toString()
    val password = etPassword.text.toString()

    val retrofit = Retrofit.Builder()
        .baseUrl("https://your-api-url.com/") // 替换为实际 API 地址
        .addConverterFactory(GsonConverterFactory.create())
        .build()

    val apiService = retrofit.create(ApiService::class.java)
    val request = LoginRequest(username, password)

    apiService.login(request).enqueue(object : Callback<LoginResponse> {
        override fun onResponse(call: Call<LoginResponse>, response: Response<LoginResponse>) {
            if (response.isSuccessful) {
                val token = response.body()?.token
                token?.let {
                    saveToken(it)
                    goToMainActivity()
                }
            } else {
                showError("登录失败,请检查用户名或密码")
            }
        }

        override fun onFailure(call: Call<LoginResponse>, t: Throwable) {
            showError("网络错误,请稍后重试")
        }
    })
}

说明

  • saveTokengoToMainActivity 用于保存 token 并跳转到主界面,具体实现见后文。
  • showError 用于显示错误提示。

4. 实现手机验证码登录

手机验证码登录分为两步:发送验证码和验证验证码。

定义 API 接口
kotlin 复制代码
interface ApiService {
    @POST("send_verification_code")
    fun sendVerificationCode(@Body request: SendCodeRequest): Call<SendCodeResponse>

    @POST("verify_code")
    fun verifyCode(@Body request: VerifyCodeRequest): Call<VerifyCodeResponse>
}

data class SendCodeRequest(val phoneNumber: String)
data class SendCodeResponse(val success: Boolean)

data class VerifyCodeRequest(val phoneNumber: String, val code: String)
data class VerifyCodeResponse(val token: String)
发送验证码
kotlin 复制代码
private fun sendVerificationCode(phoneNumber: String) {
    val retrofit = Retrofit.Builder()
        .baseUrl("https://your-api-url.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .build()

    val apiService = retrofit.create(ApiService::class.java)
    val request = SendCodeRequest(phoneNumber)

    apiService.sendVerificationCode(request).enqueue(object : Callback<SendCodeResponse> {
        override fun onResponse(call: Call<SendCodeResponse>, response: Response<SendCodeResponse>) {
            if (response.isSuccessful && response.body()?.success == true) {
                Toast.makeText(this@LoginActivity, "验证码已发送", Toast.LENGTH_SHORT).show()
                startCountdown() // 开始倒计时
            } else {
                showError("验证码发送失败")
            }
        }

        override fun onFailure(call: Call<SendCodeResponse>, t: Throwable) {
            showError("网络错误,请稍后重试")
        }
    })
}
验证验证码
kotlin 复制代码
private fun performVerificationCodeLogin() {
    val phoneNumber = etUsername.text.toString()
    val verificationCode = etVerificationCode.text.toString()

    val retrofit = Retrofit.Builder()
        .baseUrl("https://your-api-url.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .build()

    val apiService = retrofit.create(ApiService::class.java)
    val request = VerifyCodeRequest(phoneNumber, verificationCode)

    apiService.verifyCode(request).enqueue(object : Callback<VerifyCodeResponse> {
        override fun onResponse(call: Call<VerifyCodeResponse>, response: Response<VerifyCodeResponse>) {
            if (response.isSuccessful) {
                val token = response.body()?.token
                token?.let {
                    saveToken(it)
                    goToMainActivity()
                }
            } else {
                showError("验证码错误")
            }
        }

        override fun onFailure(call: Call<VerifyCodeResponse>, t: Throwable) {
            showError("网络错误,请稍后重试")
        }
    })
}

5. 优化用户体验

为了提升用户体验,可以添加以下功能:

发送验证码倒计时
kotlin 复制代码
private fun startCountdown() {
    btnSendVerificationCode.isEnabled = false
    var remainingTime = 60 // 60秒倒计时
    val handler = Handler(Looper.getMainLooper())
    val runnable = object : Runnable {
        override fun run() {
            if (remainingTime > 0) {
                btnSendVerificationCode.text = "重新发送($remainingTime)"
                remainingTime--
                handler.postDelayed(this, 1000)
            } else {
                btnSendVerificationCode.text = "发送验证码"
                btnSendVerificationCode.isEnabled = true
            }
        }
    }
    handler.post(runnable)
}

说明

  • sendVerificationCode 成功后调用 startCountdown(),防止用户频繁发送验证码。
输入验证

可以在登录前检查输入是否合法,例如:

kotlin 复制代码
private fun isValidPhoneNumber(phone: String): Boolean {
    return phone.matches(Regex("^1[3-9]\\d{9}$")) // 简单验证中国手机号
}

private fun performPasswordLogin() {
    val username = etUsername.text.toString()
    val password = etPassword.text.toString()
    if (username.isEmpty() || password.isEmpty()) {
        showError("用户名或密码不能为空")
        return
    }
    // 继续登录逻辑
}

private fun performVerificationCodeLogin() {
    val phoneNumber = etUsername.text.toString()
    val code = etVerificationCode.text.toString()
    if (!isValidPhoneNumber(phoneNumber)) {
        showError("请输入有效的手机号")
        return
    }
    if (code.isEmpty()) {
        showError("请输入验证码")
        return
    }
    // 继续验证逻辑
}

6. 处理登录成功和失败

保存 Token 并跳转
kotlin 复制代码
private fun saveToken(token: String) {
    val sharedPreferences = getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
    sharedPreferences.edit().putString("token", token).apply()
}

private fun goToMainActivity() {
    val intent = Intent(this, MainActivity::class.java)
    startActivity(intent)
    finish()
}
显示错误提示
kotlin 复制代码
private fun showError(message: String) {
    Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}

7. 总结

通过以上步骤,我们实现了一个完整的登录页面,支持密码登录和手机验证码登录。主要包括:

  • 布局设计:使用 XML 创建用户界面。
  • 逻辑处理:通过 Kotlin 实现切换登录方式和网络请求。
  • 网络请求:使用 Retrofit 与服务器交互。
  • 用户体验:添加倒计时和输入验证。

在实际开发中,还需考虑:

  • 安全性:密码加密传输、验证码防刷。
  • 界面美化:使用 Material Design 组件。
  • 错误处理:更细致的网络错误和服务器响应处理。
相关推荐
阿巴斯甜18 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker19 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952720 小时前
Andorid Google 登录接入文档
android
黄林晴21 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇2 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android