運(yùn)行效果
- 輸入手機(jī)號(hào),自動(dòng)發(fā)送驗(yàn)證碼
- 輸入錯(cuò)誤驗(yàn)證碼自動(dòng)清空
-
輸入正確驗(yàn)證碼自動(dòng)跳轉(zhuǎn)至其他頁(yè)面
內(nèi)容簡(jiǎn)概
一、搭建界面
二、解決刪除bug,添加轉(zhuǎn)換正常格式號(hào)碼功能
三、添加按鈕實(shí)現(xiàn)界面跳轉(zhuǎn)
四、設(shè)置按鈕點(diǎn)擊狀態(tài)
五、解決格式化號(hào)碼轉(zhuǎn)為正常號(hào)碼的bug
六、布局驗(yàn)證頁(yè)面提示信息和驗(yàn)證碼視圖
七、將輸入內(nèi)容顯示到方框中
八、Bmob搭建
九、請(qǐng)求驗(yàn)證碼
十、驗(yàn)證驗(yàn)證碼
具體內(nèi)容
一、搭建界面
首先布置幾個(gè)控件ImageView、TextView、EditTextView,這個(gè)不再多說(shuō)。
然后新建一個(gè)resource file設(shè)置圓角樣式
// 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>
對(duì)應(yīng)控件的使用如下

效果如下:

