前言

从5.0(API等级21)开始,android开始支持矢量图了。利用矢量动画可以实现一些很酷炫的效果。
前阵子有个需求要实现一个酷炫输入框,利用矢量动画完美解决。

思路:画个路径,然后是加个分开和合并动画
向量动画结合TextInputLayout封装成一个输入框组件
Android 官网提示利用 AnimatedVectorDrawableCompat类兼容 Android 3.0(API 级别 11)及更高版本的

效果如下:

一、画个正常圆角输入框背景路径,及合并分开路径

1.1画个正常圆角输入框背景路径

<vector xmlns:android="http://schemas.android.com/apk/res/android"android:width="90dp"android:height="12dp"android:viewportWidth="90.0"android:viewportHeight="14.0"><path    android:strokeColor="@color/login_input_normal"    android:pathData="        M60,1        h23,0        q6,0 6,6        q0,6 -6,6        h-76        q-6,0 -6,-6        q0,-6,6,-6        h54,0        " />vector>

效果如下

1.2在drawable下建个xml文件,画个圆角带缺口的输入框背景图路径,这里起名login_input_vector_anim_drawable.xml

    

效果如下:

1.3.然后在上面的文件中加入向右伸缩的路径

1.4.在加上向左伸缩的路径

二、合并属性动画

图标基本画完了,下面加个属性动画,让它有伸缩效果。
新建个xml命名login_input_merge_anim.xml

<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android" >    <objectAnimator        android:duration="500"        android:propertyName="trimPathStart"        android:valueFrom="1"        android:valueTo="0" />set>
三、分开属性动画

在画个反过来的,让路径反过来伸缩
新建个xml命名login_input_merge_anim.xml

<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android" >    <objectAnimator        android:duration="500"        android:propertyName="trimPathStart"        android:valueFrom="0"        android:valueTo="1" />set>
四、合并的向量动画

(基于向量图标和属性动画)login_input_vector_merge_anim.xml

<?xml version="1.0" encoding="utf-8"?><animated-vector xmlns:android="http://schemas.android.com/apk/res/android"android:drawable="@drawable/login_input_vector_anim_drawable"><target    android:name="left"    android:animation="@anim/login_input_merge_anim" /><target    android:name="right"    android:animation="@anim/login_input_merge_anim" />animated-vector>
五、分开向量动画

(基于向量图标和属性动画)login_input_vector_split_anim.xml

<?xml version="1.0" encoding="utf-8"?><animated-vector xmlns:android="http://schemas.android.com/apk/res/android"    android:drawable="@drawable/login_input_vector_anim_drawable">    <target        android:name="left"        android:animation="@anim/login_input_split_anim" />    <target        android:name="right"        android:animation="@anim/login_input_split_anim" />animated-vector>
六、自定义输入框组件,封装向量动画使用

思路:封装一个自定义输入框组件,结合TextInputLayout和上面的向量动画达到,失去焦点,执行合并动画,提示下滑到中间并放大。获取焦点,没有输入内容,执行分开动画,提示上滑变小。

6.1 下面就可以代码中使用了,布局文件view_anim_edit_text.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    xmlns:app="http://schemas.android.com/apk/res-auto">    <android.support.design.widget.TextInputLayout        android:id="@+id/et_container"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:hint=""        app:hintTextAppearance="@style/HintTextAppearance"        app:hintAnimationEnabled="true">            <android.support.design.widget.TextInputEditText                android:id="@+id/et"                android:layout_width="match_parent"                android:layout_height="36dip"                android:background="@null"                android:gravity="center"                android:text=""                android:textSize="15sp"                android:paddingBottom="10dip"                android:imeOptions="actionDone"                android:inputType="textCapCharacters|textPhonetic"                android:maxLength="100" />    android.support.design.widget.TextInputLayout>LinearLayout>

6.2自定义组件,其中使用了Rxjava2.0,要首先在项目中引用插件

 compile 'com.android.support:appcompat-v7:25.4.0'    compile 'com.android.support:design:25.4.0'    compile 'com.android.support.constraint:constraint-layout:1.0.2'    compile 'io.reactivex.rxjava2:rxandroid:2.0.1'

6.3下面是本列子使用的相关代码库

