1. 自定义手机验证码
自定义手机验证码,效果如下
很明显,一般的EditText
是无法满足我们的需求,我们必须自定义一个手机验证码控件。
2. MobileVerifyItemView控件
在MobileVerifyView
里,我们将输入和显示分开
1、用EditText
来接受键盘的输入
2、用MobileVerifyItemView
来显示
public class MobileVerifyView extends RelativeLayout implements View.OnClickListener {
private EditText etInput;
private List<MobileVerifyItemView> itemViewList = new ArrayList();
private MobileTextWatcher textWatcher;
public MobileVerifyView(Context context) {
this(context, null);
}
public MobileVerifyView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MobileVerifyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
inflate(context, R.layout.view_mobile_verify, this);
etInput = findViewById(R.id.et_input);
itemViewList.add((MobileVerifyItemView) findViewById(R.id.item_view1));
itemViewList.add((MobileVerifyItemView) findViewById(R.id.item_view2));
itemViewList.add((MobileVerifyItemView) findViewById(R.id.item_view3));
itemViewList.add((MobileVerifyItemView) findViewById(R.id.item_view4));
// 监听EditText的文本变化,修改显示文本和光标位置
etInput.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) {
setSelection();
if (textWatcher != null) {
textWatcher.onTextChanged(etInput.getText().toString());
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
for (MobileVerifyItemView itemView : itemViewList) {
itemView.setOnClickListener(this);
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
setSelection();
showSoftKeyBoard();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
for (MobileVerifyItemView itemView : itemViewList) {
itemView.setFocus(false);
}
}
// 根据输入文本显示,并移动光标位置
private void setSelection() {
String text = etInput.getText().toString();
int len = etInput.length();
for (int index = 0; index < itemViewList.size(); index++) {
if (index < len) {
itemViewList.get(index).setText(text.substring(index, index+1));
} else {
itemViewList.get(index).setText("");
}
itemViewList.get(index).setFocus(false);
}
if (len < 4) {
itemViewList.get(len).setFocus(true);
} else {
itemViewList.get(3).setFocus(true);
}
}
@Override
public void onClick(View v) {
if ((v.getId() == R.id.item_view1)
|| (v.getId() == R.id.item_view2)
|| (v.getId() == R.id.item_view3)
||(v.getId() == R.id.item_view4)) {
showSoftKeyBoard();
}
}
// 显示软键盘
private void showSoftKeyBoard() {
final InputMethodManager manager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
postDelayed(new Runnable() {
@Override
public void run() {
manager.showSoftInput(etInput, 0);
}
}, 300);
}
// 添加文本监听器
public void setTextWatcher(MobileTextWatcher textWatcher) {
this.textWatcher = textWatcher;
}
public interface MobileTextWatcher {
void onTextChanged(String text);
}
}
view_mobile_verify.xml
文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/et_input"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:maxLength="4"
android:inputType="number"
android:digits="0123456789" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="horizontal">
<com.blog.demo.custom.widget.MobileVerifyItemView
android:id="@+id/item_view1"
android:layout_width="40dp"
android:layout_height="match_parent" />
<RelativeLayout
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent">
<com.blog.demo.custom.widget.MobileVerifyItemView
android:id="@+id/item_view2"
android:layout_width="40dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent">
<com.blog.demo.custom.widget.MobileVerifyItemView
android:id="@+id/item_view3"
android:layout_width="40dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent">
<com.blog.demo.custom.widget.MobileVerifyItemView
android:id="@+id/item_view4"
android:layout_width="40dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"/>
</RelativeLayout>
</LinearLayout>
</RelativeLayout>
3. MobileVerifyItemView控件
在MobileVerifyItemView
里面,包含一个tvNumber
显示验证码,cursorView
模拟光标显示,indicatorView
表示下面的显示条。
public class MobileVerifyItemView extends RelativeLayout {
private TextView tvNumber;
private View cursorView;
private View indicatorView;
private PropertyValuesHolder alphaHolder;
private ObjectAnimator alphaAnimator;
public MobileVerifyItemView(Context context) {
this(context, null);
}
public MobileVerifyItemView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MobileVerifyItemView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
inflate(context, R.layout.item_view_mobile_verify, this);
tvNumber = findViewById(R.id.tv_number);
cursorView = findViewById(R.id.view_cursor);
indicatorView = findViewById(R.id.view_indicator);
Keyframe keyframe1 = Keyframe.ofFloat(0, 1);
Keyframe keyframe2 = Keyframe.ofFloat(0.4f, 1);
Keyframe keyframe3 = Keyframe.ofFloat(0.45f, 0);
Keyframe keyframe4 = Keyframe.ofFloat(1, 0);
alphaHolder = PropertyValuesHolder.ofKeyframe("alpha", keyframe1, keyframe2, keyframe3, keyframe4);
}
public void setText(String text) {
tvNumber.setText(text);
}
public void setFocus(boolean focus) {
// 获取焦点,显示光标
if (focus) {
cursorView.setVisibility(VISIBLE);
if (alphaAnimator != null) {
alphaAnimator.cancel();
}
alphaAnimator = ObjectAnimator.ofPropertyValuesHolder(cursorView, alphaHolder);
alphaAnimator.setDuration(1500);
alphaAnimator.setRepeatCount(-1);
alphaAnimator.start();
} else {
cursorView.setVisibility(View.INVISIBLE);
if (alphaAnimator != null) {
alphaAnimator.cancel();
alphaAnimator = null;
}
}
indicatorView.setSelected(focus);
}
}
item_view_mobile_verify.xml
文件,background_cursor.xml
和background_indicator.xml
是光标和显示条背景
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="25sp"
android:textColor="@color/black"
android:textStyle="bold"
android:layout_centerInParent="true"/>
<View
android:id="@+id/view_cursor"
android:layout_width="2dp"
android:layout_height="25dp"
android:layout_toRightOf="@id/tv_number"
android:layout_centerVertical="true"
android:background="@drawable/background_cursor" />
<View
android:id="@+id/view_indicator"
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="@drawable/background_indicator"
android:layout_alignParentBottom="true"/>
</RelativeLayout>