任务二 搭建登录页面

一、任务目标

创建登录页面并完成页面布局,使页面显示欢迎文字、用户名和密码输入框、用户协议、登录按钮及注册入口,并能够从初始页面打开登录页。

二、添加 ConstraintLayout 依赖

1. 添加依赖

打开 app/build.gradle.kts,在 dependencies 中添加 ConstraintLayout 依赖:

app/build.gradle.kts
dependencies {
// ... 省略已有依赖。
implementation("androidx.constraintlayout:constraintlayout:2.2.2")
}

点击 Android Studio 顶部提示栏中的 Sync Now,等待 Gradle 同步完成。同步成功后再创建布局文件。

三、创建登录页面

先创建只显示“登录页面”的简单页面,检查页面能否打开,再添加正式控件。

1. 创建 activity_login.xml

在 app/src/main/res/layout 目录中创建 activity_login.xml,写入以下完整内容。

app/src/main/res/layout/activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="登录页面"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

最外层的 <layout> 是 Data Binding 布局的根标签。页面中间的“登录页面”是临时文字,只用于运行验证,完成检查后将被正式控件替换。布局文件准备好后,还需要创建 LoginActivity 来显示它。

2. 创建 LoginActivity

在 com.android.practice.food.feature.account 包中创建 login 包,然后创建 LoginActivity.kt,写入以下完整内容。

app/src/main/java/com/android/practice/food/feature/account/login/LoginActivity.kt
package com.android.practice.food.feature.account.login
import android.os.Bundle
import androidx.databinding.DataBindingUtil
import com.android.practice.food.R
import com.android.practice.food.core.ui.base.BaseActivity
import com.android.practice.food.databinding.ActivityLoginBinding
class LoginActivity : BaseActivity() {
private lateinit var binding: ActivityLoginBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 显示登录布局,并取得访问页面控件的 binding。
binding = DataBindingUtil.setContentView(this, R.layout.activity_login)
// 后续绑定的数据会随当前页面的运行状态更新。
binding.lifecycleOwner = this
}
}

在 onCreate() 中,DataBindingUtil.setContentView(...) 加载上一步创建的 activity_login.xml,让登录页显示其中的临时文字。它还会返回一个绑定对象,保存在 binding 中。后面添加控件时,可以通过 binding 操作它们。

接着用 binding.lifecycleOwner = this 告诉 binding 当前登录页的运行状态,为以后根据数据更新页面内容做准备。现在页面只有临时文字,所以这行代码不会改变显示效果。

3. 注册 LoginActivity

LoginActivity 已经能加载布局,还需要在清单文件中注册,应用才能打开它。打开 app/src/main/AndroidManifest.xml,在 <application> 标签内加入:

app/src/main/AndroidManifest.xml
<manifest ...>
<application ...>
<activity android:name=".feature.main.MainActivity" ...>
...
</activity>
<activity android:name=".feature.account.login.LoginActivity" />
</application>
</manifest>

注册位置与 MainActivity 处于同一级,不能写在 MainActivity 的 <activity> 标签内部。

4. 从 MainActivity 打开登录页

当前项目还没有登录状态判断。为了便于查看页面,在 MainActivity.kt 中暂时直接打开 LoginActivity。

打开 MainActivity.kt,添加 Intent 和 LoginActivity 的导入语句,并在 onCreate() 末尾打开登录页:

app/src/main/java/com/android/practice/food/feature/main/MainActivity.kt
import android.content.Intent
import com.android.practice.food.feature.account.login.LoginActivity
class MainActivity : BaseActivity() {
// ... 省略成员变量。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.lifecycleOwner = this
// 暂时跳过主页面,直接打开登录页检查布局。
startActivity(Intent(this, LoginActivity::class.java))
// 关闭主页面,避免按返回键时再次回到这里。
finish()
}
}

startActivity() 打开登录页,finish() 关闭当前主页面。这是便于查看登录页的临时跳转,任务五将改为仅在未登录时跳转。

5. 运行验证

运行应用。屏幕中间应显示“登录页面”,表示 Activity、布局文件和页面跳转配置已生效。

最小登录页面运行效果

如果应用仍停留在初始页面,检查 MainActivity 中是否已经调用 startActivity();如果打开页面时闪退,检查 AndroidManifest.xml 中是否已经注册 LoginActivity。

四、添加关闭按钮和欢迎文字

返回 activity_login.xml,在 ConstraintLayout 中加入关闭按钮和欢迎文字。

1. 删除临时文字

删除 ConstraintLayout 中显示“登录页面”的临时 TextView,保留外层布局标签。

app/src/main/res/layout/activity_login.xml
<layout ...>
<androidx.constraintlayout.widget.ConstraintLayout ...>
<TextView android:text="登录页面" ... />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

2. 添加关闭按钮

在 activity_login.xml 的 ConstraintLayout 中添加关闭按钮,使用初始工程中的 ic_cancel_black 图标和 bg_circle_black 背景。

app/src/main/res/layout/activity_login.xml
<layout ...>
<androidx.constraintlayout.widget.ConstraintLayout ...>
<ImageView
android:id="@+id/close_btn"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
android:background="@drawable/bg_circle_black"
android:padding="10dp"
android:scaleType="centerInside"
android:src="@drawable/ic_cancel_black"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

任务三将为该按钮添加关闭页面的点击事件。

3. 添加欢迎文字

在关闭按钮后加入:

