Android登录注册界面源码详解:从XML布局到完整交互实现

发布时间:2026/9/9 6:50:49
Android登录注册界面源码详解:从XML布局到完整交互实现 简介面向初中级Android开发者的登录与注册界面源码聚焦UI设计与交互体验适合需要快速搭建美观认证页面的项目参考。压缩包共52个文件包含15个XML布局/菜单文件、13个PNG图片资源、3个Java源文件、APK成品及构建中间文件等整体约1.27MB结构清晰便于直接导入工程查看或二次定制。界面依托Material Design思路实现涵盖文本输入框、按钮、浮动标签、密码可见性切换及动画过渡等常用组件后端涉及Intent页面跳转、SharedPreferences本地存储等基础逻辑可帮助理解Android界面与数据交互的完整流程。资源标签为“界面”内容预览显示包含AndroidManifest、drawable多密度目录、libs依赖等可借此了解Android登录注册项目的完整工程结构。源码已吸引6482人学习适合作为课程设计或初学练手的参考实例。 直接上干货吧。今天聊的是“Android漂亮好看的登录注册界面程序源码”——这套东西我断断续续做过七八个版本从早期纯XML手写到后来封装成通用组件踩过的坑比写出来的代码都多。如果你正打算给自己的App加一套能看的登陆注册页或者你是个刚学Android想找个完整案例练手的新人这篇博文应该能帮你省掉至少一个星期的试错时间。先说清楚这套源码解决了什么问题它不是一个孤零零的布局文件而是把登录、注册两个完整流程串起来的最小可用工程。包含输入框校验、密码明文切换、记住密码、注册时密码强度提示、按钮防重复点击以及最容易被忽略的软键盘遮挡处理。拿到代码后你可以直接跑起来看效果也可以把样式扒到自己项目里改一改就用。1. 项目概述与目标拆解1.1 这套登录注册界面到底是什么简单说它是一套基于Android原生View体系写的登录注册界面实现UI层面用的是经典的XML布局Drawable资源逻辑层用Java或Kotlin都可以跑通。我这里给的是Kotlin版本因为现在的Android Studio默认模板基本都是Kotlin而且空安全特性确实能帮你少写很多判空代码。界面包含三个主要部分登录页LoginActivity、注册页RegisterActivity以及一个用于启动它们和模拟登录成功跳转的MainActivity。登录页有App Logo位、用户名输入框、密码输入框带眼睛开关、登录按钮、跳转注册的文本按钮注册页有用户名、手机号、密码、确认密码四个输入项加上一个协议勾选框和注册按钮。整套界面用了圆角卡片输入框和渐变背景视觉上就是在很多App里常见的“现代简洁风”。登录成功之后会通过SharedPreferences保存一个登录状态跳转到主页面并显示当前用户名这就构成了一个完整的最小闭环。你拿这个源码去改业务逻辑只需要把模拟登录替换成真实接口请求就行。1.2 为什么需要一套“漂亮”的界面源码说实话很多刚接触Android的人遇到的第一个坎不是逻辑写不对而是界面怎么摆都丑。系统默认的Button是那个方方正正的样子EditText的底线样式也谈不上好看再加上没处理状态栏和键盘一运行就感觉像个2009年的古董应用。漂亮两个字看起来是审美问题但背后其实是几个具体技术点的组合形状可变背景shape drawable、层次化颜色、间距体系、状态反馈按下、禁用、获取焦点。这些点单独拆开都不难难的是把它们放在一起用对地方。这套源码的价值就在于它把这些东西已经组合好、调校好你只需要理解每一处为什么这样写就能举一反三。从面试角度讲这也是一个高频现场题。面试官经常会问“你做过登录吗”“怎么让EditText不弹键盘”“如何实现密码可见切换”与其背八股不如自己手头有一套能讲的源码。1.3 适合谁看刚学完Android基础、想做一个完整小项目的学生需要给外包项目或自己App快速配一套UI的独立开发者想研究输入框校验、状态栏适配、键盘处理的初级工程师如果你已经写过三五年Android那这套源码对你就太基础了但你还是可以看看里面关于适配和键盘处理的坑毕竟这部分即便老手也偶尔会翻车。2. 技术选型与界面设计思路2.1 用View系统还是Jetpack Compose现在新项目很多人直接上Compose这没错。但这套源码我选择用传统的View系统XML原因很实际兼容性更稳第三方库接入成本低而且大多数现存项目还是View体系。如果你在公司维护老项目上来就逼着人家重构到Compose也不太现实。另外View体系的登录注册参考资料多遇到问题更容易搜到答案对初学者比较友好。Compose的好处是声明式UI写起来快、状态管理方便但它在键盘处理、系统UI兼容性方面依然不如View体系成熟。所以我的建议是学习阶段用View把原理搞懂之后想玩Compose再对照着写一遍两边都会了才是真本事。2.2 界面布局拆解从设计稿到XML以登录页为例子先看整体结构androidx.constraintlayout.widget.ConstraintLayout android:layout_widthmatch_parent android:layout_heightmatch_parent android:backgrounddrawable/bg_gradient ImageView android:idid/iv_logo android:layout_width80dp android:layout_height80dp android:layout_marginTop100dp app:layout_constraintTop_toTopOfparent app:layout_constraintStart_toStartOfparent app:layout_constraintEnd_toEndOfparent android:srcmipmap/ic_logo/ TextView android:idid/tv_app_name android:layout_widthwrap_content android:layout_heightwrap_content android:layout_marginTop16dp android:textXX App android:textSize24sp android:textStylebold android:textColorcolor/white app:layout_constraintTop_toBottomOfid/iv_logo app:layout_constraintStart_toStartOfparent app:layout_constraintEnd_toEndOfparent/ !-- 登录表单区 -- LinearLayout android:idid/ll_form android:layout_widthmatch_parent android:layout_heightwrap_content android:layout_marginStart32dp android:layout_marginEnd32dp android:orientationvertical app:layout_constraintTop_toBottomOfid/tv_app_name app:layout_constraintBottom_toTopOfid/btn_login android:gravitycenter_vertical !-- 用户名输入框 -- EditText android:idid/et_username android:layout_widthmatch_parent android:layout_height52dp android:hint请输入用户名 android:backgrounddrawable/bg_input_white android:paddingStart16dp android:paddingEnd16dp android:inputTypetext android:maxLines1 android:textColorcolor/black android:textColorHintcolor/gray android:drawableStartdrawable/ic_user android:drawablePadding8dp/ ... /LinearLayout !-- 登录按钮 -- Button android:idid/btn_login android:layout_widthmatch_parent android:layout_height52dp android:layout_marginStart32dp android:layout_marginEnd32dp android:layout_marginBottom40dp android:text登 录 android:textSize16sp android:backgrounddrawable/bg_btn_login android:enabledtrue app:layout_constraintBottom_toParentparent/ /androidx.constraintlayout.widget.ConstraintLayout整体用ConstraintLayout做根布局好处是适配不同屏幕时控件之间的相对位置关系能保持稳定。中间用LinearLayout包住输入框是为了让两个输入框等宽且纵向排列起来更简便。Logo和App名称区用约束链来居中底部登录按钮固定在父布局底部偏上一点的位置视觉重心就不会歪。如果你拿到一套UI设计稿第一件事不是写代码而是先把设计稿拆成“块”背景层、内容层、操作层。背景层是渐变或图片内容层包括Logo、标题、输入框操作层是按钮和跳转文本。分好块之后再对应到布局里思路会清晰很多也不会改着改着把自己绕晕。2.3 配色与动效的设计逻辑“好看”的核心不是元素多而是颜色搭配舒服、层次分明。这套源码用的背景是从左上到右下的渐变颜色主体是青色系#43B3AE到#58D5C0白色输入框、白色文字、按钮用更深一些的青色整个页面不超过三种主色。这样的好处是背景和前景对比度足够文字可读性强同时不会花里胡哨。动效方面登录按钮按下时通过selector改变背景颜色或透明度输入框获取焦点时描边变色。这些效果不需要引入任何动画库纯粹用shape和selector就能实现。如果你想要更高级的过渡可以在登录和注册页之间加一个overridePendingTransition让页面切换时产生淡入淡出或左右平移的效果两三行代码就能带来体验提升。但这里要提醒一句动效永远是辅助别为了炫技挡住主要操作路径。按钮动画时间超过300ms就会让用户觉得卡输入框切换焦点动画如果幅度太大也会显得廉价。3. 核心模块实现登录界面3.1 输入框、按钮、背景的落地写法输入框的细节决定了界面的精致度。系统默认EditText的背景是一条下划线这套源码改成圆角白色半透明背景!-- drawable/bg_input_white.xml -- shape xmlns:androidhttp://schemas.android.com/apk/res/android android:shaperectangle solid android:color#CCFFFFFF / corners android:radius26dp / stroke android:width1dp android:colorandroid:color/transparent / /shape入框高度我建议不低于48dp低于这个值手指很难点准。字体大小16sp以上maxLines设为1避免多行输入把布局撑爆。密码框要设置inputTypetextPassword同时加一个眼睛图标在右边点击时切换输入类型ivEye.setOnClickListener { if (etPassword.inputType InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD) { etPassword.inputType InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD ivEye.setImageResource(R.drawable.ic_eye_open) } else { etPassword.inputType InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD ivEye.setImageResource(R.drawable.ic_eye_close) } etPassword.setSelection(etPassword.text.length) }这里有一个坑切换inputType之后光标位置会被重置必须重新setSelection到文本末尾不然用户输满一长串密码之后一切换光标跑前面去了删改非常别扭。这个细节很多教程都不提但你实际用一次就知道多难受了。登录按钮的背景推荐用selector按下去有反馈!-- drawable/bg_btn_login.xml -- selector xmlns:androidhttp://schemas.android.com/apk/res/android item android:state_enabledfalse shape solid android:color#80C4C4C4 / corners android:radius26dp / /shape /item item android:state_pressedtrue shape solid android:colorcolor/colorPrimaryDark / corners android:radius26dp / /shape /item item shape solid android:colorcolor/colorPrimary / corners android:radius26dp / /shape /item /selector注意状态顺序disabled要在最上面pressed其次默认状态放最后。selector匹配是按顺序走的如果把默认状态放最前面后面两个永远不会生效。3.2 输入校验与正则登录页最少要校验两项用户名不为空、密码不为空密码长度至少6位。真正写的时候我习惯写一个统一的表单校验函数private fun validateInput(): Boolean { val username etUsername.text.toString().trim() val password etPassword.text.toString().trim() when { username.isEmpty() - { etUsername.error 请输入用户名 etUsername.requestFocus() return false } password.length 6 - { etPassword.error 密码不能少于6位 etPassword.requestFocus() return false } } return true }这里要强调两点第一判断用户名时一定先trim()不然用户明明输入了字母却因为前后多敲了个空格校验就不通过这体验极其糟糕。第二错误提示用EditText的error属性而不是Toast这样用户能直接看到是哪个输入框出了问题。当然error弹出来之后再次输入时记得清掉它具体做法是在TextWatcher的onTextChanged里把error置空。明文密码还有个安全检查点即使客户端能校验长度真正的密码策略必须在服务端再验证一次。客户端校验只是提升体验不代表安全。另外不要在客户端本地存储用户密码哪怕是用SharedPreferences也不要存明文下一步的“记住密码”功能我们保存的是token不是密码。3.3 记住密码与登录逻辑“记住密码”这个功能正确做法是保存“用户名登录令牌”而不是直接存密码。但在纯本地Demo环境里没有服务端生成token很多人就直接把密码明文存起来图省事。我这里提供一个折中方案用Android自带EncryptedSharedPreferences对数据进行加密保存虽然也是本地存储但至少不是明文。添加依赖implementation androidx.security:security-crypto:1.1.0-alpha06初始化val masterKey MasterKey.Builder(context) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build() val sharedPreferences EncryptedSharedPreferences.create( context, login_prefs, masterKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM )登录成功后如果用户勾选了“记住密码”就把用户名和加密后的密码写进去下次打开App时读取自动填到输入框。登录成功后的处理逻辑我建议做成接口回调的形式方便以后替换真实网络请求btnLogin.setOnClickListener { if (!validateInput()) returnsetOnClickListener btnLogin.isEnabled false btnLogin.text 登录中... loginPresenter.login(username, password) { success, msg - runOnUiThread { btnLogin.isEnabled true btnLogin.text 登 录 if (success) { startActivity(Intent(this, MainActivity::class.java)) finish() } else { Toast.makeText(this, msg, Toast.LENGTH_SHORT).show() } } } }按钮防重复点击也是老生常谈的问题上面这段代码在点击后立刻setEnabled(false)等结果回来再恢复比你在onClick外面包一层时间戳判断要直观得多也更好维护。4. 核心模块实现注册界面4.1 注册布局要注意的细节注册页比登录页多两个信息手机号和确认密码。布局上仍复用登录页的输入框风格但多了两件事要处理一是手机号输入框要限制长度11位并且inputType设为phone或number这样弹出来的软键盘是数字键盘二是确认密码输入框要和密码框保持同宽且在视觉上明确“它是密码框的二次输入”所以通常会在hint里写清楚。手机号限制长度的最佳实践是用TextWatcher控制长度而不是在XML里设置maxLength。因为maxLength只限制输入长度不会阻止用户粘贴超长内容而且一旦用户中间删除再输入光标行为有时会很诡异。我一般这样写etPhone.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {} override fun afterTextChanged(s: Editable?) { if (s ! null s.length 11) { etPhone.setText(s.subSequence(0, 11)) etPhone.setSelection(11) } } })这里要注意setText会再次触发afterTextChanged所以判断条件要写对。如果只做if (s.length 11)就截断那截完后length11不会再进入判断所以不会有死循环问题。4.2 密码强度检测与二次确认密码强度这玩意儿做得太复杂用户烦做得太简单没意义。我用的是一套常规评分逻辑长度≥8且包含字母和数字才算“强”。注册时如果用户密码太弱直接提示但不阻止提交如果两次密码不一致则必须阻止。private fun checkPasswordStrength(password: String): String { return when { password.length 6 - 密码太短 password.length in 6..7 - 强度一般 password.length 8 password.any { it.isLetter() } password.any { it.isDigit() } - 强度较高 else - 建议包含字母和数字 } } private fun confirmPassword(): Boolean { val pwd etPassword.text.toString() val pwdConfirm etConfirmPassword.text.toString() if (pwd ! pwdConfirm) { etConfirmPassword.error 两次密码输入不一致 return false } return true }真正开发时注册页往往还有一些服务端逻辑比如手机号验证码、邮箱验证等。这套源码里我用的是模拟注册只要校验通过就写入本地SharedPreferences保存用户名和密码副本然后跳回登录页并把用户名带回去。这么做的好处是整个流程可以跑通你拿到之后知道每个环节该做什么再对接真实接口就顺理成章了。4.3 注册数据的本地保存与上传接口预留本地保存用一个简单的UserStore对象里面封装增删改查object UserStore { private const val PREFS_NAME user_store fun saveUser(username: String, password: String) { val prefs MyApp.context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) prefs.edit().putString(username, password).apply() } fun checkUser(username: String, password: String): Boolean { val prefs MyApp.context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) val savedPwd prefs.getString(username, null) ?: return false return savedPwd password } }这里要说明Demo阶段用本地Map模拟数据库是可以的但真正的注册逻辑一定要走服务端接口。你可以在UserStore旁边预留一个接口interface ApiService { POST(register) suspend fun register(Body body: RegisterBody): ResponseRegisterResponse }以后接入真实后台只需要把本地那段checkUser换成网络调用就行其他UI逻辑完全不用动。这也是我坚持把“保存用户”和“校验用户”抽成独立类的原因面向接口编程的好处就在这种时候体现。5. 常见问题与排查技巧实录5.1 软键盘遮挡输入框这是登录注册页面最经典的问题尤其在小屏手机上。解决思路分两步第一步在AndroidManifest.xml中给Activity设置windowSoftInputModeadjustResize让键盘弹起时重新调整布局大小第二步如果adjustResize在某些全屏或沉浸式场景下不生效用android:fitsSystemWindowstrue或手动监听OnApplyWindowInsetsListener。我实测下来最稳妥的是在主题里设置style nameAppTheme.Login parentTheme.AppCompat.Light.NoActionBar item nameandroid:windowSoftInputModeadjustResize/item item nameandroid:windowActionModeOverlaytrue/item /style注意如果你的根布局用的是ConstraintLayout并且设置了背景那么键盘弹出时背景会跟着拉伸导致渐变比例变化。这种问题通常把背景放到主题的android:windowBackground上就能解决别放在布局根节点。5.2 沉浸式状态栏导致的问题现在很多界面喜欢做沉浸式状态栏让状态栏颜色和背景融为一体。登录页如果背景是渐变色状态栏也设置成同一渐变会很漂亮。但沉浸式之后布局会向上顶到状态栏后面这时根布局的背景也会被状态栏遮住所以要在布局里预留出状态栏高度。比较简单的方式是给根布局设置android:fitsSystemWindowstrue系统会自动加上paddingTop。但fitsSystemWindows会同时处理底部导航栏如果你不想底部也加padding可以手动获取状态栏高度再设置paddingTopfun setStatusBarHeight(view: View) { val resourceId resources.getIdentifier(status_bar_height, dimen, android) if (resourceId 0) { val height resources.getDimensionPixelSize(resourceId) view.setPadding(0, height, 0, 0) } }这个方案只对顶部生效不会影响底部手势条。5.3 适配问题与drawable资源管理换一台不同分辨率的手机测试经常会遇到按钮宽度超出屏幕、输入框内容被截断等问题。解决办法有三个一是根布局ConstraintLayout中所有横向约束要明确start和end不要让控件依赖于某个固定宽度二是字体用sp尺寸用dp不要写死px三是背景shape尽量用drawable目录下的通用资源不要单独在layout里写尺寸。另外很多新手会把所有背景图都丢到drawable-mdpi里结果在高分辨率机器上模糊得没法看。正确做法是矢量图放drawable位图放drawable-xxhdpi等对应目录。对于登录背景这种用shape渐变就能搞定的完全不用位图清晰度永远在线。5.4 数据库或网络请求常见坑开发登录注册时如果你接入了真实网络请求最常见的坑是“在主线程进行网络操作”。在Android 8.0以上主线程请求网络会直接抛NetworkOnMainThreadException。解决办法是使用协程或Thread Handler推荐用协程coroutineScope.launch { val response withContext(Dispatchers.IO) { apiService.login(body) } if (response.isSuccessful) { ... } }另一个坑是接口返回的JSON字段和实体类对不上一用Gson解析就崩。解决方法是先把响应字符串用Log打出来再用GsonFormat等工具生成实体千万别手敲。6. 实操心得与扩展建议6.1 从Demo到上线我要改哪些东西很多人在本地Demo里跑通了一套登录注册以为上线只需要把接口地址换一换就行了其实远没那么简单。首先本地存储的用户数据必须迁移到服务端数据库客户端的SharedPreferences只能存token不能存密码其次登录请求必须做加密传输至少用HTTPS不能明文POST。界面上Demo里的“圆角白色半透明输入框”在真机上可能会因为背景透明度导致文字看不清楚上线前最好调成不透明纯白。按钮禁用状态的颜色也要重新调整灰白色和整体青色系不一定搭。另外Demo里跳转主页用的intent直接startActivity上线版本涉及到的是登录态持久化、自动登录、Session过期判断这些都需要完整的状态管理框架。最好的做法是把这个登录注册界面的UI部分抽取成一个自定义View或Fragment与数据层完全解耦。这样不管后端怎么变前端UI不受影响。我在自己项目里就是把这套页面封装成LoginSdk业务方只调用一个startLogin(context, callback)。6.2 扩展方向第三方登录、验证码、指纹这套源码的框架比较经典往上加功能也简单。想加第三方登录在登录按钮下方加一排图标按钮点击后拉起对应的SDK授权页面拿到unionId后走同一个登录回调即可。想加验证码登录把密码输入框替换成一个“手机号验证码”区域点击获取验证码按钮后倒计时60秒逻辑上再加一个Timer。如果设备支持指纹还可以在登录页加一个“指纹快捷登录”按钮。调用系统BiometricPrompt验证成功后直接跳转主页面体验很高级。这些扩展都不需要改动核心布局只在现有LoginActivity里增加分支判断就够了。6.3 最后分享一个小技巧界面做漂亮不能只看静态交互反馈要跟得上。我现在每次写完一个输入框都会真机测一遍“点击、输入、删除、清空、切换焦点”这五步尤其是密码的明文切换光标位置、图标变化、是否还有旧的error提示这些小细节决定了用户愿不愿意用你的App。如果你是自己练手建议拿到源码后先原封不动跑起来然后尝试改三个地方背景渐变换成自己的主色调、Logo换成自己的、按钮文案从“登 录”改成“Login”。做完这三个小改动你会对每个资源文件在什么位置、怎么被引用的有一个特别扎实的概念。之后再尝试往里面加一个“忘记密码”页面这套能力基本就吃透了。这套登录注册界面源码就是为“快速落地”和“学习参考”设计的代码量不大但踩过的坑都写在注释和这篇文章里了。希望你在自己项目里用的时候少走一点我当年绕过的弯路。本文还有配套的精品资源点击获取