1.自定义的验证码输入框效果:
1.做法是6个EditText(无光标),后来发现,这样写最大问题是无法满足粘贴功能,因为android版本10不支持粘贴功能,验证码短信一般都带“复制”,点击短信通知栏的“复制”后,6位验证码会自动显示在软键盘左上角,点击一下即完成填充
2.如果牺牲掉了验证码“通知栏短信——复制——点击填充”功能,用户必须一次性记住6位,一个一个输入;可能手机版本不一样,app无法自动填充,自己也没记验证码,再次下拉看通知栏看短信时,发现通知栏短信也没了,就必须要回到短信收件箱里查找,这种用户体验
实现功能:
1.点短信复制后,支持自动填充(显示在软键盘左上角)
2.有输入时就屏蔽长按粘贴
3.输入完成回调
4.输入方框的样式:大小、长宽…
5.屏蔽长按事件避免出现“剪切、复制、粘贴”的那个系统选项框
6.屏蔽双击事件,测试红米、华为等手机,双击和长按都会弹出“剪切,复制,粘贴”系统选项框
2.输入框的个数设置6个,1~6个都可以
inputNum = ta.getInteger(R.styleable.VerCodeInputView_inputNum, 6);//6表示6个输入框
3.验证码输入框的样式,selector_bg_edit.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="false">
<shape android:shape="rectangle">
<solid android:color="#FFFFFF" />
<stroke android:width="3dp" android:color="#C4C4C4" />
<corners android:radius="5dp" />
</shape>
</item>
<item android:state_selected="true">
<shape android:shape="rectangle">
<solid android:color="#ffffff" />
<stroke android:width="3dp" android:color="#028C54" />
<corners android:radius="5dp" />
</shape>
</item>
</selector>
4.设置把光标为true,光标是false的就无法长按粘贴
editText.setCursorVisible(true);
5.设置透明光标,如果直接不显示光标的话,长按粘贴会没效果
try {
Field f = TextView.class.getDeclaredField("mCursorDrawableRes");
f.setAccessible(true);
f.set(editText, R.drawable.edit_cursor_bg_transparent);
} catch (Exception e) {
e.printStackTrace();
}
edit_cursor_bg_transparent.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size android:width="0.01dp" />
<solid android:color="@android:color/transparent" />
</shape>
6.输入框,已经有输入时,屏蔽长按和光标
if (inputContent.length() > 0) {
editText.setLongClickable(false);
editText.setCursorVisible(false);
} else {
editText.setLongClickable(true);
editText.setCursorVisible(true);
}
7.自定义输入框,选中的框边的颜色随着要输入变绿(没输入),未选中(没输入、有输入)就变灰
for (int i = 0, len = textViewList.size(); i < len; i++) {
TextView textView = textViewList.get(i);
textView.setSelected(false);
if (i < inputContent.length()) {
textView.setText(String.valueOf(inputContent.charAt(i)));
} else {
textView.setText("");
//选中待输入的textView
if (i == inputContent.length()) {
textView.setSelected(true);
}
}
}
8.自定义的验证码输入框EditText,完整的VerCodeInputView.java:
/**
* Created by @author iblade.Wang on 2019/4/4.
* 验证码输入框
* EditText字号极小,且颜色透明
*/
public class VerCodeInputView extends FrameLayout {
public OnCompleteListener listener;
/**
* 输入框个数
*/
private int inputNum;
/**
* 输入框宽度
*/
private int inputWidth;
private int inputHeight;
/**
* 输入框之间的间隔
*/
private int childPadding;
/**
* 输入框背景
*/
private int editTextBg;
/**
* 文本颜色
*/
private int textColor;
/**
* 文本字体大小
*/
private int textSize;
//****************************
//****************************
/**
* 输入类型
*/
private int inputType;
private List<TextView> textViewList;
private EditText editText;
private TextWatcher textWatcher = 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 editable) {
String inputContent = (null == editText.getText()) ? "" : editText.getText().toString();
//已经有输入时,屏蔽长按和光标
if (inputContent.length() > 0) {
editText.setLongClickable(false);
editText.setCursorVisible(false);
} else {
editText.setLongClickable(true);
editText.setCursorVisible(true);
}
if (listener != null && inputContent.length() >= inputNum) {
listener.onComplete(inputContent);
}
for (int i = 0, len = textViewList.size(); i < len; i++) {
TextView textView = textViewList.get(i);
textView.setSelected(false);
if (i < inputContent.length()) {
textView.setText(String.valueOf(inputContent.charAt(i)));
} else {
textView.setText("");
//选中待输入的textView
if (i == inputContent.length()) {
textView.setSelected(true);
}
}
}
}
};
private boolean isAuto = false;
public VerCodeInputView(Context context) {
this(context, null);
}
public VerCodeInputView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public VerCodeInputView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.VerCodeInputView, defStyleAttr, 0);
inputNum = ta.getInteger(R.styleable.VerCodeInputView_inputNum, 6);
inputWidth = ta.getDimensionPixelSize(R.styleable.VerCodeInputView_inputWidth, DensityUtil.dip2px(context, 43));
inputHeight = inputWidth;
childPadding = ta.getDimensionPixelSize(R.styleable.VerCodeInputView_inputPadding, DensityUtil.dip2px(context, 9.5f));
textColor = ta.getColor(R.styleable.VerCodeInputView_inputTxtColor, Color.parseColor("#333333"));
textSize = ta.getDimensionPixelSize(R.styleable.VerCodeInputView_inputTxtSize, 24);
editTextBg = ta.getResourceId(R.styleable.VerCodeInputView_inputBg, R.drawable.selector_bg_edit);
inputType = ta.getInt(R.styleable.VerCodeInputView_inputType, InputType.TYPE_CLASS_NUMBER);
ta.recycle();
textViewList = new ArrayList<>(inputNum);
initViews();
}
private void initViews() {
textViewList.clear();
//textViewList = new ArrayList<>(inputNum);
LinearLayout llTextViewRoot = new LinearLayout(getContext());
LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
llTextViewRoot.setLayoutParams(layoutParams);
llTextViewRoot.setOrientation(LinearLayout.HORIZONTAL);
addView(llTextViewRoot);
for (int i = 0; i < inputNum; i++) {
TextView textView = new TextView(getContext());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(inputWidth, inputHeight);
if (i != inputNum - 1) {//最后一个textView 不设置margin
params.rightMargin = childPadding;
}
params.gravity = Gravity.CENTER;
textView.setLayoutParams(params);
textView.setTextColor(textColor);
textView.setTextSize(textSize);
textView.setGravity(Gravity.CENTER);
textView.setFilters(new InputFilter[]{new InputFilter.LengthFilter(1)});
textView.setInputType(inputType);
textView.setBackgroundResource(editTextBg);
if (i == 0) textView.setSelected(true);//默认首个方框选中
textView.setId(i);
llTextViewRoot.addView(textView);
textViewList.add(textView);
}
editText = new EditText(getContext());
editText.setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
return false;
}
});
LayoutParams layoutParam2 = new LayoutParams(LayoutParams.MATCH_PARENT, inputHeight);
editText.setLayoutParams(layoutParam2);
editText.setTextSize(0.01f);
//---
editText.setCursorVisible(true);
//设置透明光标,如果直接不显示光标的话,长按粘贴会没效果
try {
Field f = TextView.class.getDeclaredField("mCursorDrawableRes");
f.setAccessible(true);
f.set(editText, R.drawable.edit_cursor_bg_transparent);
} catch (Exception e) {
e.printStackTrace();
}
editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(inputNum)});
editText.setInputType(inputType);
editText.setTextColor(ContextCompat.getColor(getContext(), R.color.transparent));
editText.setBackground(null);
editText.addTextChangedListener(textWatcher);
addView(editText);
initListener();
}
private void initListener() {
//屏蔽双击: 好多手机双击会出现 选择 剪切 粘贴 的选项卡,
new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDoubleTap(MotionEvent e) {
return true;
}
});
}
/**
* 设置宽高自适应,单个框的宽度平分父布局总宽度
*/
public void setAutoWidth() {
isAuto = true;
requestLayout();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
if (isAuto && width > 0) {
isAuto = false;
//resetWH(width);
resetMargin(width);
}
}
/* private void resetWH(int w) {
int paddings = childPadding * (inputNum - 1);
inputWidth = (w - paddings) / (inputNum);
inputHeight = inputWidth;
for (int i = 0, len = textViewList.size(); i < len; i++) {
View child = textViewList.get(i);
child.getLayoutParams().height = inputHeight;
child.getLayoutParams().width = inputWidth;
}
editText.getLayoutParams().height = inputHeight;
}*/
private void resetMargin(int width) {
if (width > 0) {
int remainWidth = width - (inputNum * inputWidth);
if (remainWidth > 0 && inputNum > 1) {
childPadding = remainWidth / (inputNum - 1);
for (int i = 0, len = textViewList.size(); i < len; i++) {
View child = textViewList.get(i);
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) child.getLayoutParams();
if (i != inputNum - 1) {//最后一个textView 不设置margin
params.rightMargin = childPadding;
}
params.gravity = Gravity.CENTER;
child.setLayoutParams(params);
child.getLayoutParams().height = inputHeight;
child.getLayoutParams().width = inputWidth;
}
editText.getLayoutParams().height = inputHeight;
}
}
}
/**
* 获取编辑框内容
*
* @return 编辑框内容
*/
public String getEditContent() {
return editText.getText().toString();
}
public void setOnCompleteListener(OnCompleteListener listener) {
this.listener = listener;
}
public interface OnCompleteListener {
/**
* 完成验证码的填写
*
* @param content 填写内容
*/
void onComplete(String content);
}
}
9.如何调用MainActivity.java:
public class MainActivity extends AppCompatActivity{
private VerCodeInputView codeInputCard;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
codeInputCard = (VerCodeInputView) findViewById(R.id.edit);
codeInputCard.setAutoWidth();
codeInputCard.setOnCompleteListener(new VerCodeInputView.OnCompleteListener() {
@Override
public void onComplete(String content) {
Toast.makeText(MainActivity.this, "您输入了:" + content, Toast.LENGTH_LONG).show();
}
});
}
}
10.activity_main.xml布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<com.lwz.conn.view.VerCodeInputView
android:id="@+id/edit"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
11.在res的values目录下创建attrs.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="VerCodeInputView">
<!--输入框个数-->
<attr name="inputNum" format="integer" />
<!--输入框宽度-->
<attr name="inputWidth" format="dimension" />
<!--输入框内边框大小-->
<attr name="inputPadding" format="dimension" />
<!--文本颜色-->
<attr name="inputTxtColor" format="color" />
<!--文本字体大小-->
<attr name="inputTxtSize" format="integer" />
<!--输入框背景-->
<attr name="inputBg" format="reference" />
<!--输入类型-->
<attr name="inputType" format="string" />
</declare-styleable>
</resources>
12.封装工具类DensityUtil代码直接复制就即可,点击链接:Android 封装工具类DensityUtil(dp转px、px转dp)