嗨,我正在开发一个应用程序,当用户想要重置他的密码时我要求OTP,我需要一个像附加图像中那样的文本...我想要继续的是每个人的单独文本字母,所有这些都以水平方向的线性布局排列,有一些边距......最大长度为1,因此每个editText只能输入一个字母......这是正确的方法?? 任何建议??
答案 0 :(得分:15)
您可以尝试使用 TextWatcher 更多Generic,因此易于使用和理解
使用以下课程:
public class GenericTextWatcher implements TextWatcher
{
private View view;
private GenericTextWatcher(View view)
{
this.view = view;
}
@Override
public void afterTextChanged(Editable editable) {
// TODO Auto-generated method stub
String text = editable.toString();
switch(view.getId())
{
case R.id.editText1:
if(text.length()==1)
et2.requestFocus();
break;
case R.id.editText2:
if(text.length()==1)
et3.requestFocus();
else if(text.length()==0)
et1.requestFocus();
break;
case R.id.editText3:
if(text.length()==1)
et4.requestFocus();
else if(text.length()==0)
et2.requestFocus();
break;
case R.id.editText4:
if(text.length()==0)
et3.requestFocus();
break;
}
}
@Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub
}
@Override
public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub
}
}
如何使用上述课程
et1.addTextChangedListener(new GenericTextWatcher(et1));
et2.addTextChangedListener(new GenericTextWatcher(et2));
et3.addTextChangedListener(new GenericTextWatcher(et3));
et4.addTextChangedListener(new GenericTextWatcher(et4));
这里 et1,et2,et3和et4 是你的EditTexts,我知道它根据Java标准的错误命名约定,但你可以用你的替换它。
P.S 您可以在此处找到xml设计 GitHub some other, sample design xml for reference
答案 1 :(得分:14)
在所有这些答案之后,考虑到UI / UX,我没有找到想要的东西,元素的删除存在缺陷,以至于可以返回到先前的EditText
,当前的EditText不应是空的。
这是我在Kotlin中实现的解决方案,该解决方案可通过按键盘上的Delete键进行删除。此外,删除功能的实现方式是,当当前EditText
为空并按下Delete键时,它将切换回前一个EditText
并删除其元素。
这样调用函数:
//GenericTextWatcher here works only for moving to next EditText when a number is entered
//first parameter is the current EditText and second parameter is next EditText
editText1.addTextChangedListener(GenericTextWatcher(editText1, editText2))
editText2.addTextChangedListener(GenericTextWatcher(editText2, editText3))
editText3.addTextChangedListener(GenericTextWatcher(editText3, editText4))
editText4.addTextChangedListener(GenericTextWatcher(editText4, null))
//GenericKeyEvent here works for deleting the element and to switch back to previous EditText
//first parameter is the current EditText and second parameter is previous EditText
editText1.setOnKeyListener(GenericKeyEvent(editText1, null))
editText2.setOnKeyListener(GenericKeyEvent(editText2, editText1))
editText3.setOnKeyListener(GenericKeyEvent(editText3, editText2))
editText4.setOnKeyListener(GenericKeyEvent(editText4,editText3))
现在,将这两个类粘贴到当前类中
class GenericKeyEvent internal constructor(private val currentView: EditText, private val previousView: EditText?) : View.OnKeyListener{
override fun onKey(p0: View?, keyCode: Int, event: KeyEvent?): Boolean {
if(event!!.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_DEL && currentView.id != R.id.editText1 && currentView.text.isEmpty()) {
//If current is empty then previous EditText's number will also be deleted
previousView!!.text = null
previousView.requestFocus()
return true
}
return false
}
}
class GenericTextWatcher internal constructor(private val currentView: View, private val nextView: View?) : TextWatcher {
override fun afterTextChanged(editable: Editable) { // TODO Auto-generated method stub
val text = editable.toString()
when (currentView.id) {
R.id.editText1 -> if (text.length == 1) nextView!!.requestFocus()
R.id.editText2 -> if (text.length == 1) nextView!!.requestFocus()
R.id.editText3 -> if (text.length == 1) nextView!!.requestFocus()
//You can use EditText4 same as above to hide the keyboard
}
}
override fun beforeTextChanged(
arg0: CharSequence,
arg1: Int,
arg2: Int,
arg3: Int
) { // TODO Auto-generated method stub
}
override fun onTextChanged(
arg0: CharSequence,
arg1: Int,
arg2: Int,
arg3: Int
) { // TODO Auto-generated method stub
}
}
此外,要禁用可见光标,可以在布局的android:cursorVisible="false"
标签中使用EditText
,也可以使用Java函数setCursorVisible(false)
。
编辑:我使用的是股票小部件EditTexts
,因此,如果您想在它们周围显示一个框,只需创建一个可绘制的版面并将其设置为EditTexts
的背景并给它们填充5dp。这将创建一个框,并使它看起来更酷。
答案 2 :(得分:6)
OtpEditText.java(自定义EditText):
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.Editable;
import android.util.AttributeSet;
import android.view.ActionMode;
import android.view.View;
import androidx.appcompat.widget.AppCompatEditText;
public class OtpEditText extends AppCompatEditText {
private float mSpace = 24; //24 dp by default, space between the lines
private float mNumChars = 4;
private float mLineSpacing = 8; //8dp by default, height of the text from our lines
private int mMaxLength = 4;
private float mLineStroke = 2;
private Paint mLinesPaint;
private OnClickListener mClickListener;
public OtpEditText(Context context) {
super(context);
}
public OtpEditText(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public OtpEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
float multi = context.getResources().getDisplayMetrics().density;
mLineStroke = multi * mLineStroke;
mLinesPaint = new Paint(getPaint());
mLinesPaint.setStrokeWidth(mLineStroke);
mLinesPaint.setColor(getResources().getColor(R.color.colorPrimaryDark));
setBackgroundResource(0);
mSpace = multi * mSpace; //convert to pixels for our density
mLineSpacing = multi * mLineSpacing; //convert to pixels for our density
mNumChars = mMaxLength;
super.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// When tapped, move cursor to end of text.
setSelection(getText().length());
if (mClickListener != null) {
mClickListener.onClick(v);
}
}
});
}
@Override
public void setOnClickListener(OnClickListener l) {
mClickListener = l;
}
@Override
public void setCustomSelectionActionModeCallback(ActionMode.Callback actionModeCallback) {
throw new RuntimeException("setCustomSelectionActionModeCallback() not supported.");
}
@Override
protected void onDraw(Canvas canvas) {
int availableWidth = getWidth() - getPaddingRight() - getPaddingLeft();
float mCharSize;
if (mSpace < 0) {
mCharSize = (availableWidth / (mNumChars * 2 - 1));
} else {
mCharSize = (availableWidth - (mSpace * (mNumChars - 1))) / mNumChars;
}
int startX = getPaddingLeft();
int bottom = getHeight() - getPaddingBottom();
//Text Width
Editable text = getText();
int textLength = text.length();
float[] textWidths = new float[textLength];
getPaint().getTextWidths(getText(), 0, textLength, textWidths);
for (int i = 0; i < mNumChars; i++) {
canvas.drawLine(startX, bottom, startX + mCharSize, bottom, mLinesPaint);
if (getText().length() > i) {
float middle = startX + mCharSize / 2;
canvas.drawText(text, i, i + 1, middle - textWidths[0] / 2, bottom - mLineSpacing, getPaint());
}
if (mSpace < 0) {
startX += mCharSize * 2;
} else {
startX += mCharSize + mSpace;
}
}
}
}
在XML中使用此自定义的EditText,如下所示:
<OtpEditText
android:id="@+id/et_otp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:cursorVisible="false"
android:digits="1234567890"
android:inputType="number"
android:maxLength="4"
android:textIsSelectable="false"
android:textSize="20sp"/>
参考:
文章:https://medium.com/@ali.muzaffar/building-a-pinentryedittext-in-android-5f2eddcae5d3
示例代码: https://gist.github.com/alphamu/0d3055e0233c5749b8d6
答案 3 :(得分:5)
我根据其他答案实现了以下代码。
我希望这段代码非常简单,经过优化并且可以理解更改。
请勿在您的xml中使用aws s3 ls
。
aws cp --recursive s3://<source bucket> s3://<destination bucket>
答案 4 :(得分:2)
使用4种不同的EditText。使用以下代码在后续输入后更改焦点。
private EditText editText1;
private EditText editText2;
private EditText editText3;
private EditText editText4;
editText1.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (editText1.getText().toString().length() == 1) //size as per your requirement
{
editText2.requestFocus();
}
}
public void beforeTextChanged(CharSequence s, int start,
int count, int after) {
}
public void afterTextChanged(Editable s) {
}
});
editText2.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (editText2.getText().toString().length() == 1) //size as per your requirement
{
editText3.requestFocus();
}
}
public void beforeTextChanged(CharSequence s, int start,
int count, int after) {
}
public void afterTextChanged(Editable s) {
}
});
依旧......
连接所有EditText中的文本。
答案 5 :(得分:2)
我为6位数的OTP制作了通用的TextWatcher:
public class GenericTextWatcher implements TextWatcher {
private View view;
GenericTextWatcher(View view) {
this.view = view;
}
@Override
public void afterTextChanged(Editable s) {
boolean allOtherFilled = false;
EditText nextEdit = null;
EditText previousEdit = null;
switch (view.getId()) {
case R.id.otp_et1:
allOtherFilled = otpEdit2.getText().length() == 1
&& otpEdit3.getText().length() == 1
&& otpEdit4.getText().length() == 1
&& otpEdit5.getText().length() == 1
&& otpEdit6.getText().length() == 1;
nextEdit = otpEdit2;
break;
case R.id.otp_et2:
allOtherFilled = otpEdit1.getText().length() == 1
&& otpEdit3.getText().length() == 1
&& otpEdit4.getText().length() == 1
&& otpEdit5.getText().length() == 1
&& otpEdit6.getText().length() == 1;
nextEdit = otpEdit3;
previousEdit = otpEdit1;
break;
case R.id.otp_et3:
allOtherFilled = otpEdit1.getText().length() == 1
&& otpEdit2.getText().length() == 1
&& otpEdit4.getText().length() == 1
&& otpEdit5.getText().length() == 1
&& otpEdit6.getText().length() == 1;
nextEdit = otpEdit4;
previousEdit = otpEdit2;
break;
case R.id.otp_et4:
allOtherFilled = otpEdit1.getText().length() == 1
&& otpEdit2.getText().length() == 1
&& otpEdit3.getText().length() == 1
&& otpEdit5.getText().length() == 1
&& otpEdit6.getText().length() == 1;
nextEdit = otpEdit5;
previousEdit = otpEdit3;
break;
case R.id.otp_et5:
allOtherFilled = otpEdit1.getText().length() == 1
&& otpEdit2.getText().length() == 1
&& otpEdit3.getText().length() == 1
&& otpEdit4.getText().length() == 1
&& otpEdit6.getText().length() == 1;
nextEdit = otpEdit6;
previousEdit = otpEdit4;
break;
case R.id.otp_et6:
allOtherFilled = otpEdit1.getText().length() == 1
&& otpEdit2.getText().length() == 1
&& otpEdit3.getText().length() == 1
&& otpEdit4.getText().length() == 1
&& otpEdit5.getText().length() == 1;
previousEdit = otpEdit5;
break;
}
if (s.length() == 1) {
if (allOtherFilled) {
//if next 2 edit texts are filled , enable the pay button
enableDisableButton(continueButton, true);
KeyboardUtils.hideKeyboard(LoginActivity.this, (EditText) view);
}
} else if (s.length() > 1) {
if (allOtherFilled) {
//if all next edit texts are filled , enable the pay button
enableDisableButton(continueButton, true);
KeyboardUtils.hideKeyboard(LoginActivity.this, (EditText) view);
} else if (nextEdit != null) {
if (nextEdit.getText().length() == 0) {
//if next edit is not filled, move to next edit and set the second digit
moveToNextEdit(nextEdit, (EditText) view);
} else {
//if any other edit is not filled, stay in current edit
enableDisableButton(continueButton, false);
stayOnCurrentEdit((EditText) view);
}
}
} else if (s.length() < 1) {
if (null != previousEdit)
moveToPreviousEdit(previousEdit);
enableDisableButton(continueButton, false);
}
}
@Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
}
@Override
public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
}
private void stayOnCurrentEdit(EditText editText) {
editText.setText(editText.getText().toString().substring(0, 1));
editText.setSelection(editText.getText().length());
}
private void moveToPreviousEdit(EditText editText) {
editText.setSelection(editText.getText().length());
editText.requestFocus();
}
private void moveToNextEdit(EditText editText2, EditText editText1) {
editText2.setText(editText1.getText().toString().substring(1, 2));
editText2.requestFocus();
editText2.setSelection(editText2.getText().length());
editText1.setText(editText1.getText().toString().substring(0, 1));
}
}
您可以将此textWatcher添加到所有这样的编辑文本中:
this.otpEdit1.addTextChangedListener(new GenericTextWatcher(otpEdit1));
答案 6 :(得分:1)
使用 Kotlin,您可以使用这种方法:
fun configOtpEditText(vararg etList: EditText) {
val afterTextChanged = { index: Int, e: Editable? ->
val view = etList[index]
val text = e.toString()
when (view.id) {
// first text changed
etList[0].id -> {
if (text.isNotEmpty()) etList[index + 1].requestFocus()
}
// las text changed
etList[etList.size - 1].id -> {
if (text.isEmpty()) etList[index - 1].requestFocus()
}
// middle text changes
else -> {
if (text.isNotEmpty()) etList[index + 1].requestFocus()
else etList[index - 1].requestFocus()
}
}
false
}
etList.forEachIndexed { index, editText ->
editText.doAfterTextChanged { afterTextChanged(index, it) }
}
}
像这样配置尽可能多的 EditText :
configOtpEditText(
binding.et1,
binding.et2,
binding.et3,
binding.et4,
binding.et5,
binding.et6
)
答案 7 :(得分:1)
首先制作一个Text watcher类 以便如果您有多个需要 nextFocus 的屏幕可以通过此完成 所以 TextWatcher 类
公共类 GenericTextWatcher 实现 TextWatcher { 私人最终 EditText etPrev,etNext;
private View view;
public GenericTextWatcher(EditText etNext ,EditText etPrev)
{
this.etPrev=etPrev;
this.etNext=etNext;
}
@Override
public void afterTextChanged(Editable editable) {
String text = editable.toString();
if(text.length()==1){
etNext.requestFocus();
}else{
etPrev.requestFocus();
}
}
@Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
}
@Override
public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
}
}
和 EditText nextFocus 自动化代码
et1.addTextChangedListener(new GenericTextWatcher(et2,et1));
et2.addTextChangedListener(new GenericTextWatcher(et3,et1));
et3.addTextChangedListener(new GenericTextWatcher(et4,et2));
et4.addTextChangedListener(new GenericTextWatcher(et4,et3));
答案 8 :(得分:1)
我为此创建了一个简单的库。看看这个。
https://github.com/hexdecimal16/EditTextPin
用法
<com.dhairytripathi.library.EditTextPin
android:id="@+id/editTextPin"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
EditTextPin editTextPin = findViewById(R.id.editTextPin);
String pin = editTextPin.getPin(); //To get the current entered pin
app:underlineColor="" <!-- To change underline color-->
答案 9 :(得分:1)
在Kotlin中,您可以像下面这样使用波纹管。效果很好
name: NPM Publish
on:
push:
branches:
- master
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12.x
- name: Get yarn cache directory
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v1
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install dependencies
if: steps.yarn-cache.outputs.cache-hit != 'true'
run: yarn install --frozen-lockfile
- name: Test cases
run: yarn run pdn:test
答案 10 :(得分:1)
对于@SachinMandhare在https://stackoverflow.com/a/57337907/8765580上提出的问题
只需将代码修改为
String pattern = "";
for (int j = 0; j < getText().toString().length(); j++) {
pattern += "*"; //Any Character that you want to display
}
for (int i = 0; i < mNumChars; i++) {
canvas.drawLine(startX, bottom, startX + mCharSize, bottom, mLinesPaint);
if (getText().length() > i) {
float middle = startX + mCharSize / 2;
canvas.drawText(pattern, i, i + 1, middle - textWidths[0] / 2, bottom - mLineSpacing, getPaint());
}
if (mSpace < 0) {
startX += mCharSize * 2;
} else {
startX += mCharSize + mSpace;
}
}
答案 11 :(得分:1)
public class GenericTextWatcher implements TextWatcher {
private EditText etPrev;
private EditText etNext;
public GenericTextWatcher(EditText etNext, EditText etPrev) {
this.etPrev = etPrev;
this.etNext = etNext;
}
@Override
public void afterTextChanged(Editable editable) {
String text = editable.toString();
if (text.length() == 1)
etNext.requestFocus();
else if (text.length() == 0)
etPrev.requestFocus();
}
@Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
}
@Override
public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
}
}
接下来,我们在每个edittext上添加addTextChangedListener。
e1.addTextChangedListener(GenericTextWatcher(e2, e1))
e2.addTextChangedListener(GenericTextWatcher(e3, e1))
e3.addTextChangedListener(GenericTextWatcher(e4, e2))
e4.addTextChangedListener(GenericTextWatcher(e5, e3))
e5.addTextChangedListener(GenericTextWatcher(e6, e4))
e6.addTextChangedListener(GenericTextWatcher(e6, e5))
答案 12 :(得分:1)
使用数据绑定:
class EnterOTPDialogFragment extends Fragment {
FragmentEnterOtpdialogBinding binding;
GenericTextWatcher watcher1;
GenericTextWatcher watcher2;
GenericTextWatcher watcher3;
GenericTextWatcher watcher4;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_enter_otpdialog, container, false);
watcher1 = new GenericTextWatcher(binding.optDigit1);
watcher2 = new GenericTextWatcher(binding.optDigit2);
watcher3 = new GenericTextWatcher(binding.optDigit3);
watcher4 = new GenericTextWatcher(binding.optDigit4);
binding.optDigit1.addTextChangedListener(watcher1);
binding.optDigit1.setOnKeyListener(watcher1);
binding.optDigit2.addTextChangedListener(watcher2);
binding.optDigit2.setOnKeyListener(watcher2);
binding.optDigit3.addTextChangedListener(watcher3);
binding.optDigit3.setOnKeyListener(watcher3);
binding.optDigit4.addTextChangedListener(watcher4);
binding.optDigit4.setOnKeyListener(watcher4);
return binding.getRoot();
}
public class GenericTextWatcher implements TextWatcher, View.OnKeyListener {
private View view;
String previousText = "";
private GenericTextWatcher(View view) {
this.view = view;
}
@Override
public void afterTextChanged(Editable editable) {
// TODO Auto-generated method stub
String text = editable.toString();
switch (view.getId()) {
case R.id.optDigit1:
if (text.length() == 1) {
if (previousText.length() > 0) {
binding.optDigit1.removeTextChangedListener(watcher1);
binding.optDigit1.setText(previousText);
binding.optDigit1.addTextChangedListener(watcher1);
binding.optDigit2.removeTextChangedListener(watcher2);
binding.optDigit2.setText(text);
binding.optDigit2.addTextChangedListener(watcher2);
}
binding.optDigit2.requestFocus();
}
break;
case R.id.optDigit2:
if (text.length() == 1) {
if (previousText.length() > 0) {
binding.optDigit2.removeTextChangedListener(watcher2);
binding.optDigit2.setText(previousText);
binding.optDigit2.addTextChangedListener(watcher2);
binding.optDigit3.removeTextChangedListener(watcher3);
binding.optDigit3.setText(text);
binding.optDigit3.addTextChangedListener(watcher3);
}
binding.optDigit3.requestFocus();
} else if (text.length() == 0)
binding.optDigit1.requestFocus();
break;
case R.id.optDigit3:
if (text.length() == 1) {
if (previousText.length() > 0) {
binding.optDigit3.removeTextChangedListener(watcher3);
binding.optDigit3.setText(previousText);
binding.optDigit3.addTextChangedListener(watcher3);
binding.optDigit4.removeTextChangedListener(watcher4);
binding.optDigit4.setText(text);
binding.optDigit4.addTextChangedListener(watcher4);
}
binding.optDigit4.requestFocus();
} else if (text.length() == 0)
binding.optDigit2.requestFocus();
break;
case R.id.optDigit4:
if (text.length() == 0) {
binding.optDigit3.requestFocus();
} else {
try {
final InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getView().getWindowToken(), 0);
Log.e(TAG, "afterTextChanged: hide keyboard");
} catch (Exception e) {
Log.e(TAG, "afterTextChanged: " + e.toString());
}
}
break;
}
}
@Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub
Log.d(TAG, "beforeTextChanged: " + arg0);
if (arg0.length() > 0) {
previousText = arg0.toString();
}
}
@Override
public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub
}
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
previousText = "";
Log.d(TAG, "onKey: keyCode = " + keyCode + ", event = " + event.toString());
if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KEYCODE_DEL) {
switch (view.getId()) {
case R.id.optDigit2:
if (binding.optDigit2.getText().toString().trim().length() == 0)
binding.optDigit1.requestFocus();
break;
case R.id.optDigit3:
if (binding.optDigit3.getText().toString().trim().length() == 0)
binding.optDigit2.requestFocus();
break;
case R.id.optDigit4:
if (binding.optDigit4.getText().toString().trim().length() == 0)
binding.optDigit3.requestFocus();
else if (binding.optDigit4.getText().toString().trim().length() == 1)
try {
((BaseActivity) getActivity()).hideSoftKeyboard();
} catch (Exception e) {
Log.e(TAG, "afterTextChanged: " + e.toString());
}
break;
}
}
return false;
}
}
}
答案 13 :(得分:1)
如果您想添加一些额外的逻辑来删除OTP,可以尝试一下,我根据A.R.的回答创建了它。
确保为所有edittext设置maxlength = 2
public class GenericTextWatcher implements TextWatcher
{
private View view;
private GenericTextWatcher(View view)
{
this.view = view;
}
@Override
public void afterTextChanged(Editable editable) {
// TODO Auto-generated method stub
String text = editable.toString();
switch (view.getId()) {
case R.id.etOTP1:
if (text.length() > 1) {
etOTP1.setText(String.valueOf(text.charAt(0)));
etOTP2.setText(String.valueOf(text.charAt(1)));
etOTP2.requestFocus();
etOTP2.setSelection(etOTP2.getText().length());
}
break;
case R.id.etOTP2:
if (text.length() > 1){
etOTP2.setText(String.valueOf(text.charAt(0)));
etOTP3.setText(String.valueOf(text.charAt(1)));
etOTP3.requestFocus();
etOTP3.setSelection(etOTP3.getText().length());
}
if (text.length() == 0){
etOTP1.requestFocus();
etOTP1.setSelection(etOTP1.getText().length());
}
break;
case R.id.etOTP3:
if (text.length() > 1){
etOTP3.setText(String.valueOf(text.charAt(0)));
etOTP4.setText(String.valueOf(text.charAt(1)));
etOTP4.requestFocus();
etOTP4.setSelection(etOTP4.getText().length());
}
if (text.length() == 0){
etOTP2.requestFocus();
etOTP2.setSelection(etOTP2.getText().length());
}
break;
case R.id.etOTP4:
if (text.length() == 0){
etOTP3.requestFocus();
etOTP3.setSelection(etOTP3.getText().length());
}
break;
}
}
@Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
}
@Override
public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
}
}
答案 14 :(得分:1)
以下解决方案已考虑在内:
在聚焦的编辑文本中输入一位OTP时,自动聚焦到下一个编辑文本。
在聚焦的编辑文本中删除一位OTP时,自动聚焦到上一个编辑文本。
onTextChanged()和afterTextChanged()中的工作组合有助于实现相同目的。
private EditText firstDigitOtpEdt, secondDigitOtpEdt, thirdDigitOtpEdt, fourthDigitOtpEdt;
firstDigitOtpEdt.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
if (firstDigitOtpEdt.getText().toString().length() == 1) {
secondDigitOtpEdt.requestFocus();
}
}
});
secondDigitOtpEdt.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if (secondDigitOtpEdt.getText().toString().length() == 0) {
firstDigitOtpEdt.requestFocus();
}
}
@Override
public void afterTextChanged(Editable editable) {
if (secondDigitOtpEdt.getText().toString().length() == 1) {
thirdDigitOtpEdt.requestFocus();
}
}
});
thirdDigitOtpEdt.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if (thirdDigitOtpEdt.getText().toString().length() == 0) {
secondDigitOtpEdt.requestFocus();
}
}
@Override
public void afterTextChanged(Editable editable) {
if (thirdDigitOtpEdt.getText().toString().length() == 1) {
fourthDigitOtpEdt.requestFocus();
}
}
});
fourthDigitOtpEdt.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if (fourthDigitOtpEdt.getText().toString().length() == 0) {
thirdDigitOtpEdt.requestFocus();
}
}
@Override
public void afterTextChanged(Editable editable) {
// We can call api to verify the OTP here or on an explicit button click
}
});
答案 15 :(得分:0)
使用DataBinding布局:
public class EnterOTPActivity extends AppCompatActivity {
private ActivityEnterOtpBinding binding;
private Context mContext;
private int currentEditIndex;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_enter_otp);
mContext = this;
binding.et1.addTextChangedListener(new MyTextChangeWatcher(1));
binding.et2.addTextChangedListener(new MyTextChangeWatcher(2));
binding.et3.addTextChangedListener(new MyTextChangeWatcher(3));
binding.et4.addTextChangedListener(new MyTextChangeWatcher(4));
binding.et5.addTextChangedListener(new MyTextChangeWatcher(5));
binding.et6.addTextChangedListener(new MyTextChangeWatcher(6));
binding.et1.setOnKeyListener(keyListener);
binding.et2.setOnKeyListener(keyListener);
binding.et3.setOnKeyListener(keyListener);
binding.et4.setOnKeyListener(keyListener);
binding.et5.setOnKeyListener(keyListener);
binding.et6.setOnKeyListener(keyListener);
}
private View.OnKeyListener keyListener = new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if ((((EditText) v).getText().toString() == null || ((EditText) v)
.getText().toString().isEmpty())
&& keyCode == KeyEvent.KEYCODE_DEL
&& event.getAction() == KeyEvent.ACTION_DOWN) {
if (currentEditIndex == 6)
currentEditIndex = 5;
if (currentEditIndex > 0) {
EditText editText = getEditTextFromIndex(currentEditIndex);
editText.setText("");
editText.requestFocusFromTouch();
currentEditIndex--;
}
}
return false;
}
};
class MyTextChangeWatcher implements TextWatcher {
private int index;
public MyTextChangeWatcher(int index) {
super();
this.index = index;
}
@Override
public void afterTextChanged(Editable s) {
if (s != null && s.length() == 1) {
if (index < 7) {
if (index < 6) {
EditText editText = getEditTextFromIndex(index);
editText.clearFocus();
getEditTextFromIndex(index + 1).requestFocusFromTouch();
}
currentEditIndex = index;
} else {
}
} else {
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
}
}
private EditText getEditTextFromIndex(int index) {
switch (index) {
case 1:
return binding.et1;
case 2:
return binding.et2;
case 3:
return binding.et3;
case 4:
return binding.et4;
case 5:
return binding.et5;
case 6:
return binding.et6;
default:
break;
}
return null;
}
}
答案 16 :(得分:0)
仅使用具有自定义背景的 EditText
。
<RelativeLayout
android:layout_width="280dp"
android:layout_height="44dp"
android:layout_gravity="center"
android:background="#eef">
<ProgressBar
android:id="@+id/enterProgressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="10dp"
android:layout_alignBottom="@+id/idEnterCode"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:max="60"
android:progressBackgroundTint="@android:color/transparent"
android:progressTint="@color/colorPrimaryDark" />
<EditText
android:id="@+id/idEnterCode"
android:layout_width="match_parent"
android:layout_height="44dp"
android:background="@drawable/ic_otp_block"
android:backgroundTint="@color/colorAppBackground"
android:cursorVisible="false"
android:imeOptions="flagNoFullscreen|actionDone"
android:inputType="number"
android:letterSpacing="1.48"
android:maxLength="6"
android:text="5623"
android:padding="5dp"
android:textIsSelectable="false"
android:textSize="22dp"
tools:ignore="SpUsage" />
</RelativeLayout>
此处ic_otp_block是矢量资产(使用SVG),它是一个矩形块,上面带有透明的圆形孔。
注意:这是一个模糊的png图片,可用于测试或将其转换为SVG。
现在在您的Java文件中:
private ProgressBar enterProgressBar;
private EditText enterCode;
...
enterCode.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
@Override
public void afterTextChanged(Editable editable) {
if (enterCode.getText().length() == 0) {
setProgressAnimate(enterProgressBar, 0);
} else if (enterCode.getText().length() == 1) {
setProgressAnimate(enterProgressBar, 10);
} else if (enterCode.getText().length() == 2) {
setProgressAnimate(enterProgressBar, 20);
} else if (enterCode.getText().length() == 3) {
setProgressAnimate(enterProgressBar, 30);
} else if (enterCode.getText().length() == 4) {
setProgressAnimate(enterProgressBar, 40);
} else if (enterCode.getText().length() == 5) {
setProgressAnimate(enterProgressBar, 50);
} else if (enterCode.getText().length() == 6) {
setProgressAnimate(enterProgressBar, 60);
String code = enterCode.getText().toString().trim();
verifyVerificationCode(code);
}
}
});
...
private void setProgressAnimate(ProgressBar pb, int progressTo) {
ObjectAnimator animation = ObjectAnimator.ofInt(pb, "progress",
pb.getProgress(), progressTo);
animation.setDuration(200);
animation.setInterpolator(new DecelerateInterpolator());
animation.start();
}
输出:
答案 17 :(得分:0)
我在https://gist.github.com/ShivamPokhriyal/8d0cf4aef062e6c59d00c04c53e03158处创建了要点,您只需在项目中复制粘贴即可。
它将创建一个自定义的OTPEditText类,该类可在用户键入内容时处理将焦点移至下一个或上一个edittext的问题,并在用户长按otp并将其粘贴到editText时处理粘贴事件。所有这些只能在xml中完成。无需用这些东西污染您的活动。
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* This class handles otp input in multiple edittexts.
* It will move focus to next edittext, if available, when user enters otp.
* And it will move focus to the previous edittext, if available, when user deletes otp.
* It will also delegate the paste option, if user long presses and pastes a string into the otp input.
*
* <b>XML attributes</b>
*
* @attr ref your_package_name.R.styleable#OTPView_nextView
* @attr ref your_package_name.R.styleable#OTPView_prevView
*
* @author $|-|!˅@M
*/
public class OTPEditText extends androidx.appcompat.widget.AppCompatEditText {
@Nullable
private View nextView;
@Nullable
private View previousView;
// Unfortunately getParent returns null inside the constructor. So we need to store the IDs.
private int nextViewId;
private int previousViewId;
@Nullable
private Listener listener;
private static final int NO_ID = -1;
public interface Listener {
void onPaste(String s);
}
public OTPEditText(@NonNull Context context) {
super(context);
}
public OTPEditText(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public OTPEditText(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
public void setListener(Listener listener) {
this.listener = listener;
}
/**
* Called when a context menu option for the text view is selected. Currently
* this will be one of {@link android.R.id#selectAll}, {@link android.R.id#cut},
* {@link android.R.id#copy}, {@link android.R.id#paste} or {@link android.R.id#shareText}.
*
* @return true if the context menu item action was performed.
*/
@Override
public boolean onTextContextMenuItem(int id) {
if (id == android.R.id.paste) {
ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
// Examines the item on the clipboard. If getText() does not return null, the clip item contains the
// text. Assumes that this application can only handle one item at a time.
ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0);
// Gets the clipboard as text.
CharSequence pasteData = item.getText();
if (listener != null && pasteData != null) {
listener.onPaste(pasteData.toString());
return true;
}
}
return super.onTextContextMenuItem(id);
}
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
// If we've gotten focus here
if (focused && this.getText() != null) {
this.setSelection(this.getText().length());
}
}
private void init(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.OTPView, 0, 0);
nextViewId = typedArray.getResourceId(R.styleable.OTPView_nextView, NO_ID);
previousViewId = typedArray.getResourceId(R.styleable.OTPView_prevView, NO_ID);
typedArray.recycle();
this.setOnKeyListener((v, keyCode, event) -> {
if (event.getAction()!= KeyEvent.ACTION_DOWN) {
return true;
}
//You can identify which key pressed by checking keyCode value with KeyEvent.KEYCODE_
if(keyCode == KeyEvent.KEYCODE_DEL) {
// Back pressed. If we have a previous view. Go to it.
if (getPreviousView() != null) {
getPreviousView().requestFocus();
return true;
}
}
return false;
});
this.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) { }
@Override
public void afterTextChanged(Editable s) {
if (s.length() == 1 && getNextView() != null) {
getNextView().requestFocus();
} else if (s.length() == 0 && getPreviousView() != null) {
getPreviousView().requestFocus();
}
}
});
// Android 3rd party keyboards show the copied text into the suggestion box for the user.
// Users can then simply tap on that suggestion to paste the text on the edittext.
// But I don't know of any API that allows handling of those paste actions.
// Below code will try to tell those keyboards to stop showing those suggestion.
this.setInputType(EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS | EditorInfo.TYPE_CLASS_NUMBER);
}
private View getNextView() {
if (nextView != null) {
return nextView;
}
if (nextViewId != NO_ID && getParent() instanceof View) {
nextView = ((View) getParent()).findViewById(nextViewId);
return nextView;
}
return null;
}
private View getPreviousView() {
if (previousView != null) {
return previousView;
}
if (previousViewId != NO_ID && getParent() instanceof View) {
previousView = ((View) getParent()).findViewById(previousViewId);
return previousView;
}
return null;
}
}
要点还包括可以直接添加到活动中的xml和java代码。
答案 18 :(得分:0)
private class GenericTextWatcher implements TextWatcher {
private EditText currentView;
private EditText nextView;
private GenericTextWatcher(EditText currentView, EditText nextView) {
this.currentView = currentView;
this.nextView = nextView;
}
@Override
public void afterTextChanged(Editable editable) {
// TODO Auto-generated method stub
String text = editable.toString();
if (nextView != null && text.length() == 1) {
nextView.requestFocus();
}
if(text.length() >1){
currentView.setText(String.valueOf(text.charAt(text.length() - 1)));
currentView.setSelection(1);
}
}
@Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub
}
@Override
public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub
}
}
private class GenericKeyEvent implements View.OnKeyListener {
private EditText currentView;
private EditText previousView;
public GenericKeyEvent(EditText currentView, EditText previousView) {
this.currentView = currentView;
this.previousView = previousView;
}
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_DEL && currentView.getText().toString().isEmpty()) {
if (previousView != null) {
previousView.requestFocus();
}
return true;
}
return false;
}
}
private void attachTextWatchers() {
binding.editText1.addTextChangedListener(new GenericTextWatcher(binding.editText1, binding.editText2));
binding.editText2.addTextChangedListener(new GenericTextWatcher(binding.editText2, binding.editText3));
binding.editText3.addTextChangedListener(new GenericTextWatcher(binding.editText3, binding.editText4));
binding.editText4.addTextChangedListener(new GenericTextWatcher(binding.editText4, null));
binding.editText2.setOnKeyListener(new GenericKeyEvent(binding.editText2, binding.editText1));
binding.editText3.setOnKeyListener(new GenericKeyEvent(binding.editText3, binding.editText2));
binding.editText4.setOnKeyListener(new GenericKeyEvent(binding.editText4, binding.editText3));
}
答案 19 :(得分:0)
您可以继承TextWatcher并实现自己的逻辑。
public class OTPTextWatcher implements TextWatcher {
private EditText view;
private List<EditText> otpDigitViews;
private OTPListener otpListener;
public OTPTextWatcher(EditText otpView, List<EditText> otpDigitViews, OTPListener listener) {
view = otpView;
this.otpDigitViews = otpDigitViews;
this.otpListener = listener;
}
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
String digit1 = otpDigitViews.get(0).getText().toString();
String digit2 = otpDigitViews.get(1).getText().toString();
String digit3 = otpDigitViews.get(2).getText().toString();
String digit4 = otpDigitViews.get(3).getText().toString();
String currentDigit = editable.toString();
final String inputValue = digit1 + digit2 + digit3 + digit4;
if (inputValue.length() == 4) {
if (inputValue.equals("1234")) {
otpListener.onOTPSuccess();
} else {
otpListener.onOTPError();
}
} else {
if (currentDigit.length() >= 1
&& view != otpDigitViews.get(3)) {
if (view != null)
view.focusSearch(View.FOCUS_RIGHT).requestFocus();
} else {
if (currentDigit.length() <= 0 && view.getSelectionStart() <= 0) {
try {
view.focusSearch(View.FOCUS_LEFT).requestFocus();
} catch (NullPointerException e) {
LogHelper.printErrorLog("There is not view left to current edit text");
}
}
}
}
}
public interface OTPListener {
void onOTPSuccess();
void onOTPError();
}
}
答案 20 :(得分:0)
您可以制作自定义Editext并将其添加到xml文件中,在下面找到自定义类
Require Import Coq.Lists.List. Import ListNotations.
Require Import Coq.Sorting.Mergesort. Import NatSort.
Scheme Equality for nat.
Inductive Gen : Set :=
| BGen : nat -> nat -> Gen.
Definition g1 := BGen 1 2.
Definition g2 := BGen 2 3.
Fixpoint parents (l : list Gen) (c : nat) :=
match l with
| [] => []
| (BGen p c') :: l' => if nat_beq c c'
then [p]
else parents l' c
end.
Fixpoint deduplicate' (ls : list nat) :=
match ls with
| [] => []
| x :: [] => [x]
| x :: ((y :: ys) as xs)
=> if nat_beq x y
then deduplicate' xs
else x :: deduplicate' xs
end.
Definition deduplicate (ls : list nat) := deduplicate' (sort ls).
Definition parents_step (l : list Gen) (cs : list nat) :=
deduplicate (cs ++ List.flat_map (parents l) cs).
Fixpoint all_parents' (l : list Gen) (cs : list nat) (fuel : nat) :=
match fuel with
| 0 => cs
| S fuel'
=> all_parents' l (parents_step l cs) fuel'
end.
Definition all_parents (l : list Gen) (c : nat) :=
deduplicate (all_parents' l (parents l c) (List.length l)).
Definition gs := (g1::g2::nil).
Compute all_parents gs 3. (* [1; 2] *)
以下参考检查链接