Android仿滴滴出行驗證碼輸入框效果

hydCoder發表於2019-03-04

1、前言

最近擼碼忙成狗啊,果然從無到有的獨立開發不是一般的累啊。。。。
最近公司專案中有一個類似滴滴出行填寫驗證碼的彈框,下面是我擼出來的效果:

輸入驗證碼.gif

中間的那個輸入密碼的6個框框其實就是用shape畫的背景,通過監聽EditText獲取焦點來改變背景,廢話少說,直接上程式碼吧。

2、效果實現

程式碼內容比較簡單,所以大家可以直接看程式碼

VerificationCodeInput.java

  /**
   * @author hydCoder
   * @date 2017/9/22 14:39
   * @desc 輸入驗證碼的自定義view
   * @email hyd_coder@163.com
   */

public class VerificationCodeInput extends LinearLayout implements TextWatcher, View.OnKeyListener{

private final static String TYPE_NUMBER = "number";
private final static String TYPE_TEXT = "text";
private final static String TYPE_PASSWORD = "password";
private final static String TYPE_PHONE = "phone";

private static final String   TAG           = "VerificationCodeInput";
private              int      box           = 4;
private              int      boxWidth      = 80;
private              int      boxHeight     = 80;
private              int      childHPadding = 14;
private              int      childVPadding = 14;
private              String   inputType     = TYPE_NUMBER;
private              Drawable boxBgFocus    = null;
private              Drawable boxBgNormal   = null;
private Listener listener;
private boolean        focus           = false;
private List<EditText> mEditTextList   = new ArrayList<>();
private int            currentPosition = 0;

public VerificationCodeInput(Context context, AttributeSet attrs) {
    super(context, attrs);
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.vericationCodeInput);
    box = a.getInt(R.styleable.vericationCodeInput_box, 4);

    childHPadding = (int) a.getDimension(R.styleable.vericationCodeInput_child_h_padding, 0);
    childVPadding = (int) a.getDimension(R.styleable.vericationCodeInput_child_v_padding, 0);
    boxBgFocus =  a.getDrawable(R.styleable.vericationCodeInput_box_bg_focus);
    boxBgNormal = a.getDrawable(R.styleable.vericationCodeInput_box_bg_normal);
    inputType = a.getString(R.styleable.vericationCodeInput_inputType);
    boxWidth = (int) a.getDimension(R.styleable.vericationCodeInput_child_width, boxWidth);
    boxHeight = (int) a.getDimension(R.styleable.vericationCodeInput_child_height, boxHeight);
    initViews();

}


@Override
protected void onAttachedToWindow() {
    super.onAttachedToWindow();

}

@Override
protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();


}

private void initViews() {
    for (int i = 0; i < box; i++) {
        EditText editText = new EditText(getContext());
        LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(boxWidth, boxHeight);
        layoutParams.bottomMargin = childVPadding;
        layoutParams.topMargin = childVPadding;
        layoutParams.leftMargin = childHPadding;
        layoutParams.rightMargin = childHPadding;
        layoutParams.gravity = Gravity.CENTER;


        editText.setOnKeyListener(this);
        if(i == 0)
            setBg(editText, true);
        else setBg(editText, false);
        editText.setTextColor(Color.BLACK);
        editText.setLayoutParams(layoutParams);
        editText.setGravity(Gravity.CENTER);
        editText.setInputType(EditorInfo.TYPE_CLASS_PHONE);
        editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(1)});

        if (TYPE_NUMBER.equals(inputType)) {
            editText.setInputType(InputType.TYPE_CLASS_NUMBER);
        } else if (TYPE_PASSWORD.equals(inputType)){
            editText.setTransformationMethod(PasswordTransformationMethod.getInstance());
        } else if (TYPE_TEXT.equals(inputType)){
            editText.setInputType(InputType.TYPE_CLASS_TEXT);
        } else if (TYPE_PHONE.equals(inputType)){
            editText.setInputType(InputType.TYPE_CLASS_PHONE);

        }
        editText.setId(i);
        editText.setEms(1);
        editText.addTextChangedListener(this);
        addView(editText,i);
        mEditTextList.add(editText);

    }


}

private void backFocus() {
    int count = getChildCount();
    EditText editText ;
    for (int i = count-1; i>= 0; i--) {
        editText = (EditText) getChildAt(i);
        if (editText.getText().length() == 1) {
            editText.requestFocus();
            setBg(mEditTextList.get(i),true);
            //setBg(mEditTextList.get(i-1),true);
            editText.setSelection(1);
            return;
        }
    }
}

private void focus() {
    int count = getChildCount();
    EditText editText ;
    for (int i = 0; i< count; i++) {
        editText = (EditText) getChildAt(i);
        if (editText.getText().length() < 1) {
            editText.requestFocus();
            return;
        }
    }
}

private void setBg(EditText editText, boolean focus) {
    if (boxBgNormal != null && !focus) {
        editText.setBackground(boxBgNormal);
    } else if (boxBgFocus != null && focus) {
        editText.setBackground(boxBgFocus);
    }
}