package aimissu.com.animationinputbox;import android.annotation.SuppressLint;import android.annotation.TargetApi;import android.content.Context;import android.content.res.TypedArray;import android.graphics.drawable.Animatable;import android.graphics.drawable.AnimatedVectorDrawable;import android.graphics.drawable.Drawable;import android.os.Build;import android.support.annotation.NonNull;import android.support.annotation.Nullable;import android.support.annotation.RequiresApi;import android.support.design.widget.TextInputEditText;import android.support.design.widget.TextInputLayout;import android.support.graphics.drawable.AnimatedVectorDrawableCompat;import android.support.graphics.drawable.VectorDrawableCompat;import android.support.v4.content.ContextCompat;import android.support.v7.app.AppCompatDelegate;import android.text.Editable;import android.text.InputFilter;import android.text.InputType;import android.text.TextUtils;import android.text.TextWatcher;import android.util.AttributeSet;import android.util.TypedValue;import android.view.LayoutInflater;import android.view.View;import android.widget.LinearLayout;import java.util.concurrent.TimeUnit;import io.reactivex.Flowable;import io.reactivex.android.schedulers.AndroidSchedulers;import io.reactivex.functions.Consumer;/** * author:dz-hexiang on 2017/10/30. * email:472482006@qq.com * 向量动画输入框 */public  class AnimEditText extends LinearLayout {    private TextInputEditText mEditText;    private TextInputLayout mEditTextContainer;    private AnimatedVectorDrawableCompat mSplitAnim;    private AnimatedVectorDrawableCompat mMergeAnim;    private VectorDrawableCompat noAnimBg;    private String mHit;    private float mHitSize;    private int mHitColor;    private  String mText;    private float mTextSize;    private int mTextColor;    private boolean mIsPwd;    private int mMaxLength;    private boolean mIsNumber;    public AnimEditText(Context context) {        super(context);       initView(context,null,-1);    }    public AnimEditText(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);        initView(context, attrs,-1);    }    public AnimEditText(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        initView(context, attrs,defStyleAttr);    }    @SuppressLint("NewApi")    public AnimEditText(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {        super(context, attrs, defStyleAttr, defStyleRes);        initView(context, attrs,defStyleAttr);    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        super.onLayout(changed, l, t, r, b);    }    public void initView(Context context, AttributeSet attrs, int defStyleRes)    {        LayoutInflater.from(context).inflate(R.layout.view_anim_edit_text, this);        mEditText = (TextInputEditText) findViewById(R.id.et);        mEditTextContainer = (TextInputLayout) findViewById(R.id.et_container);        TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.animedittext_style);        if(typedArray != null){            //这里要注意,String类型是没有默认值的,所以必须定义好,不然又是空指针大法            mHit = typedArray.getString(R.styleable.animedittext_style_hit);            mHitColor = typedArray.getColor(R.styleable.animedittext_style_hitColor, ContextCompat.getColor(context,R.color.login_input_text_color));            mHitSize = typedArray.getDimension(R.styleable.animedittext_style_hitSize, 13);            mText = typedArray.getString(R.styleable.animedittext_style_text);            mTextColor = typedArray.getColor(R.styleable.animedittext_style_textColor, ContextCompat.getColor(context,R.color.login_input_text_color));            mTextSize = typedArray.getDimensionPixelSize(R.styleable.animedittext_style_textSize, 13);            mIsPwd = typedArray.getBoolean(R.styleable.animedittext_style_isPwd, false);            mIsNumber = typedArray.getBoolean(R.styleable.animedittext_style_isNumber, false);            mMaxLength = typedArray.getInt(R.styleable.animedittext_style_maxLength,0);        }        if(!TextUtils.isEmpty(mText))            mEditText.setText(mText);        else            mEditText.setText("");        mEditText.setTextColor(mTextColor);        if(mIsPwd)        mEditText.setInputType(InputType.TYPE_CLASS_TEXT|InputType.TYPE_TEXT_VARIATION_PASSWORD);        if(!TextUtils.isEmpty(mHit))            mEditTextContainer.setHint(mHit);        else            mEditTextContainer.setHint("");        mEditText.setHintTextColor(mHitColor);        mEditText.setTextSize(TypedValue.COMPLEX_UNIT_PX,mTextSize);        if(mIsNumber)        {            mEditText.setInputType(InputType.TYPE_CLASS_NUMBER);        }        if(mMaxLength >0)            mEditText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(mMaxLength)});//        mSplitAnim = (AnimatedVectorDrawable) ContextCompat.getDrawable(context,R.drawable.login_input_vector_split_anim);//        mMergeAnim = (AnimatedVectorDrawable) ContextCompat.getDrawable(context,R.drawable.login_input_vector_merge_anim);        mSplitAnim= AnimatedVectorDrawableCompat.create(context,R.drawable.login_input_vector_split_anim);        mMergeAnim= AnimatedVectorDrawableCompat.create(context,R.drawable.login_input_vector_merge_anim);        noAnimBg= VectorDrawableCompat.create(context.getResources(), R.drawable.login_input_no_anim_vector_drawable,null);        if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) {            mEditTextContainer.setBackgroundDrawable(noAnimBg);        } else {            mEditTextContainer.setBackground(noAnimBg);        }        mEditText.setOnFocusChangeListener(new AOnFocusChangeListener(){            @Override            public void onFocusChange(View v, boolean hasFocus) {                super.onFocusChange(v, hasFocus);            }        });        mEditText.addTextChangedListener(new ATextWatcher());    }    public boolean mIsSplit=false;    public abstract class AOnFocusChangeListener implements OnFocusChangeListener {        @SuppressLint("NewApi")        @Override        public void onFocusChange(View v, boolean hasFocus) {            setHitNotice();            if (hasFocus) {                if(!TextUtils.isEmpty(mEditText.getText().toString()))                    return;                /**                 * 只有当为空值的时候才提示hit,和分开动画                 */                if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) {                    mEditTextContainer.setBackgroundDrawable(mSplitAnim);                } else {                    mEditTextContainer.setBackground(mSplitAnim);                }                Drawable drawable =    mEditTextContainer.getBackground();                if (drawable instanceof Animatable){                    ((Animatable) drawable).start();                    mIsSplit=true;                }            }            else{                if(!mIsSplit)                    return;                /**                 * 只有当分开的拾柴可以触发合并动画                 */                if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) {                    mEditTextContainer.setBackgroundDrawable(mMergeAnim);                } else {                    mEditTextContainer.setBackground(mMergeAnim);                }                Drawable drawable =    mEditTextContainer.getBackground();                if (drawable instanceof Animatable){                    ((Animatable) drawable).start();                    mIsSplit=false;                }            }        }    }    /**     * 设置hit提示     * @return     * 返回true 设置了hit ,表示没有数据     *     * 返回false 没有hit提示,表示有数据     */    private  boolean setHitNotice()    {        String str= mEditText.getText().toString();        if(!TextUtils.isEmpty(str))        {            mEditTextContainer.setHint("");            return false;        }        else        {            mEditTextContainer.setHint(mHit);            return true;        }    }    public  class ATextWatcher implements 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 s) {            /**             *没有数据 并且合并 ,应该进行分开动画给出提示             * 为了增加体验延迟设置hit             */            if(TextUtils.isEmpty(mEditText.getText().toString())&&!mIsSplit)            {                if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) {                    mEditTextContainer.setBackgroundDrawable(mSplitAnim);                } else {                    mEditTextContainer.setBackground(mSplitAnim);                }                Drawable drawable =    mEditTextContainer.getBackground();                if (drawable instanceof Animatable){                    ((Animatable) drawable).start();                    mIsSplit=true;                }                Flowable.timer(350, TimeUnit.MILLISECONDS)                        .observeOn(AndroidSchedulers.mainThread())                        .subscribe(new Consumer() {                            @Override                            public void accept(@NonNull Long aLong) throws Exception {                                mEditTextContainer.setHint(mHit);                            }                        });            }            /**             * 如果有数,但是分开着,应该进行合并动画,并且清楚hit             */            if(!TextUtils.isEmpty(mEditText.getText().toString())&&mIsSplit)            {                if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) {                    mEditTextContainer.setBackgroundDrawable(mMergeAnim);                } else {                    mEditTextContainer.setBackground(mMergeAnim);                }                Drawable drawable =    mEditTextContainer.getBackground();                if (drawable instanceof Animatable){                    ((Animatable) drawable).start();                    mIsSplit=false;                }                Flowable.timer(300, TimeUnit.MILLISECONDS)                        .observeOn(AndroidSchedulers.mainThread())                        .subscribe(new Consumer() {                            @Override                            public void accept(@NonNull Long aLong) throws Exception {                                mEditTextContainer.setHint("");                            }                        });            }        }    }    public void setOnFocusChangeListener(AOnFocusChangeListener aOnFocusChangeListener)    {        if(aOnFocusChangeListener!=null)            mEditText.setOnFocusChangeListener(aOnFocusChangeListener);    }    public void addTextChangedListener(ATextWatcher aTextWatcher)    {        if(aTextWatcher!=null)            mEditText.addTextChangedListener(aTextWatcher);    }    public String getText()    {        return mEditText.getText().toString();    }    public void setText(String str)    {        mEditText.setText(str);    }    public void setmHit(String mHit)    {        this.mHit = mHit;        mEditTextContainer.setHint(mHit);    }}
七、项目例子的地址:
https://github.com/dz-hexiang/AnimEditText.git

更多相关文章

  1. Android混淆代码proguard,内存溢出
  2. android App全局SD卡路径统一管理
  3. Android动态加载补充 加载SD卡中的SO库
  4. Android实现可播放GIF动画的ImageView
  5. Android(安卓)APK DEX分包总结
  6. Android笔记:AlbumSaver图片视频保存工具类
  7. Android增加一个物理按键检测步骤
  8. Android(安卓)AppWidget(桌面小部件-音乐播放动画)
  9. Android小项目之五 splash动画效果

随机推荐

  1. 删除Android包
  2. android 模拟器 获取gps 值
  3. V8 android 测试
  4. android清除指定堆栈中某个activity
  5. android webview点击返回键返回上一级act
  6. Android(安卓)dex2jar反编译失败
  7. android下实现程序不操作一段时间,执行另
  8. android跳转到应用市场进行软件评论和评
  9. android 用ExpandableListView实现的文件
  10. Android(安卓)JNI C调用Java