Android中实现发送验证码功能,以及重新发送
- 引入splitedittext框架;
//类似滴滴出行验证码输入框控件
//https://github.com/jenly1314/SplitEditText
implementation 'com.github.jenly1314:splitedittext:1.1.0'
- 页面中创建控件
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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=".component.input.activity.InputCodeActivity">
<include layout="@layout/toolbar"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="@dimen/d30"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/verification_code"
android:textColor="?attr/colorOnSurface"
android:textSize="@dimen/text_large2"
android:textStyle="bold"/>
<TextView
android:id="@+id/code_send_target"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/d10"
android:text="@string/verification_code_sent_to"
android:textColor="?attr/colorOnSurface"
android:textSize="@dimen/s14"/>
<com.king.view.splitedittext.SplitEditText
android:id="@+id/code"
android:layout_width="match_parent"
android:layout_height="@dimen/d50"
android:layout_marginTop="@dimen/d40"
android:inputType="number"
app:setBorderColor="@color/divider"
app:setBorderCornerRadius="@dimen/d5"
app:setInputBorderColor="?attr/colorPrimary"/>
<Button
android:id="@+id/resend"
style="?android:attr/borderlessButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:textColor="@color/black80"
android:text="@string/resend"/>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
- 代码中使用,调度;
package com.ixuea.courses.mymusic.component.input.activity;
import static autodispose2.AutoDispose.autoDisposable;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.view.View;
import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.ixuea.courses.mymusic.R;
import com.ixuea.courses.mymusic.component.api.HttpObserver;
import com.ixuea.courses.mymusic.component.input.activity.model.ui.InputCodePageData;
import com.ixuea.courses.mymusic.component.input.model.CodeRequest;
import com.ixuea.courses.mymusic.component.login.activity.BaseLoginActivity;
import com.ixuea.courses.mymusic.component.password.activity.SetPasswordActivity;
import com.ixuea.courses.mymusic.component.password.model.ui.SetPasswordPageData;
import com.ixuea.courses.mymusic.databinding.ActivityInputCodeBinding;
import com.ixuea.courses.mymusic.model.Base;
import com.ixuea.courses.mymusic.model.response.DetailResponse;
import com.ixuea.courses.mymusic.repository.DefaultRepository;
import com.ixuea.courses.mymusic.util.Constant;
import com.king.view.splitedittext.SplitEditText;
import org.apache.commons.lang3.StringUtils;
import autodispose2.androidx.lifecycle.AndroidLifecycleScopeProvider;
public class InputCodeActivity extends BaseLoginActivity<ActivityInputCodeBinding> {
private InputCodePageData pageData;
private CodeRequest codeRequest;
private int codeStyle;
private CountDownTimer countDownTimer;
@Override
protected void initDatum() {
super.initDatum();
pageData = extraData();
codeRequest = new CodeRequest();
String target;
if(StringUtils.isNotBlank(pageData.getPhone())){
target = pageData.getPhone();
codeStyle = Constant.VALUE10;
codeRequest.setPhone(pageData.getPhone());
}else{
target=pageData.getEmail();
codeStyle = Constant.VALUE0;
codeRequest.setEmail(pageData.getEmail());
}
binding.codeSendTarget.setText(getString(R.string.verification_code_sent_to,target));
sendCode();
}
private void sendCode() {
DefaultRepository.getInstance()
.sendCode(codeStyle, codeRequest)
.to(autoDisposable(AndroidLifecycleScopeProvider.from(this)))
.subscribe(new HttpObserver<DetailResponse<Base>>() {
@Override
public void onSucceeded(DetailResponse<Base> data) {
startCountDown();
}
});
}
private void startCountDown() {
countDownTimer = new CountDownTimer(60000,1000) {
@Override
public void onTick(long millisUntilFinished) {
binding.resend.setText(getString(R.string.resend_count, millisUntilFinished / 1000));
}
@Override
public void onFinish() {
binding.resend.setText(R.string.resend);
binding.resend.setEnabled(true);
}
};
countDownTimer.start();
binding.resend.setEnabled(false);
}
@Override
protected void initListeners() {
super.initListeners();
binding.code.setOnTextInputListener(new SplitEditText.OnSimpleTextInputListener() {
@Override
public void onTextInputCompleted(@NonNull String s) {
processNext(s);
}
});
binding.resend.setOnClickListener(v -> sendCode());
}
private void processNext(String data){
if(pageData.getStyle() == Constant.STYLE_PHONE_LOGIN){
login(pageData.getPhone(),data);
}else{
codeRequest.setCode(data);
DefaultRepository.getInstance()
.checkCode(codeRequest)
.to(autoDisposable(AndroidLifecycleScopeProvider.from(this)))
.subscribe(new HttpObserver<DetailResponse<Base>>() {
@Override
public void onSucceeded(DetailResponse<Base> d) {
SetPasswordActivity.start(getHostActivity(), new SetPasswordPageData(
pageData.getPhone(),
pageData.getEmail(),
data
));
}
@Override
public boolean onFailed(DetailResponse<Base> data, Throwable e) {
binding.code.setText("");
return super.onFailed(data, e);
}
});
}
}
@Override
protected void onDestroy() {
if(countDownTimer !=null){
countDownTimer.cancel();
countDownTimer = null;
}
super.onDestroy();
}
public static void start(Context context, InputCodePageData data) {
Intent intent = new Intent(context, InputCodeActivity.class);
intent.putExtra(Constant.DATA, data);
context.startActivity(intent);
}
}