我正在参与创作者训练营第6期, 点击了解活动概况
Android开发仿Web端登录界面(Kotlin)
前语
各位大佬好,给咱们分享一下用Android原生完结Web端的登录界面作用,有哪些能够优化希望大佬们能够指正,那咱们开端吧
最终作用图
前期准备
咱们需求先把需求的资源给download下来,我用Chrome
来进行这一步
- 敞开Chrmoe的调试形式: 按F12敞开或许在设置->更多东西->开发东西
- 开是网络抓包:网络->图片
这样咱们就看到了所需求的图片资料了,咱们另存一下放入咱们的项目
代码
装备Gradle
- 咱们来装备
ViewBinding
。在build.gradle
中的android
增加如下代码:
viewBinding {
enabled = true
}
- 咱们需求增加一些依靠
//glide库
implementation 'com.github.bumptech.glide:glide:4.13.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.13.0'
LoginDialog
咱们创立一个LoginDialog.kt
文件,而且承继与DialogFragment
用于展现登录的UI,详细操作如下
- dailog_login.xml
在layout
目录下创立dailog_login.xml
文件,用于显现登录的布局,详细代码如下:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
android:layout_width="match_parent"
//设置这个布局的高度是自适应的
android:layout_height="wrap_content">
//CardView来优化布局(能够快速设置圆角、暗影等操作)
<androidx.cardview.widget.CardView
android:layout_width="0dp"
android:layout_height="wrap_content"
//这儿设置88dp是因为最上面的图片高度是96dp,咱们这是88dp就能够完结完结堆叠作用
android:layout_marginTop="88dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/dialog_top_img">
//为了方便布局在CardView里边增加一个束缚布局
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<ImageView
android:id="@+id/imageView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="@+id/textView2"
app:layout_constraintEnd_toEndOf="@+id/edit_user"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_baseline_close_24" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="手机登录"
android:textColor="@color/black"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/edit_user"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:background="@drawable/bg_edit"
android:ems="11"
android:hint="请输入手机号码"
android:inputType="number"
android:maxLength="11"
android:paddingStart="100dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView2" />
<EditText
android:id="@+id/edit_pwd"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:background="@drawable/bg_edit"
android:ems="11"
android:hint="请输入暗码"
android:inputType="number"
android:maxLength="4"
android:paddingStart="10dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/edit_user" />
<TextView
android:id="@+id/tv_code"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:text="获取验证码"
android:textColor="#007fff"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@+id/edit_pwd"
app:layout_constraintEnd_toEndOf="@+id/edit_pwd"
app:layout_constraintTop_toTopOf="@+id/edit_pwd" />
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="80dp"
android:layout_height="0dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="@+id/edit_user"
app:layout_constraintStart_toStartOf="@+id/edit_user"
app:layout_constraintTop_toTopOf="@+id/edit_user">
<TextView
android:id="@+id/textView5"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="+86"
android:textColor="#000000" />
<ImageView
android:id="@+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:scaleType="center"
app:srcCompat="@drawable/ic_down" />
</LinearLayout>
<TextView
android:id="@+id/btn_login"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginTop="16dp"
android:background="@drawable/bg_btn"
android:gravity="center"
android:text="登录"
android:textColor="@color/white"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="@+id/edit_pwd"
app:layout_constraintStart_toStartOf="@+id/edit_pwd"
app:layout_constraintTop_toBottomOf="@+id/edit_pwd" />
<TextView
android:id="@+id/textView6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="其他登录方式"
android:textColor="#007fff"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="@+id/btn_login"
app:layout_constraintTop_toBottomOf="@+id/btn_login" />
<TextView
android:id="@+id/textView7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="登录即表示同意"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="@+id/btn_login"
app:layout_constraintTop_toBottomOf="@+id/textView6" />
<TextView
android:id="@+id/textView8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:text="用户协议"
android:textColor="#007fff"
android:textSize="16sp"
app:layout_constraintStart_toEndOf="@+id/textView7"
app:layout_constraintTop_toTopOf="@+id/textView7" />
<TextView
android:id="@+id/textView10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:text="、"
android:textSize="16sp"
app:layout_constraintStart_toEndOf="@+id/textView8"
app:layout_constraintTop_toTopOf="@+id/textView8" />
<TextView
android:id="@+id/textView9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="隐私方针"
android:textColor="#007fff"
android:textSize="16sp"
app:layout_constraintStart_toEndOf="@+id/textView10"
app:layout_constraintTop_toTopOf="@+id/textView7" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
<ImageView
android:id="@+id/dialog_top_img"
android:layout_width="142dp"
android:layout_height="96dp"
android:elevation="2dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_login_2" />
</androidx.constraintlayout.widget.ConstraintLayout>
- bg_edit.xml
在drawable
目录下创立bg_edit.xml
的资源文件,设置EditText
的款式,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
//不对焦的款式
<item android:state_window_focused="false"
android:drawable="@drawable/bg_edit_nofocused"/>
//对焦的款式
<item android:state_focused="true"
android:drawable="@drawable/bg_edit_focused" />
</selector>
- bg_edit_nofocused
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="2px"/>
<solid android:color="@color/white"/>
<stroke android:color="#e4e6eb" android:width="1dp"/>
</shape>
- bg_edit_focused
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/white"/>
<stroke android:color="#007fff" android:width="1dp"/>
</shape>
- bg_btn.xml
在drawable
目录下创立bg_btn.xml
的资源文件,设置TextView
的款式,不必Button
是因为设置background
较为费事,代码如下
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#007fff"/>
<corners android:radius="2px"/>
</shape>
- LoginDialo
LoginDialog.kt
中代码详细如下:
class LoginDialog : DialogFragment() {
//使用viewBinding
lateinit var mBinding: DialogLoginBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
//创立布局
mBinding = DialogLoginBinding.inflate(layoutInflater)
return mBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//初始化Dialog的相关装备
initDialog()
}
/**
* 初始化dialog相关装备
*
*/
private fun initDialog() {
//设置Dialog的显现巨细
setDialogSize()
//设置window的背景为通明色
dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
//设置点击空白和返回键不消失
dialog?.setCanceledOnTouchOutside(false)
//设置dialog的动画
dialog?.window?.setWindowAnimations(R.style.dialog_base_anim)
}
/**
* 设置dialog的巨细
*
*/
private fun setDialogSize(){
val window = dialog?.window
window?.let {
//获取屏幕信息
val wm = requireContext().getSystemService(Context.WINDOW_SERVICE) as? WindowManager
val display = wm?.defaultDisplay
val point = Point();
display?.getSize(point);
val layoutParams = it.attributes;
//设置宽度为屏幕的百分之90
layoutParams.width = (point.x * 0.9).toInt()
//设置高度为自适应
layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT
it.attributes = layoutParams
}
}
}
MainActivity
- 咱们修正一下
MainActivity
,完结展现一个登录Button
,点击后弹出登录界面,详细代码如下:
class MainActivity : AppCompatActivity() {
lateinit var mBinding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(mBinding.root)
mBinding.button.setOnClickListener {
LoginDialog().show(supportFragmentManager, "")
}
}
}
运转一下
这个时候咱们的UI大致就完结了,咱们运转看一下,是不是咱们所有期望的那样
登录逻辑
咱们完结了UI相关的功能,接下来咱们需求开端写,登录相关的逻辑了
- 点击不同输入框显现不同UI
在web端中,当咱们点击输入手机号和请输入暗码时,最上面的UI是显现不同,咱们先把这个一部分功能完结以下:
咱们增加一个initView()
办法,专门初始化View
相关操作,详细代码如下:
private fun initView() {
//设置焦点变化监听
mBinding.editUser.onFocusChangeListener =
View.OnFocusChangeListener { v, hasFocus ->
//该控件获取了焦点
if(hasFocus){
//设置获取焦点后的UI
Glide.with(this).load(R.drawable.ic_login_2).into(mBinding.dialogTopImg)
}
}
mBinding.editPwd.onFocusChangeListener =
View.OnFocusChangeListener { v, hasFocus ->
//该控件获取了焦点
if(hasFocus){
//设置获取焦点后的UI
Glide.with(this).load(R.drawable.ic_login_1).into(mBinding.dialogTopImg)
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//让输入框获取焦点
mBinding.editUser.requestFocus()
}
}
获取验证码
咱们知道点击获取验证码会出现一个验证是否为人为操作,当操作完结后发送验证码,而且会有一个60s距离,而且需求显现出实践秒数,咱们使用Captcha
库来完结验证,使用CountDownTimer
来完结倒计时的作用
增加验证拼图
- 增加倒计时
val timeDown = object : CountDownTimer(60 * 1000, 1000) {
override fun onTick(millisUntilFinished: Long) {
mBinding.tvCode.text = "${millisUntilFinished / 1000}s"
}
override fun onFinish() {
//设置验证码可点击
mBinding.tvCode.isEnabled = true
//康复text
mBinding.tvCode.text = "获取验证码"
}
}
- 增加依靠
implementation 'com.luozm.captcha:captcha:1.1.2'
- 增加布局
<com.luozm.captcha.Captcha
android:id="@+id/capt_cha"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="2dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/cardView"
app:layout_constraintStart_toStartOf="@+id/cardView"
app:layout_constraintTop_toTopOf="@+id/cardView"
//随便找一个图片就行了
app:src="@drawable/ic_captcha" />
- 增加事情监听
mBinding.captCha.setCaptchaListener(object : Captcha.CaptchaListener {
/**
* 验证经过回调
*
* @param time
* @return
*/
override fun onAccess(time: Long): String {
//设置验证码不可点击
mBinding.tvCode.isEnabled = false
//开端倒计时
timeDown.start()
//关闭图片验证
mBinding.captCha.visibility = View.GONE
return "验证经过,耗时" + time + "毫秒";
}
/**
* 验证失利回调
*
* @param failCount
* @return
*/
override fun onFailed(failCount: Int): String {
return "验证失利,已失利" + failCount + "次";
}
override fun onMaxFailed(): String {
Toast.makeText(
this@LoginDialog.requireContext(),
"验证超越次数,你的帐号被封闭",
Toast.LENGTH_SHORT
).show();
return "验证失利,帐号已封闭";
}
})
- 点击发送验证码
mBinding.tvCode.setOnClickListener {
//显现图片验证
mBinding.captCha.visibility = View.VISIBLE
}
登录
- 增加登录判别逻辑
咱们还是在initView
办法中增加代码:
mBinding.btnLogin.setOnClickListener {
//登录按钮不可交互
mBinding.btnLogin.isEnabled =false
//修正UI
mBinding.btnLogin.text = "登录中..."
//开端验证
//判别手机号格局是否正确,这儿只做了长度的按断,其实能够用正则来判别,我这儿知识简略判别
if(mBinding.editUser.text.toString().length < 11){
Toast.makeText(
this@LoginDialog.requireContext(),
"账号格局过错",
Toast.LENGTH_SHORT
).show();
//登录按钮可交互
mBinding.btnLogin.isEnabled =true
//修正UI
mBinding.btnLogin.text = "登录"
return@setOnClickListener
}
if(mBinding.editPwd.text.toString().length < 4){
Toast.makeText(
this@LoginDialog.requireContext(),
"验证码过错",
Toast.LENGTH_SHORT
).show();
//登录按钮可交互
mBinding.btnLogin.isEnabled =true
//修正UI
mBinding.btnLogin.text = "登录"
return@setOnClickListener
}
Toast.makeText(
this@LoginDialog.requireContext(),
"登录成功",
Toast.LENGTH_SHORT
).show();
dismiss()
}
总结
到这儿咱们仿照Web端登录就成功了,如果想看源码在这儿传送门