然后再設(shè)置讓輸入框起始光標(biāo)右移一點(diǎn),間距大一點(diǎn)
android:hint="請(qǐng)輸入手機(jī)號(hào)"
android:letterSpacing="0.1"
android:paddingStart="10dp"
接下來(lái)設(shè)置手機(jī)號(hào)自動(dòng)分割和輸入位數(shù)限制,如139 3333 3333,要實(shí)現(xiàn)這個(gè)功能,需要設(shè)置監(jiān)聽(tīng)器
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,添加轉(zhuǎn)換正常格式號(hào)碼功能
(一)解決刪除bug
上面寫好后有一個(gè)bug,就是如果寫錯(cuò)號(hào)碼,刪除到第9個(gè)就刪不了了,因?yàn)楫?dāng)號(hào)碼長(zhǎng)度等于8時(shí),會(huì)自動(dòng)添加空格,所以看起來(lái)就是刪不動(dòng)了。所以需要增加一個(gè)判斷,如果是在輸入數(shù)字,就自動(dòng)添加空格,如果是在刪除數(shù)字,就不添加空格。
這里打印一下日志,用onTextChange監(jiān)視一下start、before、count的變化,發(fā)現(xiàn)刪除數(shù)字時(shí)before=1,count=0,增加數(shù)字時(shí)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
}
})
(二)添加轉(zhuǎn)換正常格式號(hào)碼功能
即將123 3333 3333 轉(zhuǎn)換為 12333333333,方便我們后面的數(shù)據(jù)傳遞
// 將123 3333 3333 變成 1233333333
private fun getPhoneNumber(editable: Editable):String{
editable.delete(3,4)
editable.delete(7,8)
return editable.toString()
}
三、添加按鈕實(shí)現(xiàn)界面跳轉(zhuǎn)
先是界面布局,設(shè)置id,樣式等,設(shè)置enable=false,默認(rèn)不可點(diǎn)擊
然后設(shè)置點(diǎn)擊事件,添加一個(gè)跳轉(zhuǎn)頁(yè)面VerifyActivity
// 按鈕的點(diǎn)擊事件
mLogin.setOnClickListener{
Intent().apply {
// 跳轉(zhuǎn)方向
setClass(this@MainActivity,VerifyActivity::class.java)
// 配置跳轉(zhuǎn)攜帶的數(shù)據(jù)
putExtra("phone",getPhoneNumber(mPhoneEditText.text))
startActivity(this )
}
}
用日志測(cè)試,確保在VerifyActivity中獲取到數(shù)據(jù)
// 獲取數(shù)據(jù)
intent.getStringExtra("phone").also {
Log.v("wxj",it )
}
四、設(shè)置按鈕點(diǎn)擊狀態(tài)
設(shè)置只有在輸入框有內(nèi)容時(shí),登錄按鈕才能被點(diǎn)擊
mPhoneEditText.addTextChangedListener(object: LoginTextWatcher() {
override fun afterTextChanged(s: Editable?) {
// 設(shè)置按鈕能否被點(diǎn)擊,當(dāng)輸入內(nèi)容長(zhǎng)度為13時(shí)能點(diǎn)擊
if (s.toString().length==13){
mLogin.isEnabled=true
}
再設(shè)置一下按鈕的樣式,分為能點(diǎn)擊狀態(tài)和不能點(diǎn)擊狀態(tài)。新建一個(gè)resource file文件——btn_status_selector.xml和btn_text_selector.xml,分別對(duì)應(yīng)按鈕的背景顏色和文字顏色
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!--一般正常狀態(tài)在后,特殊狀態(tài)在前,這里無(wú)法點(diǎn)擊是特殊狀態(tài)放前面-->
<!--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>
并設(shè)置按鈕控件
android:background="@drawable/btn_status_selector"
android:textColor="@drawable/btn_text_selector"
五、解決格式化號(hào)碼轉(zhuǎn)為正常號(hào)碼的bug
當(dāng)輸入正確號(hào)碼跳轉(zhuǎn)到VerifyActivity頁(yè)面,再返回主頁(yè)面時(shí),原本的號(hào)碼變成正常格式“13344445555”,而我們又規(guī)定長(zhǎng)度等于13時(shí)才能跳轉(zhuǎn),故此時(shí)無(wú)法跳轉(zhuǎn)。這是因?yàn)樵镜目崭裨谔D(zhuǎn)前被刪掉,返回后沒(méi)有加回來(lái),長(zhǎng)度變成只有11了。
解決方法:不直接操縱editable,而是復(fù)制一份,對(duì)其拷貝內(nèi)容做更改(正常化號(hào)碼),使用SpannableStringBuilder方法。
// 將123 3333 3333 變成 1233333333
private fun getPhoneNumber(editable:Editable):String{
// 創(chuàng)建一個(gè)新的對(duì)象 用于操作editable對(duì)象的內(nèi)容
SpannableStringBuilder(editable.toString()).also {
it.delete(3,4)
it.delete(7,8)
return it.toString()
}
}
六、布局驗(yàn)證頁(yè)面提示信息和驗(yàn)證碼視圖
在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放到驗(yàn)證碼框底下,設(shè)置透明度為0.01,后續(xù)讓editView的數(shù)字顯示在驗(yàn)證框上。
1個(gè)EditView
七、將輸入內(nèi)容顯示到方框中
設(shè)置一些控件的id

// 獲取數(shù)據(jù)
intent.getStringExtra("phone").also {
// 顯示號(hào)碼
mPhone.text=it
}

用一個(gè)數(shù)組保存驗(yàn)證碼
// 保存所有顯示驗(yàn)證碼的textView
private val verifyViews:Array<TextView> by lazy {
arrayOf(mv1,mv2,mv3,mv4,mv5,mv6)
}
將輸入的數(shù)據(jù)拆分到對(duì)應(yīng)的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) {
// 將輸入的數(shù)據(jù)拆分到對(duì)應(yīng)的TextView
for ((i:Int,item:Char)in s?.withIndex()!!)
//獲取i對(duì)應(yīng)的TextView
verifyViews[i].text=item.toString()
// 如果位數(shù)小于6個(gè) 后面的文本框不顯示任何內(nèi)容
for (i:Int in s.length..5){
verifyViews[i].text=""
}
}
})
八、Bmob搭建
需求:進(jìn)入VerifyActivity頁(yè)面,自動(dòng)發(fā)送短信,輸入驗(yàn)證碼后自動(dòng)驗(yàn)證
(一)注冊(cè)并登錄Bmob網(wǎng)站
登陸后進(jìn)入我的控制臺(tái),創(chuàng)建一個(gè)應(yīng)用。過(guò)程很簡(jiǎn)單不多說(shuō)。
(二)在Android studio完成配置——Bmob文檔中心
我們要實(shí)現(xiàn)短信驗(yàn)證碼,點(diǎn)擊到短信服務(wù)——Android,根據(jù)提示,我們需要先完成服務(wù)環(huán)境搭建,所以先去數(shù)據(jù)服務(wù)頁(yè)面。

