这段时间在做新版的APP,在写登录页面的时候,发现了AS直接创建的LoginActivity自带的输入框效果体验还不错,在有输入时把hint的内容移到上方,无输入时隐藏掉,输入错误的时候右边提示错误信息,遗憾的是没有删除按钮,在实际开发当中,产品还是喜欢放上删除按钮的。总得来说,还是达不到理想的效果吧。然后就试着自己动手封装了一个。感觉效果还可以吧。(没有录制动态图,将就着看看吧~)
主要特点:
1.有输入时hint的内容在上方显示,无输入时与EditText
一样;
2.右侧有删除按钮,可设置显示/隐藏,默认显示;
3.出错是可以再java代码中控制显示右侧提示,当控件再次获取焦点重新输入时,错误提示隐藏。
4.重写了setOnFocusChangeListener
方法,可以监听时候获取光标;
5.添加addTextChangedListener
方法,可以监听输入内容;
6.具有原生EditText
的大部分功能,同时也提供了getInput
方法,返回 EditText
,可以进行更多的原生EditText
的设置。
java代码
package com.ms.text.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.sunwoda.cloudplatform.R;
/**
* ┏┓ ┏┓
* ┏┛┻━━━┛┻┓
* ┃ ┃
* ┃ ━ ┃
* ┃ ┳┛ ┗┳ ┃
* ┃ ┃
* ┃ ┻ ┃
* ┃ ┃
* ┗━┓ ┏━┛
* ┃ ┃ 神兽保佑
* ┃ ┃ 代码无BUG!
* ┃ ┗━━━┓
* ┃ ┣┓
* ┃ ┏┛
* ┗┓┓┏━┳┓┏┛
* ┃┫┫ ┃┫┫
* ┗┻┛ ┗┻┛
*/
/**
* @author ms
* on 2019/4/29
* Description:
*/
public class MsTextInputLayout extends RelativeLayout implements OnClickListener, TextWatcher, View.OnFocusChangeListener {
private static final String TAG = "MsTextInputLayout";
private static final int HINT_ID = 11111;
private static final int EDIT_ID = 22222;
private static final int DELETE_ID = 33333;
private static final int ERROR_ID = 4444;
/**
* 提示内容
*/
private String inputHint;
/**
* 提示字体颜色
*/
private int hintColor;
/**
* 提示字体大小
*/
private float hintSize = 14;
/**
* 左边图标
*/
private int drawable;
/**
* 图标padding
*/
private int drawablePadding = 5;
/**
* 输入内容
*/
private String inputContent;
/**
* 输入框Height
*/
private int inputHeight = 5;
/**
* 输入框padding
*/
private int inputPadding = 5;
/**
* 输入框paddingTop
*/
private int inputPaddingTop = 5;
/**
* 输入框paddingBottom
*/
private int inputPaddingBottom = 5;
/**
* 输入框paddingLeft
*/
private int inputPaddingLeft = 5;
/**
* 输入框paddingRight
*/
private int inputPaddingRight = 5;
/**
* 输入框背景
*/
private Drawable inputBackground;
/**
* 输入字体大小
*/
private float inputSize = 16;
/**
* 输入字体颜色
*/
private int inputColor;
/**
* 是否显示删除按钮
*/
private int delBtnVisibility;
/**
* 重新获取焦点时,清除已输入内容
*/
private boolean clearContentsFocus;
/**
* 错误内容
*/
private String error;
/**
* 输入类型
*/
private int inputType;
/**
* 当前对象
*/
private Context mContext;
/**
* 提示text
*/
private TextView hintView;
/**
* 错误text
*/
private TextView errorView;
/**
* 输入框
*/
private EditText editText;
/**
* 删除按钮
*/
private DeleteButton delBtn;
private TextWatcher textWatcher;
private OnFocusChangeListener focusChangeListener;
public MsTextInputLayout(Context context) {
super(context);
this.mContext = context;
initView();
}
public MsTextInputLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MsTextInputLayout);
inputHeight = typedArray.getLayoutDimension(R.styleable.MsTextInputLayout_inputHeight, LayoutParams.WRAP_CONTENT);
inputHint = typedArray.getString(R.styleable.MsTextInputLayout_inputHint) == null ? "请输入内容" : typedArray.getString(R.styleable.MsTextInputLayout_inputHint);
hintColor = typedArray.getColor(R.styleable.MsTextInputLayout_hintColor, Color.RED);
hintSize = px2sp(typedArray.getDimension(R.styleable.MsTextInputLayout_hintSize, sp2px(hintSize)));
drawable = typedArray.getResourceId(R.styleable.MsTextInputLayout_drawable, -1);
drawablePadding = typedArray.getDimensionPixelOffset(R.styleable.MsTextInputLayout_drawablePadding, -1);
inputContent = typedArray.getString(R.styleable.MsTextInputLayout_inputContent);
inputPadding = typedArray.getDimensionPixelOffset(R.styleable.MsTextInputLayout_inputPadding, -1);
inputPaddingTop = typedArray.getDimensionPixelOffset(R.styleable.MsTextInputLayout_inputPaddingTop, -1);
inputPaddingRight = typedArray.getDimensionPixelOffset(R.styleable.MsTextInputLayout_inputPaddingRight, -1);
inputPaddingBottom = typedArray.getDimensionPixelOffset(R.styleable.MsTextInputLayout_inputPaddingBottom, -1);
inputPaddingLeft = typedArray.getDimensionPixelOffset(R.styleable.MsTextInputLayout_inputPaddingLeft, -1);
inputBackground = typedArray.getDrawable(R.styleable.MsTextInputLayout_inputBackground);
inputSize = px2sp(typedArray.getDimension(R.styleable.MsTextInputLayout_inputSize, sp2px(inputSize)));
inputColor = typedArray.getColor(R.styleable.MsTextInputLayout_inputColor, Color.BLACK);
delBtnVisibility = typedArray.getInt(R.styleable.MsTextInputLayout_delBtnVisibility, 1);
clearContentsFocus = typedArray.getBoolean(R.styleable.MsTextInputLayout_clearContentsFocus, true);
inputType = typedArray.getInt(R.styleable.MsTextInputLayout_android_inputType, InputType.TYPE_CLASS_TEXT);
typedArray.recycle();
initView();
}
private void initView() {
hintView = new TextView(mContext);
hintView.setId(HINT_ID);
errorView = new TextView(mContext);
errorView.setId(ERROR_ID);
editText = new EditText(mContext);
editText.setId(EDIT_ID);
delBtn = new DeleteButton(mContext);
delBtn.setId(DELETE_ID);
delBtn.setOnClickListener(this);
RelativeLayout.LayoutParams hintViewParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
hintView.setText(inputHint);
addView(hintView, hintViewParams);
RelativeLayout.LayoutParams editTextParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, inputHeight);
editTextParams.addRule(RelativeLayout.BELOW, hintView.getId());
addView(editText, editTextParams);
RelativeLayout.LayoutParams delBtnParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
delBtnParams.addRule(RelativeLayout.ALIGN_BOTTOM, editText.getId());
delBtnParams.addRule(RelativeLayout.ALIGN_RIGHT, editText.getId());
delBtnParams.addRule(RelativeLayout.ALIGN_TOP, editText.getId());
delBtnParams.rightMargin = 10;
delBtnParams.leftMargin = 30;
delBtnParams.bottomMargin = 10;
delBtn.setPadding(20);
addView(delBtn, delBtnParams);
RelativeLayout.LayoutParams errorViewParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
errorViewParams.addRule(RelativeLayout.ALIGN_TOP, editText.getId());
errorViewParams.addRule(RelativeLayout.ALIGN_BOTTOM, editText.getId());
errorViewParams.addRule(RelativeLayout.LEFT_OF, delBtn.getId());
errorView.setText("错误的输入");
errorView.setTextColor(Color.RED);
errorView.setGravity(Gravity.CENTER_VERTICAL);
addView(errorView, errorViewParams);
initConfig();
}
/**
* 初始化配置
*/
void initConfig() {
errorView.setVisibility(GONE);
hintView.setVisibility(GONE);
hintView.setTextColor(hintColor);
hintView.setTextSize(hintSize);
hintView.setText(inputHint);
hintView.setPadding(10, 0, 0, 0);
if (drawable != -1) {
Drawable drawable = ContextCompat.getDrawable(mContext, this.drawable);
drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight());
editText.setCompoundDrawables(drawable, null, null, null);
editText.setCompoundDrawablePadding(drawablePadding);
}
if (inputPadding == -1) {
editText.setPadding(inputPaddingLeft > editText.getPaddingLeft() ? inputPaddingLeft : editText.getPaddingLeft(), inputPaddingTop > editText.getPaddingTop() ? inputPaddingTop : editText.getPaddingTop(), inputPaddingRight > editText.getPaddingRight() ? inputPaddingRight : editText.getPaddingRight(), inputPaddingBottom > editText.getPaddingBottom() ? inputPaddingBottom : editText.getPaddingBottom());
} else {
editText.setPadding(inputPadding > editText.getPaddingLeft() ? inputPadding : editText.getPaddingLeft(), inputPadding > editText.getPaddingTop() ? inputPadding : editText.getPaddingTop(), inputPadding > editText.getPaddingRight() ? inputPadding : editText.getPaddingRight(), inputPadding > editText.getPaddingBottom() ? inputPadding : editText.getPaddingBottom());
}
editText.setInputType(inputType);
editText.setTextColor(inputColor);
editText.setTextSize(inputSize);
editText.setHint(inputHint);
editText.setMaxLines(1);
editText.setSingleLine(true);
editText.addTextChangedListener(this);
if (inputBackground != null) {
editText.setBackground(inputBackground);
}
editText.setOnFocusChangeListener(this);
delBtn.setVisibility(delBtnVisibility);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//强制高度为WRAP_CONTENT,不然有输入的时候会出现显示不全的bug
getLayoutParams().height = LayoutParams.WRAP_CONTENT;
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
/**
* 输入监听
*
* @param textWatcher
*/
public void addTextChangedListener(TextWatcher textWatcher) {
this.textWatcher = textWatcher;
}
/**
* 设置输入框提示
*
* @param inputHint
*/
public void setInputHint(String inputHint) {
this.inputHint = inputHint;
hintView.setText(inputHint);
editText.setHint(inputHint);
}
/**
* 设置有输入时,输入框上方提示文字颜色
*
* @param hintColor
*/
public void setHintColor(int hintColor) {
this.hintColor = hintColor;
hintView.setTextColor(hintColor);
}
/**
* 设置有输入时,输入框上方提示文字大小 单位:sp
*
* @param hintSize
*/
public void setHintSize(float hintSize) {
this.hintSize = hintSize;
hintView.setTextSize(hintSize);
}
/**
* 输入框输入类型
* @param type
*/
public void setInputType(int type){
editText.setInputType(type);
}
/**
* 设置输入框左边图标
*
* @param id
*/
public void setDrawable(int id) {
this.drawable = id;
Drawable drawable = ContextCompat.getDrawable(mContext, this.drawable);
drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight());
editText.setCompoundDrawables(drawable, null, null, null);
}
/**
* 设置输入框左边图标 Padding
*
* @param drawablePadding
*/
public void setDrawablePadding(int drawablePadding) {
this.drawablePadding = drawablePadding;
editText.setCompoundDrawablePadding(drawablePadding);
}
/**
* 设置输入框内容
*
* @param inputContent
*/
public void setInputContent(String inputContent) {
this.inputContent = inputContent;
editText.setText(inputContent);
}
/**
* 获取输入框内容
*
* @return 输入框内容
*/
public String getInputContent() {
return editText.getText().toString().trim();
}
/**
* 设置输入框高度
*
* @param inputHeight
*/
public void setInputHeight(int inputHeight) {
this.inputHeight = inputHeight;
}
/**
* 设置输入框padding
*
* @param inputPadding
*/
public void setInputPadding(int inputPadding) {
this.inputPadding = inputPadding;
}
/**
* 设置输入框 inputPaddingTop
*
* @param inputPaddingTop
*/
public void setInputPaddingTop(int inputPaddingTop) {
this.inputPaddingTop = inputPaddingTop;
}
/**
* 设置输入框 inputPaddingBottom
*
* @param inputPaddingBottom
*/
public void setInputPaddingBottom(int inputPaddingBottom) {
this.inputPaddingBottom = inputPaddingBottom;
}
/**
* 设置输入框 inputPaddingLeft
*
* @param inputPaddingLeft
*/
public void setInputPaddingLeft(int inputPaddingLeft) {
this.inputPaddingLeft = inputPaddingLeft;
}
/**
* 设置输入框 inputPaddingRight
*
* @param inputPaddingRight
*/
public void setInputPaddingRight(int inputPaddingRight) {
this.inputPaddingRight = inputPaddingRight;
}
/**
* 设置输入框背景
*
* @param drawable
*/
public void setInputBackground(Drawable drawable) {
this.inputBackground = drawable;
editText.setBackground(inputBackground);
}
/**
* 设置输入框背景
*
* @param resId
*/
public void setInputBackgroundResource(int resId) {
editText.setBackgroundResource(resId);
}
/**
* 设置输入框背景
*
* @param color
*/
public void setInputBackgroundColor(int color) {
editText.setBackgroundColor(color);
}
/**
* 设置输入框字体大小
*
* @param inputSize
*/
public void setInputSize(float inputSize) {
this.inputSize = inputSize;
editText.setTextSize(inputSize);
}
/**
* 设置输入框字体颜色
*
* @param inputColor
*/
public void setInputColor(int inputColor) {
this.inputColor = inputColor;
editText.setTextColor(inputColor);
}
/**
* 设置删除按钮显示隐藏 默认显示
*
* @param delBtnVisibility
*/
public void setDelBtnVisibility(int delBtnVisibility) {
this.delBtnVisibility = delBtnVisibility;
delBtn.setVisibility(delBtnVisibility);
}
/**
* 设置输入错误时,再次获取光标是否清除内容
*
* @param clearContentsFocus
*/
public void setClearContentsFocus(boolean clearContentsFocus) {
this.clearContentsFocus = clearContentsFocus;
}
/**
* 设置输入有误信息
*
* @param err
*/
public void setError(String err) {
if (err.length() <= 0) {
return;
}
if (errorView.getVisibility() == GONE) {
errorView.setVisibility(VISIBLE);
}
errorView.setText(err);
}
/**
* 设置错误提示显示/隐藏
*
* @param visibility
*/
public void setErrorVisibility(int visibility) {
errorView.setVisibility(visibility);
}
/**
* 获取输入框,进行更多的原生editText的设置
* @return
*/
public EditText getInput(){
return editText;
}
@Override
public void setOnFocusChangeListener(OnFocusChangeListener listener) {
this.focusChangeListener = listener;
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case DELETE_ID:
editText.setText("");
break;
default:
break;
}
}
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if (textWatcher != null) {
textWatcher.beforeTextChanged(charSequence, i, i1, i2);
}
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if (textWatcher != null) {
textWatcher.onTextChanged(charSequence, i, i1, i2);
}
}
@Override
public void afterTextChanged(Editable editable) {
if (textWatcher != null) {
textWatcher.afterTextChanged(editable);
}
if (editable.length() > 0) {
hintView.setVisibility(VISIBLE);
if (errorView.getVisibility() == VISIBLE) {
errorView.setVisibility(GONE);
}
} else {
hintView.setVisibility(GONE);
}
}
@Override
public void onFocusChange(View view, boolean b) {
if (focusChangeListener != null) {
focusChangeListener.onFocusChange(view, b);
}
if (b) {
//获得焦点
if (clearContentsFocus && errorView.getVisibility() == VISIBLE) {
editText.setText("");
} else {
editText.setSelection(editText.getText().length());
}
} else {
//失去焦点
}
}
/**
* 删除按钮
*/
class DeleteButton extends View {
int padding = 10;
public DeleteButton(Context context) {
super(context);
}
@Override
public void setPadding(int left, int top, int right, int bottom) {
super.setPadding(left, top, right, bottom);
}
public void setPadding(int padding) {
this.padding = padding;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int diam = getWidth() > getHeight() ? getHeight() : getWidth();
int width = getWidth();
int height = getHeight();
int centreX = width / 2;
int centreY = height / 2;
int radius = diam / 2;
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.parseColor("#D1D6D9"));
canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius - padding, paint);
Paint paint1 = new Paint();
paint1.setAntiAlias(true);
paint1.setColor(Color.WHITE);
paint1.setStrokeWidth(12);
canvas.drawLine((float) (centreX - Math.sqrt(2) / 2 * radius + (radius + padding) / 3), (float) (centreY - Math.sqrt(2) / 2 * radius + (radius + padding) / 3), (float) (centreX + Math.sqrt(2) / 2 * radius - (radius + padding) / 3), (float) (centreY + Math.sqrt(2) / 2 * radius - (radius + padding) / 3), paint1);
canvas.drawLine((float) (centreX - Math.sqrt(2) / 2 * radius + (radius + padding) / 3), (float) (centreY + Math.sqrt(2) / 2 * radius - (radius + padding) / 3), (float) (centreX + Math.sqrt(2) / 2 * radius - (radius + padding) / 3), (float) (centreY - Math.sqrt(2) / 2 * radius + (radius + padding) / 3), paint1);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// get calculate mode of width and height
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
// get recommend width and height
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
// this view used in scrollView or listview or recyclerView
if (modeWidth == MeasureSpec.UNSPECIFIED) {
int wrap_width = 100 + getPaddingLeft() + getPaddingRight();
sizeWidth = wrap_width;
modeWidth = MeasureSpec.EXACTLY;
}
// this view used in scrollView or listview or recyclerView
if (modeHeight == MeasureSpec.UNSPECIFIED) {
int wrap_height = 100 + getPaddingTop() + getPaddingBottom();
sizeHeight = wrap_height;
modeHeight = MeasureSpec.EXACTLY;
}
// wrap_content
if (modeWidth == MeasureSpec.AT_MOST) {
int wrap_width = 100 + getPaddingLeft() + getPaddingRight();
sizeWidth = Math.min(wrap_width, sizeWidth);
modeWidth = MeasureSpec.EXACTLY;
}
// wrap_content
if (modeHeight == MeasureSpec.AT_MOST) {
int wrap_height = 100 + getPaddingTop() + getPaddingBottom();
sizeHeight = Math.min(wrap_height, sizeHeight);
modeHeight = MeasureSpec.EXACTLY;
}
widthMeasureSpec = MeasureSpec.makeMeasureSpec(sizeWidth, modeWidth);
heightMeasureSpec = MeasureSpec.makeMeasureSpec(sizeHeight, modeHeight);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
/**
* sp转px
*
* @param spVal
* @return
*/
public int sp2px(float spVal) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
spVal, mContext.getResources().getDisplayMetrics());
}
/**
* px转sp
*
* @param pxVal
* @return
*/
public float px2sp(float pxVal) {
return (pxVal / mContext.getResources().getDisplayMetrics().scaledDensity);
}
}
attrs代码
如果想在xml文件中使用editText原生的属性,可以自行添加,像<attr name="android:inputType"/>
一样,在java代码中调用editText相应的方法设置即可。
<declare-styleable name="MsTextInputLayout">
<attr name="hintColor" format="color"/>
<attr name="hintSize" format="dimension"/>
<attr name="drawable" format="reference"/>
<attr name="drawablePadding" format="dimension"/>
<attr name="inputHint" format="string"/>
<attr name="inputPaddingTop" format="dimension"/>
<attr name="inputPaddingBottom" format="dimension"/>
<attr name="inputPaddingLeft" format="dimension"/>
<attr name="inputPaddingRight" format="dimension"/>
<attr name="inputContent" format="string"/>
<attr name="inputPadding" format="dimension"/>
<attr name="inputBackground" format="reference|color|dimension"/>
<attr name="inputSize" format="dimension"/>
<attr name="inputColor" format="color"/>
<attr name="inputHeight" format="dimension"/>
<attr name="android:inputType"/>
<attr name="delBtnVisibility" >
<enum name="visible" value="1"/>
<enum name="gone" value="8"/>
</attr>
<attr name="clearContentsFocus" format="boolean"/>
</declare-styleable>
layout代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".LoginActivity">
<ScrollView
android:id="@+id/login_form"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/email_login_form"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.ms.text.view.MsTextInputLayout
android:id="@+id/input_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:hintColor="@color/colorAccent"
app:drawable="@mipmap/ic_launcher"
app:inputBackground="#009eda"
app:inputColor="@color/colorInput"
android:inputType="number"
app:inputHeight="48dp"
app:inputHint="请输入手机号"/>
<com.ms.text.view.MsTextInputLayout
android:id="@+id/input_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
app:hintColor="@color/colorAccent"
app:inputColor="@color/colorInput"
app:inputHeight="48dp"
app:inputHint="请输入密码"
app:inputPaddingLeft="10dp"
app:delBtnVisibility="gone"
app:inputPaddingRight="10dp" />
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:text="确定" />
</LinearLayout>
</ScrollView>
</LinearLayout>