Android OTP页面如何实现仅显秒数的倒计时及重发按钮灰化效果
OTP重发倒计时功能修复方案
修复点说明
- 调整倒计时显示逻辑,仅展示剩余秒数,移除多余前缀文字
- 保留重发按钮全程可见,禁用状态下自动灰化(Android原生Button特性,无需额外配置,只要不主动设置隐藏属性即可)
- 补充OTP全部输入完成后自动停止倒计时的逻辑
核心代码修改
Java代码(OTPActivity.java)
public class OTPActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener{ private EditText editText1, editText2, editText3, editText4; private EditText[] editTexts; CountDownTimer cTimer = null; TextView tv; Button resend; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_otpactivity2); tv = (TextView) findViewById(R.id.timer); resend = (Button) findViewById(R.id.ResendOTP); editText1 = (EditText) findViewById(R.id.otpEdit1); editText2 = (EditText) findViewById(R.id.otpEdit2); editText3 = (EditText) findViewById(R.id.otpEdit3); editText4 = (EditText) findViewById(R.id.otpEdit4); editTexts = new EditText[]{editText1, editText2, editText3, editText4}; editText1.addTextChangedListener(new PinTextWatcher(0)); editText2.addTextChangedListener(new PinTextWatcher(1)); editText3.addTextChangedListener(new PinTextWatcher(2)); editText4.addTextChangedListener(new PinTextWatcher(3)); editText1.setOnKeyListener(new PinOnKeyListener(0)); editText2.setOnKeyListener(new PinOnKeyListener(1)); editText3.setOnKeyListener(new PinOnKeyListener(2)); editText4.setOnKeyListener(new PinOnKeyListener(3)); startTimer(); resend.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startTimer(); } }); } void startTimer() { resend.setEnabled(false); cTimer = new CountDownTimer(30000, 1000) { public void onTick(long millisUntilFinished) { // 仅展示剩余秒数,移除多余文字 tv.setText(String.valueOf(millisUntilFinished/1000)); } public void onFinish() { tv.setText(""); resend.setEnabled(true); } }; cTimer.start(); } //cancel timer void cancelTimer() { if(cTimer!=null) cTimer.cancel(); // 倒计时取消后清空计时文本,恢复按钮可用 tv.setText(""); resend.setEnabled(true); } @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { } @Override public void onNothingSelected(AdapterView<?> parent) { } public class PinTextWatcher implements TextWatcher { private int currentIndex; private boolean isFirst = false, isLast = false; private String newTypedString = ""; PinTextWatcher(int currentIndex) { this.currentIndex = currentIndex; if (currentIndex == 0) this.isFirst = true; else if (currentIndex == editTexts.length - 1) this.isLast = true; } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { newTypedString = s.subSequence(start, start + count).toString().trim(); } @Override public void afterTextChanged(Editable s) { String text = newTypedString; /* Detect paste event and set first char */ if (text.length() > 1) text = String.valueOf(text.charAt(0)); // TODO: We can fill out other EditTexts editTexts[currentIndex].removeTextChangedListener(this); editTexts[currentIndex].setText(text); editTexts[currentIndex].setSelection(text.length()); editTexts[currentIndex].addTextChangedListener(this); if (text.length() == 1) moveToNext(); else if (text.length() == 0) moveToPrevious(); } private void moveToNext() { if (!isLast) editTexts[currentIndex + 1].requestFocus(); if (isAllEditTextsFilled() && isLast) { // isLast is optional editTexts[currentIndex].clearFocus(); hideKeyboard(); // 所有OTP输入完成,停止倒计时 cancelTimer(); } } private void moveToPrevious() { if (!isFirst) editTexts[currentIndex - 1].requestFocus(); } private boolean isAllEditTextsFilled() { for (EditText editText : editTexts) if (editText.getText().toString().trim().length() == 0) return false; return true; } private void hideKeyboard() { if (getCurrentFocus() != null) { InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); inputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0); } } } public class PinOnKeyListener implements View.OnKeyListener { private int currentIndex; PinOnKeyListener(int currentIndex) { this.currentIndex = currentIndex; } @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_DEL && event.getAction() == KeyEvent.ACTION_DOWN) { if (editTexts[currentIndex].getText().toString().isEmpty() && currentIndex != 0) editTexts[currentIndex - 1].requestFocus(); } return false; } } }
XML布局代码(activity_otpactivity2.xml)
现有布局无需修改,只要不主动设置按钮的visibility属性即可保持全程可见,Android原生Button在setEnabled(false)时会自动呈现灰化不可点击状态:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:gravity="center" android:layout_height="match_parent" > <androidx.cardview.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto" android:id="@+id/card_view" android:layout_width="180dp" card_view:cardElevation="4dp" android:gravity="center" android:layout_height="60dp" card_view:cardCornerRadius="4dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_gravity="center"> <EditText android:id="@+id/otpEdit1" android:digits="1234567890" android:inputType="number" android:maxLength="1" android:gravity="center" android:layout_weight="1" android:layout_width="2dp" android:layout_height="wrap_content" /> <EditText android:id="@+id/otpEdit2" android:digits="1234567890" android:inputType="number" android:maxLength="1" android:gravity="center" android:layout_weight="1" android:layout_width="2dp" android:layout_height="wrap_content" /> <EditText android:id="@+id/otpEdit3" android:digits="1234567890" android:inputType="number" android:maxLength="1" android:gravity="center" android:layout_weight="1" android:layout_width="2dp" android:layout_height="wrap_content" /> <EditText android:id="@+id/otpEdit4" android:digits="1234567890" android:inputType="number" android:maxLength="1" android:gravity="center" android:layout_weight="1" android:layout_width="2dp" android:layout_height="wrap_content" /> </LinearLayout> </androidx.cardview.widget.CardView> <TextView android:id="@+id/timer" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Timer" android:layout_marginLeft="10dp"/> <Button android:id="@+id/ResendOTP" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="ResendOTP" /> </LinearLayout>
内容的提问来源于stack exchange,提问作者laxmi narayan
相关产品推荐
相关产品推荐