然后根據(jù)提示,在gradle和manifest文件中添加指定代碼。SDK無(wú)需下載,應(yīng)用密鑰就是我們創(chuàng)建應(yīng)用的key。

下一步是初始化BmobSDK,我們新建一個(gè)class,按照文檔提示添加代碼。Application ID在我們的控制臺(tái)→應(yīng)用key中
class MyApplication: Application() {
override fun onCreate() {
super.onCreate()
//第一:默認(rèn)初始化
Bmob.initialize(this, "a626a9aabc63d89bd191eeaaadd285f6");
}
}
在manifest的application中添加
android:name=".MyApplication"
然后在project structure進(jìn)行如下修改
九、請(qǐng)求驗(yàn)證碼——短信服務(wù)
根據(jù)文檔提示,我們可以這樣使用
override fun onResume() {
super.onResume()
// 使用bmob發(fā)送驗(yàn)證碼
BmobSMS.requestSMSCode(mPhone.text.toString(), "", object : QueryListener<Int>() {
override fun done(p0: Int?, p1: BmobException?) {
// 判斷短信是否發(fā)送成功
if (p1==null){
Toast.makeText(this@VerifyActivity,"驗(yàn)證碼發(fā)送成功",Toast.LENGTH_SHORT).show()
}else{
Toast.makeText(this@VerifyActivity,"驗(yàn)證碼發(fā)送失敗",Toast.LENGTH_SHORT).show()
}
}
})
}
也可以專門用一個(gè)object,這里我新建一個(gè)Object——BmobUtil
object BmobUtil {
const val SUCCESS=0
const val FAILURE=1
// 向服務(wù)器請(qǐng)求,發(fā)送驗(yàn)證碼 -》發(fā)送成功/失敗
fun requestSMSCode(phone:String,callBack:(Int) ->Unit){
BmobSMS.requestSMSCode(phone, "",object : QueryListener<Int>() {
override fun done(p0: Int?, p1: BmobException?) {
if (p1==null){
// 發(fā)送驗(yàn)證碼成功
callBack(SUCCESS)
}else{
// 發(fā)送驗(yàn)證碼失敗
Log.v("wxj","錯(cuò)誤碼:${p1.errorCode} message:${p1.message}")
callBack(FAILURE)
}
}
})
}
}
然后在VerifyActivity中使用
override fun onResume() {
super.onResume()
// 使用bmob發(fā)送驗(yàn)證碼
BmobUtil.requestSMSCode(mPhone.text.toString()){
if (it==BmobUtil.SUCCESS){
Toast.makeText(this,"發(fā)送驗(yàn)證碼成功",Toast.LENGTH_SHORT).show()
}else{
Toast.makeText(this,"發(fā)送驗(yàn)證碼失敗",Toast.LENGTH_SHORT).show()
}
}
}
十、驗(yàn)證驗(yàn)證碼——短信服務(wù)
依舊參照文檔說(shuō)明,在BmobUtil添加方法
// 需要驗(yàn)證用戶輸入的驗(yàn)證碼
fun verifyCode(phone: String,code:String,callBack: (Int) -> Unit) {
BmobSMS.verifySmsCode(phone,code,object :UpdateListener(){
override fun done(p0: BmobException?) {
if (p0==null){
// 驗(yàn)證成功
callBack(SUCCESS)
}else{
// 驗(yàn)證失敗
Log.v("wxj","錯(cuò)誤碼:${p0.errorCode} message:${p0.message}")
callBack(FAILURE)
}
}
})
}
創(chuàng)建一個(gè)新的頁(yè)面以便驗(yàn)證成功后跳轉(zhuǎn)
// verifyActivity的mOrigin.addTextChangedListener中
// 判斷輸入的驗(yàn)證碼個(gè)數(shù)是不是6個(gè)
if (s.length==6){
// 發(fā)起驗(yàn)證請(qǐng)求
BmobUtil.verifyCode(mPhone.text.toString(),s.toString()){
if (it==BmobUtil.SUCCESS){
// 跳轉(zhuǎn)到主頁(yè)
startActivity(Intent(this@VerifyActivity,HomeActivity::class.java))
}else{
Toast.makeText(this@VerifyActivity,"驗(yàn)證失敗",Toast.LENGTH_SHORT).show()
mOrigin.text.clear()
}
}
}