app/src/main/res/layout/activity_login.xml
<layout ...>
<androidx.constraintlayout.widget.ConstraintLayout ...>
<ImageView android:id="@+id/close_btn" ... />
<TextView
android:id="@+id/welcome"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="35dp"
android:layout_marginTop="50dp"
android:layout_marginEnd="35dp"
android:text="@string/label_login_welcome"
android:textColor="@color/black_80"
android:textSize="30dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/close_btn" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

label_login_welcome 已经定义在初始工程的 strings.xml 中,直接使用即可。

layout_width="0dp" 配合左右约束使用,表示控件宽度由两侧约束共同确定。欢迎文字距离页面左右两侧各 35dp,并放置在关闭按钮下方。

4. 运行验证

重新运行应用。关闭按钮应位于页面左上角,欢迎文字位于按钮下方。

登录页面顶部运行效果

五、添加账号输入区

登录页包含用户名和密码两个输入框。输入框样式 EditText_Single_Line、提示文字 hint_username 和 hint_password 均已在初始工程中提供。

1. 添加用户名输入框

在欢迎文字后加入:

app/src/main/res/layout/activity_login.xml
<layout ...>
<androidx.constraintlayout.widget.ConstraintLayout ...>
<TextView android:id="@+id/welcome" ... />
<EditText
android:id="@+id/et_username"
style="@style/EditText_Single_Line"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:hint="@string/hint_username"
app:layout_constraintEnd_toEndOf="@id/welcome"
app:layout_constraintStart_toStartOf="@id/welcome"
app:layout_constraintTop_toBottomOf="@id/welcome"
tools:text="123892" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

输入框的左右约束与欢迎文字相同,因此二者具有相同的左右边界。tools:text 只在 Android Studio 的布局预览中显示,运行应用时输入框仍为空。

2. 添加密码输入框

在用户名输入框后加入:

app/src/main/res/layout/activity_login.xml
<layout ...>
<androidx.constraintlayout.widget.ConstraintLayout ...>
<EditText android:id="@+id/et_username" ... />
<EditText
android:id="@+id/et_password"
style="@style/EditText_Single_Line"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:hint="@string/hint_password"
android:inputType="textPassword"
app:layout_constraintEnd_toEndOf="@id/et_username"
app:layout_constraintStart_toStartOf="@id/et_username"
app:layout_constraintTop_toBottomOf="@id/et_username"
tools:text="" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

android:inputType="textPassword" 用于指定密码输入方式。运行时,密码内容会以隐藏字符显示。

3. 运行验证

运行应用,分别在两个输入框中输入内容。用户名应直接显示,密码应隐藏显示;两个输入框的宽度和左右位置应保持一致。

账号输入区运行效果

六、完成页面操作区

页面下半部分依次放置用户协议、登录按钮和账号注册入口。所需文字和样式已经包含在初始工程中。

1. 添加用户协议

在 activity_login.xml 中,接在密码输入框之后添加复选框和协议文字:

app/src/main/res/layout/activity_login.xml
<layout ...>
<androidx.constraintlayout.widget.ConstraintLayout ...>
<EditText android:id="@+id/et_password" ... />
<CheckBox
android:id="@+id/cb_agreement"
style="@style/CheckBox_Common"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:gravity="center_vertical"
android:text="@string/login_agreement_prefix"
app:layout_constraintStart_toStartOf="@id/et_password"
app:layout_constraintTop_toBottomOf="@id/et_password" />
<TextView
android:id="@+id/agreement_url"
style="@style/CheckBox_Common"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/login_agreement_postfix"
android:textColor="@color/link_text"
app:layout_constraintBottom_toBottomOf="@id/cb_agreement"
app:layout_constraintStart_toEndOf="@id/cb_agreement"
app:layout_constraintTop_toTopOf="@id/cb_agreement" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

link_text 设置协议文字的颜色。

2. 添加登录按钮

在 activity_login.xml 中,将登录按钮放在协议文字之后,并约束在协议复选框下方:

app/src/main/res/layout/activity_login.xml
<layout ...>
<androidx.constraintlayout.widget.ConstraintLayout ...>
<TextView android:id="@+id/agreement_url" ... />
<Button
android:id="@+id/login_btn"
style="@style/Primary_Button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:text="@string/label_login_btn"
app:layout_constraintEnd_toEndOf="@id/et_username"
app:layout_constraintStart_toStartOf="@id/et_username"
app:layout_constraintTop_toBottomOf="@id/cb_agreement" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Primary_Button 已经设置按钮背景、文字颜色和字号。登录按钮的左右约束与用户名输入框一致。

3. 添加账号注册入口

在登录按钮后加入:

app/src/main/res/layout/activity_login.xml
<layout ...>
<androidx.constraintlayout.widget.ConstraintLayout ...>
<Button android:id="@+id/login_btn" ... />
<TextView
android:id="@+id/register_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:padding="10dp"
android:text="@string/label_go_register"
android:textColor="@color/black_70"
android:textSize="14dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/login_btn" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

注册入口位于登录按钮下方,并在页面中水平居中。本单元先保留入口,注册功能将在拓展任务中完成。

4. 运行验证

运行应用,对照下图检查登录页各控件的位置和显示效果。

登录页面完整运行效果

输入用户名和密码,确认密码隐藏显示;勾选用户协议,确认复选框状态变化。本任务暂不处理按钮和链接点击。

任务小结

本任务创建了 LoginActivity,使用 ConstraintLayout 完成登录页布局,并通过临时跳转查看页面效果。