本篇文章主要介绍暑假期间实现的手机验证码验证页面
主要知识点:
- 手机号登录页面实现
- 验证码验证页面实现
- 页面跳转
- 验证框输入过程背景色变化
- 第三方短信验证API的使用
效果:
-
手机号登录页面
-
验证码验证页面
手机号登录页面实现:
页面布局activity_main.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:id="@+id/mContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.08" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.92" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.05" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.2" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5" />
<ImageView
android:id="@+id/imageView"
android:layout_width="49dp"
android:layout_height="45dp"
app:layout_constraintBottom_toTopOf="@+id/guideline4"
app:layout_constraintStart_toStartOf="@+id/mEditText"
app:layout_constraintTop_toTopOf="@+id/guideline3"
app:srcCompat="@mipmap/phone" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="@string/phone_number"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="@+id/imageView"
app:layout_constraintStart_toEndOf="@+id/imageView"
app:layout_constraintTop_toTopOf="@+id/imageView" />
<EditText
android:id="@+id/mEditText"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:background="@drawable/shape_rectangle_bg"
android:ems="10"
android:inputType="number"
android:letterSpacing="0.2"
android:paddingStart="6dp"
android:textAlignment="center"
app:layout_constraintBottom_toTopOf="@+id/guideline5"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintStart_toStartOf="@+id/guideline"
app:layout_constraintTop_toTopOf="@+id/guideline4"
tools:ignore="RtlSymmetry"
tools:targetApi="lollipop" />
<Button
android:id="@+id/loginButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/login"
android:background="@drawable/selector_button_bg"
android:enabled="false"
app:layout_constraintBottom_toTopOf="@+id/guideline5"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintStart_toStartOf="@+id/guideline"
app:layout_constraintTop_toBottomOf="@+id/mEditText" />
</androidx.constraintlayout.widget.ConstraintLayout>
注意这里登录按钮的背景色会随着输入手机号位数改变
selector_button_bg.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/colorGray" android:state_enabled="false"/>
<item android:drawable="@color/colorAccent" android:state_enabled="true"/>
</selector>
逻辑处理MainActivity.java
class MainActivity : AppCompatActivity() {
private var stringBuffer = StringBuffer()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
init()
initEvent()
}
private fun init() {
mEditText.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(editable: Editable?) {
if (editable.toString() != "") {
if (editable?.length == 11) {
stringBuffer.append(editable)
loginButton.isEnabled = true
} else {
loginButton.isEnabled = false
}
}
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
})
}
/**
* 点击事件,跳转到验证码验证页面
* */
private fun initEvent(){
loginButton.setOnClickListener {
initIntent()
}
}
/**
* 保存输入的手机号
* */
private fun initIntent() {
val bundle = Bundle()
bundle.putString("phone", stringBuffer.toString())
stringBuffer = StringBuffer()
Intent(this, VerifyActivity::class.java).apply {
putExtras(bundle)
startActivity(this)
}
}
}
注意在init
方法里,实现了对输入框的监听,当输入手机号为11位时,设置登录按钮可以点击同时改变登录按钮背景,否则不可以点击,然后再initIntent
方法里,将手机号保存并开启界面跳转
页面跳转在initEvent
方法里,也就是点击事件中开启,在initIntent
方法里初始化
val bundle = Bundle()
bundle.putString("phone", stringBuffer.toString())
stringBuffer = StringBuffer()
Intent(this, VerifyActivity::class.java).apply {
putExtras(bundle)
startActivity(this)
}
验证码验证页面实现
页面activity_verify.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="match_parent"
tools:context=".VerifyActivity">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.05" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.15" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.08" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.92" />
<ImageView
android:id="@+id/imageView2"
android:layout_width="50dp"
android:layout_height="55dp"
app:layout_constraintBottom_toTopOf="@+id/guideline8"
app:layout_constraintStart_toStartOf="@+id/guideline7"
app:layout_constraintTop_toTopOf="@+id/guideline6"
app:srcCompat="@mipmap/safe" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:text="向手机号"
app:layout_constraintBottom_toTopOf="@+id/guideline8"
app:layout_constraintStart_toEndOf="@+id/imageView2"
app:layout_constraintTop_toTopOf="@+id/imageView2" />
<TextView
android:id="@+id/mPhone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:text="15823560338"
android:textColor="#F44336"
app:layout_constraintBottom_toBottomOf="@+id/textView2"
app:layout_constraintStart_toEndOf="@+id/textView2"
app:layout_constraintTop_toTopOf="@+id/textView2" />
<TextView
android:id="@+id/textView4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:text="@string/send_verify_code"
app:layout_constraintBottom_toBottomOf="@+id/mPhone"
app:layout_constraintStart_toEndOf="@+id/mPhone"
app:layout_constraintTop_toTopOf="@+id/mPhone" />
<EditText
android:id="@+id/mVerifyEditText"
android:layout_width="0dp"
android:layout_height="47dp"
android:background="@android:color/transparent"
android:ems="10"
android:inputType="number"
app:layout_constraintBottom_toTopOf="@+id/guideline9"
app:layout_constraintEnd_toStartOf="@+id/guideline10"
app:layout_constraintStart_toStartOf="@+id/guideline7"
app:layout_constraintTop_toTopOf="@+id/guideline8" />
<TextView
android:id="@+id/tv_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_user_verify_code_grey"
android:gravity="center"
app:layout_constraintBottom_toTopOf="@+id/guideline9"
app:layout_constraintEnd_toStartOf="@+id/tv_2"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintStart_toStartOf="@+id/guideline7"
app:layout_constraintTop_toTopOf="@+id/guideline8" />
<TextView
android:id="@+id/tv_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_user_verify_code_grey"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="@+id/tv_1"
app:layout_constraintEnd_toStartOf="@+id/tv_3"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/tv_1"
app:layout_constraintTop_toTopOf="@+id/tv_1" />
<TextView
android:id="@+id/tv_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_user_verify_code_grey"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="@+id/tv_2"
app:layout_constraintEnd_toStartOf="@+id/tv_4"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/tv_2"
app:layout_constraintTop_toTopOf="@+id/tv_2" />
<TextView
android:id="@+id/tv_4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_user_verify_code_grey"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="@+id/tv_3"
app:layout_constraintEnd_toStartOf="@+id/tv_5"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/tv_3"
app:layout_constraintTop_toTopOf="@+id/tv_3" />
<TextView
android:id="@+id/tv_5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_user_verify_code_grey"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="@+id/tv_4"
app:layout_constraintEnd_toStartOf="@+id/tv_6"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/tv_4"
app:layout_constraintTop_toTopOf="@+id/tv_4" />
<TextView
android:id="@+id/tv_6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_user_verify_code_grey"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="@+id/tv_5"
app:layout_constraintEnd_toStartOf="@+id/guideline10"
app:layout_constraintStart_toEndOf="@+id/tv_5"
app:layout_constraintTop_toTopOf="@+id/tv_5" />
</androidx.constraintlayout.widget.ConstraintLayout>
逻辑处理
VerifyActivity.java
class VerifyActivity : AppCompatActivity() {
private var stringBuffer = StringBuffer()
private val textViews by lazy {
arrayOf(tv_1, tv_2, tv_3, tv_4, tv_5, tv_6)
}
private var inputContent: String? = null
private var max = 6
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_verify)
//隐藏光标
mVerifyEditText.isCursorVisible = false
//获取手机号码
intent.getStringExtra("phone").apply {
mPhone.text = this
}
init()
}
/**
* 初始化
* */
private fun init() {
mVerifyEditText.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(editable: Editable?) {
if (editable.toString() != "") {
if (stringBuffer.length > 5) {
//当文本长度大于5为时,editText清空
mVerifyEditText.setText("")
} else {
//将文字添加到stringBuffer中
stringBuffer.append(editable)
mVerifyEditText.setText("")
inputContent = stringBuffer.toString()
Log.v("pxd",inputContent.toString())
if (stringBuffer.length == 6) {
//文字长度位6 则开始验证输入的验证码
}
}
for (i in stringBuffer.indices){
textViews[i].text = inputContent.toString()[i].toString()
textViews[i].setBackgroundResource(R.drawable.bg_user_verify_code_blue)
}
}
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
})
mVerifyEditText.setOnKeyListener { _, keyCode, event ->
if (keyCode == KeyEvent.KEYCODE_DEL
&& event?.action == KeyEvent.ACTION_DOWN){
if (stringBuffer.isNotEmpty()) {
stringBuffer.deleteCharAt(stringBuffer.length - 1)
for (i in stringBuffer.length until max) {
textViews[i].setBackgroundResource(R.drawable.bg_user_verify_code_grey)
textViews[i].text = ""
}
}
}
false
}
}
}
这里需要注意的是setOnKeyListener
用于设置对应的响应输入事件,它有三个参数,第一个参数不使用,所有可以用_
代替,表示忽略该参数,keyCode
代表对应的操作,这里是删除操作,event
代表对应的事件,这里表示点击或者按下事件,当按下del
删除键,删除验证码并改变对应显示验证码的输入框的背景色,返回false
表示该事件未消费,也就是可以继续消费事件,其它事件的响应能够执行。
输入框的背景色
默认背景色bg_user_verify_code_grey.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/transparent" />
<stroke
android:width="1dp"
android:color="#d3d3d3" />
<corners android:radius="4dp" />
<size
android:width="47dp"
android:height="47dp"/>
</shape>
输入验证码对应的背景色bg_user_verify_code_blue.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/transparent" />
<stroke
android:width="1dp"
android:color="@color/common_blue_0090FF" />
<corners android:radius="4dp" />
<size
android:width="47dp"
android:height="47dp"/>
</shape>
验证框输入过程背景色变化,这里通过在输入框监听事件中使用for
循环实现
for (i in stringBuffer.indices){
textViews[i].text = inputContent.toString()[i].toString()
textViews[i].setBackgroundResource(R.drawable.bg_user_verify_code_blue)
}
验证码删除过程背景色变化类似
if (stringBuffer.isNotEmpty()) {
stringBuffer.deleteCharAt(stringBuffer.length - 1)
for (i in stringBuffer.length until max) {
textViews[i].setBackgroundResource(R.drawable.bg_user_verify_code_grey)
textViews[i].text = ""
}
}
注意这里需要在验证码不为空的清空下才能执行删除操作,将删除的验证码输入框的背景色设为灰色,并将数值置空。
第三方短信验证API的使用
这里我们首先来到
Bmob
的官网,找到短信验证模块,大家可以跟着说明来实现,这里我大概提一下流程
- 首先注册Bmob账号
- 网站后台创建应用
- 然后导入SDK,支持Gradle动态导入,也可以手动导入,这个看自己
- 然后设置短信验证和短信发送
注意这些步骤在官网都有完整说明,这里就不一一叙述了,主要提一下如何实现短信验证和短信发送
- 请求发送短信验证码
通过requestSMSCode
方式给绑定手机号的该用户发送指定短信模板的短信验证码:
/**
* TODO template 如果是自定义短信模板,此处替换为你在控制台设置的自定义短信模板名称;如果没有对应的自定义短信模板,则使用默认短信模板。
*/
BmobSMS.requestSMSCode(phone, "DataSDK", new QueryListener<Integer>() {
@Override
public void done(Integer smsId, BmobException e) {
if (e == null) {
mTvInfo.append("发送验证码成功,短信ID:" + smsId + "\n");
} else {
mTvInfo.append("发送验证码失败:" + e.getErrorCode() + "-" + e.getMessage() + "\n");
}
}
});
验证验证码
通过verifySmsCode
方式可验证该短信验证码:
BmobSMS.verifySmsCode(phone, code, new UpdateListener() {
@Override
public void done(BmobException e) {
if (e == null) {
mTvInfo.append("验证码验证成功,您可以在此时进行绑定操作!\n");
User user = BmobUser.getCurrentUser(User.class);
user.setMobilePhoneNumber(phone);
user.setMobilePhoneNumberVerified(true);
user.update(new UpdateListener() {
@Override
public void done(BmobException e) {
if (e == null) {
mTvInfo.append("绑定手机号码成功");
} else {
mTvInfo.append("绑定手机号码失败:" + e.getErrorCode() + "-" + e.getMessage());
}
}
});
} else {
mTvInfo.append("验证码验证失败:" + e.getErrorCode() + "-" + e.getMessage() + "\n");
}
}
});
注意verifySmsCode
有三个参数,第一个参数填写你输入的手机号,第二个参数是输入的验证码,第三个参数对应着验证码是否正确的监听
总结:这个demo不是特别难,很容易上手,没有特别难的知识点,没事的时候可以自己实现一下,可能短信验证模块稍微麻烦一点,其实也特别简单,代码复制过来直接使用就可以了