image.png项目中,遇到了这样要求:实现6位验证码填写的效果,验证码是数字,输入一个后自动跳转到下一个,然后可以删除回退,界面效果如下所示:
特别说明
没有使用第三方开源库,这可能是比较烦琐的一种原始的实现方法,也没有特意去封装使用,主要理解一下实现思路即可,实现方式应该有很多种,思路其实很重要的。
实现原理
其实是比较简单的,每一个验证码号都是一个EditText,当一个输入完成,通过判断字段的长度,看看可输入的长度是不是为1,是的话然后让下一个EditText获取焦点,以此类推,然后对EditText进行各种监听管理:例如删除、删除回退、是否是最后一个等等
核心代码实现
将验证码的6个EditText添加到集合中管理,默认第一个获取焦点
image.png
image.png
image.png
对EditText进行监听处理
image.png
image.png
image.png
image.png
image.png
完整代码如下
1、界面布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
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"
style="@style/style_match_parent_white_bg"
android:id="@+id/rootview"
tools:context="com.tecsun.tsb.tosharetreasure.activity.login.VerificationCodeActivity">
<include
android:id="@+id/llt_toolbar"
layout="@layout/layout_genal_tool_bar"/>
<LinearLayout
android:layout_below="@id/llt_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<TextView
android:layout_marginTop="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="验证码短信已经发送至:"
android:textColor="@color/black"
android:layout_gravity="center"
/>
<TextView
android:id="@+id/tv_phone_num"
android:layout_marginTop="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="1380013800"
android:textColor="@color/c_000000"
android:layout_gravity="center"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:id="@+id/textView2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"/>
<EditText
android:id="@+id/et_num_one"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@null"
android:padding="@dimen/dp_4"
android:layout_weight="1"
android:maxLength="1"
android:textColor="@color/black"
android:gravity="center"
android:inputType="number"
/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
/>
<EditText
android:id="@+id/et_num_two"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@null"
android:padding="@dimen/dp_4"
android:layout_weight="1"
android:maxLength="1"
android:textColor="@color/black"
android:gravity="center"
android:inputType="number"
/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
/>
<EditText
android:id="@+id/et_num_three"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@null"
android:padding="@dimen/dp_4"
android:layout_weight="1"
android:maxLength="1"
android:textColor="@color/black"
android:gravity="center"
android:inputType="number"
/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
/>
<EditText
android:id="@+id/et_num_four"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@null"
android:padding="@dimen/dp_4"
android:layout_weight="1"
android:maxLength="1"
android:textColor="@color/black"
android:gravity="center"
android:inputType="number"
/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
/>
<EditText
android:id="@+id/et_num_five"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@null"
android:padding="@dimen/dp_4"
android:layout_weight="1"
android:maxLength="1"
android:textColor="@color/black"
android:gravity="center"
android:inputType="number"
/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
/>
<EditText
android:id="@+id/et_num_six"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@null"
android:padding="@dimen/dp_4"
android:layout_weight="1"
android:maxLength="1"
android:textColor="@color/black"
android:gravity="center"
android:inputType="number"
/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="1dp"
android:orientation="horizontal"
>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
/>
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:background="@color/black"
android:layout_weight="1"
/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
/>
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:background="@color/black"
android:layout_weight="1"
/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
/>
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:background="@color/black"
android:layout_weight="1"
/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
/>
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:background="@color/black"
android:layout_weight="1"
/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
/>
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:background="@color/black"
android:layout_weight="1"
/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
/>
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:background="@color/black"
android:layout_weight="1"
/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
/>
</LinearLayout>
</LinearLayout>
<Button
android:layout_alignParentBottom="true"
android:id="@+id/bt_start"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="开启旅程"
android:textColor="@android:color/white"
android:background="@color/c_blue_2"
/>
</RelativeLayout>
2、界面实现逻辑
/**
* 验证码页面
*/
public class VerificationCodeActivity extends BaseActivity {
private static final String TAG = VerificationCodeActivity.class.getSimpleName();
@BindView(R.id.common_titleBar_iv_back)
ImageView commonTitleBarIvBack;
@BindView(R.id.common_titleBar_tv_title)
TextView commonTitleBarTvTitle;
@BindView(R.id.tv_phone_num)
TextView tvPhoneNum;
@BindView(R.id.textView2)
TextView textView2;
@BindView(R.id.et_num_one)
EditText etNumOne;
@BindView(R.id.et_num_two)
EditText etNumTwo;
@BindView(R.id.et_num_three)
EditText etNumThree;
@BindView(R.id.et_num_four)
EditText etNumFour;
@BindView(R.id.bt_start)
Button btStart;
@BindView(R.id.rootview)
View mRootView;
@BindView(R.id.et_num_five)
EditText etNumFive;
@BindView(R.id.et_num_six)
EditText etNumSix;
private int mFlag;
private boolean mIsRegist;
private String mPhoneNum;
private ArrayList<EditText> etLists = new ArrayList<>();
private SystemInfoBean mSystemInfoBean;
private LatitudeAndLongitudeBean mLatitudeAndLongitudeBean;
private String mLoginLatitude;
private String mLoginLongitude;
@Override
public int setLayout() {
return R.layout.activity_verification_code;
}
@Override
public void init() {
initData();
initListener();
initOperation();
}
/**
* 初始化数据的方法
*/
private void initData() {
commonTitleBarTvTitle.setText("填写验证码");
Intent intent = getIntent();
if (intent != null) {
mFlag = intent.getIntExtra(Constants.ACTIVITY_PAGE_FLAG, 0);
mIsRegist = intent.getBooleanExtra(Constants.ISREGIST, false);
mPhoneNum = intent.getStringExtra(Constants.PHONE_NUM);
mSystemInfoBean = (SystemInfoBean)intent.getSerializableExtra(SYSTEM_INFO);
if (mSystemInfoBean != null){
mLatitudeAndLongitudeBean = mSystemInfoBean
.mLatitudeAndLongitudeBean;
if (mLatitudeAndLongitudeBean != null){
mLoginLatitude = mLatitudeAndLongitudeBean.loginLatitude;
mLoginLongitude = mLatitudeAndLongitudeBean.loginLongitude;
}
}
if (!TextUtils.isEmpty(mPhoneNum)) {
tvPhoneNum.setText("+86 " + mPhoneNum);
}
}
etLists.clear();
etLists.add(etNumOne);
etLists.add(etNumTwo);
etLists.add(etNumThree);
etLists.add(etNumFour);
etLists.add(etNumFive);
etLists.add(etNumSix);
showInputManager(etNumOne);
}
private void showInputManager(EditText editText) {
editText.setFocusable(true);
editText.setFocusableInTouchMode(true);
editText.requestFocus();
/** 目前测试来看,还是挺准的
* 原理:OnGlobalLayoutListener 每次布局变化时都会调用
* 界面view 显示消失都会调用,软键盘显示与消失时都调用
* */
mRootView.getViewTreeObserver().addOnGlobalLayoutListener(mLayoutChangeListener);
InputMethodManager inputManager =
(InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputManager.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT);
}
public void showSoftInput(EditText input) {
input.setFocusable(true);
input.requestFocus();
InputMethodManager inputMethodManager = (InputMethodManager) mContext.getSystemService
(Context.INPUT_METHOD_SERVICE);
inputMethodManager.showSoftInput(input, InputMethodManager.SHOW_IMPLICIT);
}
/**
* 初始化监听的方法
*/
private void initListener() {
editTextListener(etNumOne, etNumTwo);
editTextListener(etNumTwo, etNumThree);
editTextListener(etNumThree, etNumFour);
editTextListener(etNumFour, etNumFive);
editTextListener(etNumFive, etNumSix);
}
/**
* 初始化操作的方法
*/
private void initOperation() {
if (!TextUtils.isEmpty(mPhoneNum)){
send(mPhoneNum, "1");
}
}
@OnClick({R.id.common_titleBar_iv_back, R.id.bt_start})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.common_titleBar_iv_back:
finish();
break;
case R.id.bt_start:
String codeValue = getCodeValue(etLists);
if (!TextUtils.isEmpty(codeValue)){
boolean isCodeValue = isCodeValue(codeValue);
if (isCodeValue){
codePast(mPhoneNum);
}
}else {
ToastUtil.toast(mContext,"验证码不能为空");
}
break;
}
}
/**
* 通过flag来区分是跳转到修改密码还是完善资料
*
* @param flag
*/
private void checkAction(int flag) {
switch (flag) {
case Constants.FORGET_TO_VERIFICATIONCODE:
goToResetPassword();
break;
case Constants.REGIST_TO_PERFECT:
goToPerfectData();
break;
}
}
/**
* 前往信息完善页面
*/
private void goToPerfectData() {
Intent intent = new Intent(mContext,PerfectDataActivity.class);
intent.putExtra(SYSTEM_INFO,mSystemInfoBean);
startActivity(intent);
finish();
}
/**
* 从忘记密码的流程过来,前往重设密码的界面
*/
private void goToResetPassword() {
Intent intent = new Intent(mContext,ChangePwdActivity.class);
intent.putExtra(SYSTEM_INFO,mSystemInfoBean);
startActivity(intent);
finish();
}
/**
* 4个EditText自动切换
*/
private void autoMoveEdit(EditText firstEditText, EditText twoEditText) {
if (firstEditText.isFocused()) {
LogUtils.d(TAG, "autoMoveEdit======");
String oneCode = firstEditText.getText().toString();
firstEditText.setSelection(oneCode.length());
if (oneCode.length() == 1) {
setFocu(firstEditText, twoEditText);
}
}
}
/**
* 设置焦点
*/
private void setFocu(EditText beforeEt, EditText pEt) {
pEt.setFocusable(true);
pEt.setFocusableInTouchMode(true);
pEt.requestFocus();
pEt.setSelection(pEt.getText().toString().trim().length());
delFocu(beforeEt, pEt);
}
/**
* 监听软键盘的删除键,删除
*
* @param beforeEt
* @param nextEt
*/
private void delFocu(final EditText beforeEt, final EditText nextEt) {
nextEt.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (nextEt.equals(etLists.get(0))) {
return true;
}
String s = beforeEt.getText().toString();
LogUtils.d(TAG, "delFocu============s============" + s);
if (!TextUtils.isEmpty(s)) {
beforeEt.setText("");
beforeEt.setFocusable(true);
beforeEt.setFocusableInTouchMode(true);
beforeEt.requestFocus();
}
return false;
}
});
}
/**
* 对EditText监听
*
* @param beforeEdit
* @param afterEdit
*/
private void editTextListener(final EditText beforeEdit, final EditText afterEdit) {
beforeEdit.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
LogUtils.d(TAG, "beforeTextChanged======");
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
LogUtils.d(TAG, "onTextChanged======");
autoMoveEdit(beforeEdit, afterEdit);
}
@Override
public void afterTextChanged(Editable editable) {
}
});
}
/**
* 获取手动输入的验证码的值
*/
private String getCodeValue(ArrayList<EditText> list){
StringBuilder sb = new StringBuilder();
if (list != null){
int size = list.size();
if (size > 0){
for (int i = 0; i < size; i++) {
EditText editText = list.get(i);
String s = editText.getText().toString();
if (!TextUtils.isEmpty(s)){
sb.append(s);
}
}
}
}
return sb.toString();
}
/**
* 判断有没有输入6位验证码
*/
private boolean isCodeValue(String codeValue){
int length = codeValue.length();
if (length == 6){
return true;
}else {
return false;
}
}
ViewTreeObserver.OnGlobalLayoutListener mLayoutChangeListener = new ViewTreeObserver
.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//判断窗口可见区域大小
Rect r = new Rect();
// getWindowVisibleDisplayFrame()会返回窗口的可见区域高度
getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
//如果屏幕高度和Window可见区域高度差值大于整个屏幕高度的1/3,则表示软键盘显示中,否则软键盘为隐藏状态。
int heightDifference = WindowManagerUtils.getScreenHight() - (r.bottom);
boolean isKeyboardShowing = heightDifference > WindowManagerUtils.getScreenHight() / 3;
if (isKeyboardShowing) {
// D.i("slack","show..."+ r.bottom + " - " + r.top + " = " + (r.bottom - r.top)
// +","+ heightDifference);
// bottomView 需要跟随软键盘移动的布局
// setDuration(0) 默认300, 设置 0 ,表示动画执行时间为0,没有过程,只有动画结果了
btStart.animate().translationY(-heightDifference).setDuration(0).start();
} else {
// D.i("slack","hide...");
btStart.animate().translationY(0).start();
}
}
};
/**
* 短信发送接口
*/
private void send(String phone, String type){
SendParam param = new SendParam(phone,type);
AppRequestImpl.getInstance( ).send(phone,type,new ProgressSubscriber<ReplyBaseResultBean<String>>(mContext,getString(R.string.tip_is_send_code), new SubscriberResultListener() {
@Override
public void onNext(Object o) {
ReplyBaseResultBean<String> bean = (ReplyBaseResultBean<String>) o;
loadSendData( bean );
}
@Override
public void onErr(Throwable e) {
// showWarningDialog(R.string.tip_network_error);
}
}) );
}
/**
* 处理发送短信数据
* @param bean
*/
protected void loadSendData(ReplyBaseResultBean<String> bean ){
if ( bean == null ){
return;
}
String result = bean.result;
if (bean.isSuccess()){
ToastUtil.toast(mContext,"验证码已发送,请注意查收!");
}else {
if (!TextUtils.isEmpty(result)){
ToastUtil.toast(mContext,result);
}
}
}
/**
* 短信验证码是否过期
*/
private void codePast(String phone){
CodePastParam param = new CodePastParam(phone);
AppRequestImpl.getInstance( ).codePast(param,new ProgressSubscriber<CodePastBean>(mContext,getString(R.string.tip_is_code_past), new SubscriberResultListener() {
@Override
public void onNext(Object o) {
CodePastBean bean = (CodePastBean) o;
loadCodePastData( bean );
}
@Override
public void onErr(Throwable e) {
// showWarningDialog(R.string.tip_network_error);
}
}) );
}
/**
* 处理验证码有效期数据
* @param bean
*/
protected void loadCodePastData(CodePastBean bean ){
if ( bean == null ){
return;
}
boolean past = bean.past;
if (past){ //验证码有效,前往完善信息的页面
checkAction(mFlag);
}else { //重新请求发送验证码
initOperation();
}
}
}
网友评论
https://www.jianshu.com/p/8d4d33b02da1
组件化和插件化的开发里程总结
https://www.jianshu.com/p/df2a6717009d