【Android 开发-kotlin】手机号验证登录demo

运行效果

  • 输入手机号,自动发送验证码
  • 输入错误验证码自动清空
  • 输入正确验证码自动跳转至其他页面


内容简概

一、搭建界面
二、解决删除bug,添加转换正常格式号码功能
三、添加按钮实现界面跳转
四、设置按钮点击状态
五、解决格式化号码转为正常号码的bug
六、布局验证页面提示信息和验证码视图
七、将输入内容显示到方框中
八、Bmob搭建
九、请求验证码
十、验证验证码

具体内容

一、搭建界面

首先布置几个控件ImageView、TextView、EditTextView,这个不再多说。


然后新建一个resource file设置圆角样式

// shape_roundrect.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <corners android:radius="10dp"/>
    <solid android:color="@color/colorAlphaGray"/>
</shape>

对应控件的使用如下



效果如下:



然后再设置让输入框起始光标右移一点,间距大一点
android:hint="请输入手机号"
android:letterSpacing="0.1"
android:paddingStart="10dp"

接下来设置手机号自动分割和输入位数限制,如139 3333 3333,要实现这个功能,需要设置监听器

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        mPhoneEditText.addTextChangedListener(object: LoginTextWatcher() {
            override fun afterTextChanged(s: Editable?) {
                s.toString().length.also {
                    if (it == 3 || it == 8) {
                        // 123 4567
                        s?.append(' ')
                    }
                }
            }
        })
    }
}
open class LoginTextWatcher: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?) {
    }
}
android:maxLength="13"

二、解决删除bug,添加转换正常格式号码功能

(一)解决删除bug

上面写好后有一个bug,就是如果写错号码,删除到第9个就删不了了,因为当号码长度等于8时,会自动添加空格,所以看起来就是删不动了。所以需要增加一个判断,如果是在输入数字,就自动添加空格,如果是在删除数字,就不添加空格。

这里打印一下日志,用onTextChange监视一下start、before、count的变化,发现删除数字时before=1,count=0,增加数字时before=0,count=1

override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
    Log.v("wxj","s:${s.toString()} start:$start before:$before count:$count")
}

下面增加判断

private var shouldAutoSplit=true
// onCreate中
mPhoneEditText.addTextChangedListener(object: LoginTextWatcher() {
       override fun afterTextChanged(s: Editable?) {
       // 判断是删除还是输入
       if (!shouldAutoSplit)return
            s.toString().length.also {
                  if (it == 3 || it == 8) {
                  // 123 4567
                  s?.append(' ')
                  }
             }
        }

       override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
             shouldAutoSplit = count==1
       }
})

(二)添加转换正常格式号码功能

即将123 3333 3333 转换为 12333333333,方便我们后面的数据传递

    // 将123 3333 3333 变成 1233333333
    private fun getPhoneNumber(editable: Editable):String{
        editable.delete(3,4)
        editable.delete(7,8)
        return editable.toString()
    }

三、添加按钮实现界面跳转

先是界面布局,设置id,样式等,设置enable=false,默认不可点击

然后设置点击事件,添加一个跳转页面VerifyActivity

        // 按钮的点击事件
        mLogin.setOnClickListener{
            Intent().apply {
                // 跳转方向
                setClass(this@MainActivity,VerifyActivity::class.java)
                // 配置跳转携带的数据
                putExtra("phone",getPhoneNumber(mPhoneEditText.text))
                startActivity(this )
            }
        }

用日志测试,确保在VerifyActivity中获取到数据

        // 获取数据
        intent.getStringExtra("phone").also {
            Log.v("wxj",it )
        }

四、设置按钮点击状态

设置只有在输入框有内容时,登录按钮才能被点击

