我正在参与创作者训练营第6期, 点击了解活动概况

Android开发仿Web端登录界面(Kotlin)

前语

各位大佬好,给咱们分享一下用Android原生完结Web端的登录界面作用,有哪些能够优化希望大佬们能够指正,那咱们开端吧

最终作用图

Android开发仿掘金Web端登录界面(Kotlin)

前期准备

咱们需求先把需求的资源给download下来,我用Chrome来进行这一步

  • 敞开Chrmoe的调试形式: 按F12敞开或许在设置->更多东西->开发东西

Android开发仿掘金Web端登录界面(Kotlin)

  • 开是网络抓包:网络->图片

Android开发仿掘金Web端登录界面(Kotlin)

这样咱们就看到了所需求的图片资料了,咱们另存一下放入咱们的项目

代码

装备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

  1. 咱们修正一下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大致就完结了,咱们运转看一下,是不是咱们所有期望的那样

Android开发仿掘金Web端登录界面(Kotlin)

登录逻辑

咱们完结了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端登录就成功了,如果想看源码在这儿传送门