简介:这是一份面向Android开发初学者的入门级实战资源,基于Android Studio与Java语言,实现一个不含数据库的简单个人信息注册小应用。功能覆盖用户名与密码输入、单选性别、下拉选择地址、复选爱好,点击提交后跳转结果页展示全部填写内容,并对空输入给出提示,结果页还可返回首页,适合刚接触移动应用开发、想通过完整小实例熟悉界面控件与页面跳转的读者。资源包共495个文件,约10.27MB,以xml布局、json配置、flat与webp资源、dex与class字节码、java源码及gradle构建脚本为主,另含apk成品与properties、jar等依赖文件,结构贴近真实Android工程。目前已有846人学习下载,可帮助读者快速理解Activity跳转、控件取值与简单校验的实现思路,并对照源码自行修改扩展。
1. 个人信息注册页:为什么“无数据库版”反而是最好的起点
很多人第一次打开 Android Studio,想做的第一个能拿得出手的东西,往往就是一个注册页。输入手机号、密码、确认密码,点一下按钮,弹个提示——听起来简单,但真动手时,卡人的地方从来不是界面,而是“数据存哪”。一提到注册,脑子里立刻蹦出数据库、后端接口、网络请求,结果环境还没配好,人已经劝退了。
这篇要讲的,就是把数据库这一层彻底拿掉:所有校验在本地完成,数据用内存或轻量持久化兜住,跑起来就能看到完整交互。它解决的不是“生产级注册系统”,而是让你在 Android Studio 里把 Activity、布局、输入校验、状态保存这条链路完整走一遍。适合刚装完 Android Studio、想找一个能跑通的最小闭环的新手,也适合想快速验证 UI 交互、不想被后端拖住的老手。无数据库不等于没逻辑,恰恰相反,校验规则、错误提示、状态回显这些“真活”一个都不少。
2. 从零搭出注册页:布局、控件与输入约束
2.1 先想清楚页面结构,再动手拖控件
注册页的布局没有玄学,核心就三件事:输入区、提示区、操作区。输入区放手机号、密码、确认密码;提示区用来显示每条校验的错误信息;操作区就是那个注册按钮。我一般用LinearLayout垂直排列,外层套一个ScrollView,防止小屏手机弹出软键盘后按钮被顶出屏幕——这个坑后面避坑章节还会细说。
控件选型上,手机号用EditText配inputType="phone",密码用inputType="textPassword"。这里有个新手常忽略的点:inputType不只是控制键盘类型,它还影响系统是否对输入做自动填充、是否显示明文切换按钮。密码框如果写成text,输入内容会赤裸裸显示出来,既不安全也不专业。
<!-- activity_register.xml 核心结构 --> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="24dp"> <!-- 手机号输入 --> <EditText android:id="@+id/etPhone" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="请输入手机号" android:inputType="phone" android:maxLength="11" /> <!-- 密码输入 --> <EditText android:id="@+id/etPassword" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="请输入密码" android:inputType="textPassword" /> <!-- 确认密码 --> <EditText android:id="@+id/etConfirm" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="请再次输入密码" android:inputType="textPassword" /> <!-- 错误提示统一放这里 --> <TextView android:id="@+id/tvError" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#D32F2F" android:visibility="gone" /> <Button android:id="@+id/btnRegister" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="注册" /> </LinearLayout> </ScrollView>这段布局里,maxLength="11"是手机号的第一道硬约束,超过 11 位根本敲不进去。tvError默认gone,只有校验失败才显示,避免空白占位。ScrollView包住整个LinearLayout,是应对软键盘遮挡最省事的做法。
2.2 输入校验的规则设计:别等点了按钮才报错
校验分两层:一层是输入时的实时约束,一层是点击注册时的集中校验。实时约束靠inputType和maxLength已经挡掉一部分,但手机号格式、密码长度、两次密码是否一致,这些必须在代码里判断。
我一般把校验逻辑抽成一个独立方法,返回错误信息字符串,为空表示通过。这样按钮点击和后续可能的实时校验都能复用同一套规则,不会出现“按钮说不行、实时提示说行”的精神分裂。
// RegisterActivity.kt 中的校验方法 private fun validateInput(phone: String, pwd: String, confirm: String): String? { // 手机号:11位且以1开头 if (phone.length != 11 || !phone.startsWith("1")) { return "手机号格式不正确" } // 密码:至少6位 if (pwd.length < 6) { return "密码不能少于6位" } // 两次密码一致 if (pwd != confirm) { return "两次输入的密码不一致" } return null // 全部通过 }参数说明:phone、pwd、confirm分别来自三个EditText的text.toString().trim()。注意trim()一定要加,用户从别处粘贴时很容易带首尾空格,不加的话“密码不一致”会莫名其妙地触发。返回null代表校验通过,这种“空即成功”的约定在 Kotlin 里配合?.let或if (error != null)都很顺手。
2.3 按钮点击与提示回显:把结果说清楚
点击注册按钮后,先取输入值,再调校验,最后根据结果决定是弹 Toast 还是把错误写到tvError。这里有个体验细节:错误信息写在页面上比 Toast 更友好,因为 Toast 几秒就消失,用户还没看清就没了。我一般两者结合——页面提示具体错误,Toast 给一个轻量反馈。
btnRegister.setOnClickListener { val phone = etPhone.text.toString().trim() val pwd = etPassword.text.toString().trim() val confirm = etConfirm.text.toString().trim() val error = validateInput(phone, pwd, confirm) if (error != null) { // 页面内提示,常驻可见 tvError.text = error tvError.visibility = View.VISIBLE } else { tvError.visibility = View.GONE // 无数据库版:这里只做本地回显,不落库 Toast.makeText(this, "注册成功:$phone", Toast.LENGTH_SHORT).show() } }逻辑说明:校验失败时显示tvError并写入具体原因;成功时隐藏错误提示,用 Toast 回显注册的手机号。无数据库版的关键就在成功分支——没有INSERT、没有网络请求,数据只活在当前页面。这不是偷懒,而是把注意力集中在交互链路上。等你把这条链路跑顺了,再往后接数据库或接口,只是替换成功分支里那几行而已。
3. 无数据库怎么“存”:内存、Bundle 与轻量持久化
3.1 内存单例:最直接的数据落脚点
无数据库不等于数据完全不留痕。最常见的做法是用一个单例对象在内存里存已注册的用户,页面销毁前都能读到。这种方式适合演示和单机小工具,进程被杀数据就没了,但胜在零依赖、零配置。
// UserStore.kt —— 内存单例 object UserStore { // key 用手机号,value 用密码(演示用,真实场景别这么干) private val users = mutableMapOf<String, String>() fun register(phone: String, pwd: String): Boolean { if (users.containsKey(phone)) return false // 已存在 users[phone] = pwd return true } fun exists(phone: String): Boolean = users.containsKey(phone) }参数说明:users用mutableMapOf存手机号到密码的映射。register返回Boolean,方便调用方判断是“注册成功”还是“手机号已存在”。这里刻意用手机号做 key,是因为注册场景里手机号天然唯一,比自增 ID 更贴近真实业务。注意这只是演示,真实项目里密码绝不能明文存内存,这里只是为了让你看清数据流向。
3.2 用 Bundle 扛住屏幕旋转:别让用户白填
Android 有个经典翻车场景:用户填完一屏信息,手机一转屏,Activity 重建,所有输入清空。无数据库版尤其容易中招,因为数据本来就没落盘。解决办法是用onSaveInstanceState把关键输入塞进 Bundle,重建时再取出来。
// 保存状态 override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putString("phone", etPhone.text.toString()) outState.putString("pwd", etPassword.text.toString()) outState.putString("confirm", etConfirm.text.toString()) } // 恢复状态,放在 onCreate 里 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_register) // ... 控件绑定 ... savedInstanceState?.let { etPhone.setText(it.getString("phone")) etPassword.setText(it.getString("pwd")) etConfirm.setText(it.getString("confirm")) } }逻辑说明:onSaveInstanceState在 Activity 可能被销毁前调用,把三个输入框的内容写进 Bundle。重建时savedInstanceState不为空,就回填。参数 key 用字符串,注意保存和读取的 key 必须完全一致,大小写都不能错,否则取出来是null,界面看着像没保存一样。这个机制对无数据库版几乎是刚需,因为一旦重建,内存单例虽然还在,但输入框内容不会自动恢复。
3.3 SharedPreferences:想跨启动保留时的轻量选择
如果希望关掉 App 再打开还能看到上次注册的手机号,可以用SharedPreferences。它本质是一个 XML 文件,适合存少量键值对,不适合存大量结构化数据。无数据库版里用它存“最近一次注册的手机号”刚刚好。
// 写入 val sp = getSharedPreferences("user_prefs", Context.MODE_PRIVATE) sp.edit().putString("last_phone", phone).apply() // 读取 val lastPhone = sp.getString("last_phone", "") etPhone.setText(lastPhone)参数说明:"user_prefs"是文件名,MODE_PRIVATE表示仅本应用可读。apply()是异步写入,不阻塞主线程;如果要求写入后立刻读,用commit()。读取时第二个参数是默认值,这里给空字符串,避免null导致setText崩溃。注意SharedPreferences不适合存密码,演示里存手机号就够了,别把敏感信息往里塞。
4. 避坑与排查:注册页最容易翻车的 5 个点
4.1 软键盘弹出后按钮被顶飞
现象:点密码框,软键盘升起,注册按钮被挤到屏幕外,用户找不到。原因:布局没有滚动容器,或者windowSoftInputMode设置不当。解决:外层套ScrollView,并在AndroidManifest.xml对应 Activity 上加android:windowSoftInputMode="adjustResize",让系统压缩可用高度而不是整体上推。
4.2 转屏后输入全没了
现象:填完信息一转屏,三个框全空。原因:Activity 重建,输入框状态未保存。解决:按 3.2 节用onSaveInstanceState保存和恢复。注意如果用了android:configChanges强行阻止重建,虽然能保住数据,但会错过资源重载,不推荐作为常规手段。
4.3 密码框显示明文
现象:密码输入时直接可见。原因:inputType写成了text而不是textPassword。解决:改回textPassword。如果需要在明文和密文间切换,用setTransformationMethod配合眼睛图标,别直接改inputType,否则键盘类型也会跟着变。
4.4 手机号带空格导致校验失败
现象:用户粘贴手机号,校验提示格式不正确,但肉眼看着就是 11 位。原因:粘贴内容首尾或中间带了空格。解决:取值时统一trim(),如果要求更严,用replace(" ", "")去掉所有空格再校验。这个坑血泪经验最多,因为用户根本意识不到自己带了空格。
4.5 Toast 在 Android 11 以上被限制
现象:自定义 Toast 在部分机型上不显示或显示异常。原因:从 Android 11 开始,系统对自定义 Toast 做了限制,普通文本 Toast 不受影响。解决:注册页的反馈用系统默认 Toast 即可,别为了好看去自定义布局。如果确实需要复杂提示,用Snackbar替代。
5. 进阶技巧:把注册页做成可复用的输入校验组件
写到这儿,注册页已经能跑通了。但如果你每个页面都复制一遍validateInput,迟早会后悔。我一般会把校验规则抽成一个独立的Validator类,用链式调用组合规则,这样登录页、改密页都能直接复用。
// Validator.kt —— 可复用的校验器 class Validator { private val rules = mutableListOf<() -> String?>() fun addRule(rule: () -> String?): Validator { rules.add(rule) return this } // 返回第一条不满足的规则提示,全通过返回 null fun validate(): String? { for (rule in rules) { val error = rule() if (error != null) return error } return null } } // 使用示例 val error = Validator() .addRule { if (phone.length != 11) "手机号格式不正确" else null } .addRule { if (pwd.length < 6) "密码不能少于6位" else null } .addRule { if (pwd != confirm) "两次密码不一致" else null } .validate()逻辑说明:addRule接收一个返回String?的 lambda,返回null表示该规则通过。validate按添加顺序逐条执行,遇到第一条不通过就返回其提示,短路后续规则。这种写法的好处是规则顺序即优先级,手机号格式的提示会先于密码长度出现,符合用户从上到下的填写习惯。
参数说明:每个 lambda 里直接捕获外层的phone、pwd、confirm变量,所以规则定义要放在取值之后。如果规则需要跨页面复用,可以把 lambda 改成接收参数的函数类型,但那样调用会啰嗦一些,按需取舍。
验证这套组件是否可靠,有个笨但有效的办法:把边界值列成表格,逐条手动过一遍。
| 测试项 | 输入 | 预期提示 |
|---|---|---|
| 手机号 10 位 | 1380000000 | 手机号格式不正确 |
| 手机号不以 1 开头 | 23800000000 | 手机号格式不正确 |
| 密码 5 位 | 12345 | 密码不能少于6位 |
| 两次密码不一致 | 123456 / 123457 | 两次输入的密码不一致 |
| 全部合法 | 13800000000 / 123456 / 123456 | 注册成功 |
这张表我一般会贴在代码旁边,改规则时先过表再提交。无数据库版没有后端兜底,所有边界都得自己扛,这张表就是你的后悔药。
最后说个习惯:每次改完校验规则,我一定会在真机或模拟器上把转屏、切后台、粘贴带空格手机号这三个动作各做一遍。这三个动作覆盖了无数据库注册页 80% 的翻车场景,花不了两分钟,但能省下大量“明明代码没问题”的排查时间。希望帮到你。
本文还有配套的精品资源,点击获取