一.使用EditText實現(xiàn)TextInputLayout
1.1效果

MaterialEditText.gif
1.2 talk is cheap
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.v7.widget.AppCompatEditText;
import android.text.Editable;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import com.example.rookie.hencoder_plus.R;
import com.example.rookie.hencoder_plus.custom_view_06.Utils;
/**
* 帶有提示的EditText
* 類似于design包中的TextInputLayout
* 1.加大paddingTop(文字高度+間距)用來擺放hintText
* 2.文字變化(有->無,無->有)時,增加動畫效果
* 3.增加xml自定義屬性以及java代碼的開關(guān)
*/
public class MaterialEditText extends AppCompatEditText {
private static final float HINT_TEXT_ANIMATION_OFFSET = Utils.dp2px(25);//動畫距離
private static final float EXTRA_PADDING_TOP_DISTANCE = Utils.dp2px(12);//文字高度
private static final float EXTRA_MARGIN_TOP_DISTANCE = Utils.dp2px(10);//hint文字距editText高度
private static final float EXTRA_VERTICAL_OFFSET_ORIGINAL = EXTRA_PADDING_TOP_DISTANCE + EXTRA_MARGIN_TOP_DISTANCE;
private static final float EXTRA_HORIZONTAL_OFFSET = Utils.dp2px(4);//橫向偏移,為了與EditText的content對齊
TextPaint hintTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
private String hintText = getHint().toString();
private ObjectAnimator showHintUpAnimator, disappearHintDownAnimator;
private boolean hintHasShown;//hintText是否已經(jīng)顯示
private boolean shouldShowTopHint = true;//是否需要顯示上面的hint(給外部開發(fā)者調(diào)用)
private TextWatcher watcher;
public MaterialEditText(Context context) {
this(context, null);
}
public MaterialEditText(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MaterialEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MaterialEditText);
shouldShowTopHint = typedArray.getBoolean(R.styleable.MaterialEditText_shouldShowTopHint, true);
typedArray.recycle();
init();
}
private void init() {
initTextPaint();
initPadding();
initTextWatcher();
}
private void initTextWatcher() {
if (!shouldShowTopHint) {
removeTextChangedListener(watcher);
return;
}
if (watcher == null) {
watcher = 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) {
if (hintHasShown && TextUtils.isEmpty(s)) {
//如果已經(jīng)顯示并且EditText已經(jīng)清空
hintHasShown = false;
getAnimatorToDisappearDownHint().start();
} else if (!hintHasShown && !TextUtils.isEmpty(s.toString())) {
//上方的hint還未顯示,并且EditText的文字非空
hintHasShown = true;
getAnimatorToShowUpHint().start();
}
}
@Override
public void afterTextChanged(Editable s) {
}
};
Log.e("test", "看看加了幾次");
addTextChangedListener(watcher);
}
}
ObjectAnimator getAnimatorToShowUpHint() {
if (showHintUpAnimator == null) {
showHintUpAnimator = ObjectAnimator.ofFloat(this, "hintFraction", 0, 1);
showHintUpAnimator.setDuration(1000);
}
return showHintUpAnimator;
}
ObjectAnimator getAnimatorToDisappearDownHint() {
if (disappearHintDownAnimator == null) {
disappearHintDownAnimator = ObjectAnimator.ofFloat(this, "hintFraction", 1, 0);
disappearHintDownAnimator.setDuration(1000);
}
return disappearHintDownAnimator;
}
private void initPadding() {
//增大paddingtop,為hinttext騰地方
int paddingTop = shouldShowTopHint ? (int) (getPaddingTop() + EXTRA_VERTICAL_OFFSET_ORIGINAL) : (int) (getPaddingTop() - EXTRA_VERTICAL_OFFSET_ORIGINAL);
setPadding(getPaddingLeft(), paddingTop, getPaddingRight(), getPaddingBottom());
}
private void initTextPaint() {
hintTextPaint.setColor(Color.BLACK);
hintTextPaint.setTextSize(EXTRA_PADDING_TOP_DISTANCE);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//通過動畫的fraction配合paint的alpha來實現(xiàn)文字的顯示和消失
hintTextPaint.setAlpha((int) (0xff * hintFraction));
canvas.drawText(hintText, EXTRA_HORIZONTAL_OFFSET, EXTRA_PADDING_TOP_DISTANCE + HINT_TEXT_ANIMATION_OFFSET * (1 - hintFraction), hintTextPaint);
}
/* 屬性動畫使用start */
private float hintFraction;
public float getHintFraction() {
return hintFraction;
}
/**
* dont call this method from outside
*
* @param hintFraction
*/
public void setHintFraction(float hintFraction) {
this.hintFraction = hintFraction;
invalidate();
}
/* 屬性動畫使用end */
public void setShouldShowTopHint(boolean shouldShowTopHint) {
if (this.shouldShowTopHint != shouldShowTopHint) {
//如果當(dāng)前顯示的flag和傳入的flag不同時,才修改狀態(tài)
//1。修改padding
if (this.shouldShowTopHint) {
//原來顯示,變?yōu)椴伙@示,則去掉padding
} else {
//原來不顯示,變?yōu)轱@示,加上padding
}
this.shouldShowTopHint = shouldShowTopHint;
initPadding();
initTextWatcher();
}
}
}