private void setBg(){
    int count = getChildCount();
    EditText editText ;
    for(int i = 0; i< count; i++){
        editText = (EditText) getChildAt(i);
        if (boxBgNormal != null && !focus) {
            editText.setBackground(boxBgNormal);
        } else if (boxBgFocus != null && focus) {
            editText.setBackground(boxBgFocus);
        }
    }

}
private void checkAndCommit() {
    StringBuilder stringBuilder = new StringBuilder();
    boolean full = true;
    for (int i = 0 ;i < box; i++){
        EditText editText = (EditText) getChildAt(i);
        String content = editText.getText().toString();
        if ( content.length() == 0) {
            full = false;
            break;
        } else {
            stringBuilder.append(content);
        }

    }
    if (full){
        if (listener != null) {
            listener.onComplete(stringBuilder.toString());
            setEnabled(false);
        }

    }
}

@Override
public void setEnabled(boolean enabled) {
    int childCount = getChildCount();
    for (int i = 0; i < childCount; i++) {
        View child = getChildAt(i);
        child.setEnabled(enabled);
    }
}

public void setOnCompleteListener(Listener listener){
    this.listener = listener;
}

@Override

public LayoutParams generateLayoutParams(AttributeSet attrs) {
    return new LinearLayout.LayoutParams(getContext(), attrs);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int count = getChildCount();

    for (int i = 0; i < count; i++) {
        View child = getChildAt(i);
        this.measureChild(child, widthMeasureSpec, heightMeasureSpec);
    }
    if (count > 0) {
        View child = getChildAt(0);
        int cHeight = child.getMeasuredHeight();
        int cWidth = child.getMeasuredWidth();
        int maxH = cHeight + 2 * childVPadding;
        int maxW = (cWidth + childHPadding) * box + childHPadding;
        setMeasuredDimension(resolveSize(maxW, widthMeasureSpec),
                resolveSize(maxH, heightMeasureSpec));
    }

}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    int childCount = getChildCount();
    for (int i = 0; i < childCount; i++) {
        View child = getChildAt(i);

        child.setVisibility(View.VISIBLE);
        int cWidth = child.getMeasuredWidth();
        int cHeight = child.getMeasuredHeight();
        int cl =  (i) * (cWidth + childHPadding);
        int cr = cl + cWidth;
        int ct = childVPadding;
        int cb = ct + cHeight;
        child.layout(cl, ct, cr, cb);
    }


}

@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {

}

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
    if (start == 0 && count >= 1 && currentPosition != mEditTextList.size() - 1) {
        currentPosition++;
        mEditTextList.get(currentPosition).requestFocus();
        setBg(mEditTextList.get(currentPosition),true);
        setBg(mEditTextList.get(currentPosition-1),false);
    }

}

@Override
public void afterTextChanged(Editable s) {
    if (s.length() == 0) {
    } else {
        focus();
        checkAndCommit();
    }
}

@Override
public boolean onKey(View view, int keyCode, KeyEvent event) {
    EditText editText = (EditText) view;
    if (keyCode == KeyEvent.KEYCODE_DEL && editText.getText().length() == 0) {
        int action = event.getAction();
        if (currentPosition != 0 && action == KeyEvent.ACTION_DOWN) {
            currentPosition--;
            mEditTextList.get(currentPosition).requestFocus();
            setBg(mEditTextList.get(currentPosition),true);
            setBg(mEditTextList.get(currentPosition+1),false);
            mEditTextList.get(currentPosition).setText("");
        }
    }
    return false;
}

public interface Listener {
    void onComplete(String content);
}
複製程式碼

}
···
styles.xml裡新增自定義屬性

<declare-styleable name="vericationCodeInput">

    <attr name="box" format="integer" />
    <attr name="child_h_padding" format="dimension"/>
    <attr name="child_v_padding" format="dimension"/>
    <attr name="child_width" format="dimension"/>
    <attr name="child_height" format="dimension"/>
    <attr name="padding" format="dimension"/>
    <attr name="box_bg_focus" format="reference"/>
    <attr name="box_bg_normal" format="reference"/>
    <attr name="inputType" format="string"/>
</declare-styleable>
複製程式碼

輸入框獲取焦點時的背景
verification_edit_bg_focus.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FFFFFF" />
<corners android:radius="8dip" />
<stroke
    android:width="2dip"
    android:color="@color/auxiliary_color" />
</shape>
複製程式碼

輸入框沒有獲取焦點時的背景
verification_edit_bg_normal.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@color/white" />
    <corners android:radius="8dip" />
    <stroke
        android:width="1dip"
        android:color="@color/divide_color"/>
</shape>
複製程式碼

在介面中使用

<com.sdalolo.genius.ui.view.VerificationCodeInput
    android:digits="1234567890"
    android:id="@+id/verificationCodeInput"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="25dp"
    android:layout_gravity="center_horizontal"
    ver:box="6"
    ver:box_bg_normal="@drawable/verification_edit_bg_normal"
    ver:box_bg_focus="@drawable/verification_edit_bg_focus"
    ver:child_h_padding="5dp"
    android:layout_centerInParent="true"
    android:layout_marginBottom="16dp"/>
複製程式碼

然後對它設定輸入完成後的監聽

verificationCodeInput.setOnCompleteListener(new VerificationCodeInput.Listener() {
        @Override
        public void onComplete(String content) {
            btn_confirm.setEnabled(true);
            btn_confirm.setBackgroundResource(R.drawable.btn_bg_shape_enable);
            btn_confirm.setTextColor(Color.parseColor("#e4c16a"));
            codeNum = content;
        }
    });
複製程式碼

到這裡就可以完成和滴滴出行類似的效果了,是不是很簡單,如果你剛好有需要,直接拷過去用吧!

謝謝

相關文章