mPhoneEditText.addTextChangedListener(object: LoginTextWatcher() {
        override fun afterTextChanged(s: Editable?) {
        // 设置按钮能否被点击,当输入内容长度为13时能点击
        if (s.toString().length==13){
              mLogin.isEnabled=true
        }

再设置一下按钮的样式,分为能点击状态和不能点击状态。新建一个resource file文件——btn_status_selector.xmlbtn_text_selector.xml,分别对应按钮的背景颜色和文字颜色

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!--一般正常状态在后,特殊状态在前,这里无法点击是特殊状态放前面-->
    <!--btn_status_selector.xml-->
    <item android:drawable="@color/colorAlphaGray" android:state_enabled="false"/>
    <item android:drawable="@color/colorBlue" android:state_enabled="true"/>
</selector>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!--btn_text_selector.xml-->
    <item android:state_enabled="false" android:color="@color/colorBlack" />
    <item android:state_enabled="true" android:color="@color/colorWhite" />
</selector>

并设置按钮控件

android:background="@drawable/btn_status_selector"
android:textColor="@drawable/btn_text_selector"

五、解决格式化号码转为正常号码的bug

当输入正确号码跳转到VerifyActivity页面,再返回主页面时,原本的号码变成正常格式“13344445555”,而我们又规定长度等于13时才能跳转,故此时无法跳转。这是因为原本的空格在跳转前被删掉,返回后没有加回来,长度变成只有11了。

解决方法:不直接操纵editable,而是复制一份,对其拷贝内容做更改(正常化号码),使用SpannableStringBuilder方法。

    // 将123 3333 3333 变成 1233333333
    private fun getPhoneNumber(editable:Editable):String{
        // 创建一个新的对象 用于操作editable对象的内容
        SpannableStringBuilder(editable.toString()).also {
            it.delete(3,4)
            it.delete(7,8)
            return it.toString()
        }
    }

六、布局验证页面提示信息和验证码视图

在activity_verify.xml配置


2个ImageView 、3个TextView
// verify_border.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@color/colorAlphaGray"/>
    <corners android:radius="6dp"/>
</shape>
6个TextView

将editView放到验证码框底下,设置透明度为0.01,后续让editView的数字显示在验证框上。

1个EditView

七、将输入内容显示到方框中

设置一些控件的id


// 获取数据
intent.getStringExtra("phone").also {
     // 显示号码
     mPhone.text=it
}

用一个数组保存验证码

    // 保存所有显示验证码的textView
    private val verifyViews:Array<TextView> by lazy {
        arrayOf(mv1,mv2,mv3,mv4,mv5,mv6)
    }

将输入的数据拆分到对应的TextView

       mOrigin.addTextChangedListener(object :TextWatcher{
            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
            override fun afterTextChanged(s: Editable?) {}
            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                // 将输入的数据拆分到对应的TextView
                for ((i:Int,item:Char)in s?.withIndex()!!)
                    //获取i对应的TextView
                    verifyViews[i].text=item.toString()
                // 如果位数小于6个 后面的文本框不显示任何内容
                for (i:Int in s.length..5){
                    verifyViews[i].text=""
                }
            }
        })

八、Bmob搭建

需求:进入VerifyActivity页面,自动发送短信,输入验证码后自动验证

(一)注册并登录Bmob网站

登陆后进入我的控制台,创建一个应用。过程很简单不多说。

(二)在Android studio完成配置——Bmob文档中心

我们要实现短信验证码,点击到短信服务——Android,根据提示,我们需要先完成服务环境搭建,所以先去数据服务页面。



然后根据提示,在gradle和manifest文件中添加指定代码。SDK无需下载,应用密钥就是我们创建应用的key。



下一步是初始化BmobSDK,我们新建一个class,按照文档提示添加代码。Application ID在我们的控制台→应用key中
class MyApplication: Application() {
    override fun onCreate() {
        super.onCreate()

        //第一:默认初始化
        Bmob.initialize(this, "a626a9aabc63d89bd191eeaaadd285f6");
    }
}

在manifest的application中添加

        android:name=".MyApplication"

然后在project structure进行如下修改

九、请求验证码——短信服务

根据文档提示,我们可以这样使用

    override fun onResume() {
        super.onResume()
        // 使用bmob发送验证码
        BmobSMS.requestSMSCode(mPhone.text.toString(), "", object : QueryListener<Int>() {
            override fun done(p0: Int?, p1: BmobException?) {
                // 判断短信是否发送成功
                if (p1==null){
                    Toast.makeText(this@VerifyActivity,"验证码发送成功",Toast.LENGTH_SHORT).show()
                }else{
                    Toast.makeText(this@VerifyActivity,"验证码发送失败",Toast.LENGTH_SHORT).show()
                }
            }
        })
    }

也可以专门用一个object,这里我新建一个Object——BmobUtil

object BmobUtil {
    const val SUCCESS=0
    const val FAILURE=1
    // 向服务器请求,发送验证码 -》发送成功/失败
    fun requestSMSCode(phone:String,callBack:(Int) ->Unit){
        BmobSMS.requestSMSCode(phone, "",object : QueryListener<Int>() {
            override fun done(p0: Int?, p1: BmobException?) {
                if (p1==null){
                    // 发送验证码成功
                    callBack(SUCCESS)
                }else{
                    // 发送验证码失败
                    Log.v("wxj","错误码:${p1.errorCode} message:${p1.message}")
                    callBack(FAILURE)
                }
            }
        })
    }
}

然后在VerifyActivity中使用

    override fun onResume() {
        super.onResume()
        // 使用bmob发送验证码
        BmobUtil.requestSMSCode(mPhone.text.toString()){
            if (it==BmobUtil.SUCCESS){
                Toast.makeText(this,"发送验证码成功",Toast.LENGTH_SHORT).show()
            }else{
                Toast.makeText(this,"发送验证码失败",Toast.LENGTH_SHORT).show()
            }
        }
    }

十、验证验证码——短信服务

依旧参照文档说明,在BmobUtil添加方法

    // 需要验证用户输入的验证码
    fun verifyCode(phone: String,code:String,callBack: (Int) -> Unit) {
        BmobSMS.verifySmsCode(phone,code,object :UpdateListener(){
            override fun done(p0: BmobException?) {
                if (p0==null){
                    // 验证成功
                    callBack(SUCCESS)
                }else{
                    // 验证失败
                    Log.v("wxj","错误码:${p0.errorCode} message:${p0.message}")
                    callBack(FAILURE)
                }
            }
        })
    }

创建一个新的页面以便验证成功后跳转

// verifyActivity的mOrigin.addTextChangedListener中
// 判断输入的验证码个数是不是6个
if (s.length==6){
      // 发起验证请求
      BmobUtil.verifyCode(mPhone.text.toString(),s.toString()){
            if (it==BmobUtil.SUCCESS){
                  // 跳转到主页
                  startActivity(Intent(this@VerifyActivity,HomeActivity::class.java))
            }else{
                  Toast.makeText(this@VerifyActivity,"验证失败",Toast.LENGTH_SHORT).show()
                   mOrigin.text.clear()
            }
       }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343