运行效果
- 输入手机号,自动发送验证码
- 输入错误验证码自动清空
-
输入正确验证码自动跳转至其他页面
内容简概
一、搭建界面
二、解决删除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.xml
和btn_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配置
// 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>
将editView放到验证码框底下,设置透明度为0.01,后续让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()
}
}
}