自定义实现向量图标动画VectorDrawable

前言

从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,1h23,0q6,0 6,6q0,6 -6,6h-76q-6,0 -6,-6q0,-6,6,-6h54,0" />vector>

效果如下
这里写图片描述

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



效果如下:
这里写图片描述

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



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



二、合并属性动画

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


<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


<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


<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


<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


<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);}@Overrideprotected 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);elsemEditText.setText("");mEditText.setTextColor(mTextColor);if(mIsPwd)mEditText.setInputType(InputType.TYPE_CLASS_TEXT|InputType.TYPE_TEXT_VARIATION_PASSWORD);if(!TextUtils.isEmpty(mHit))mEditTextContainer.setHint(mHit);elsemEditTextContainer.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(){@Overridepublic 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")@Overridepublic 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 {@Overridepublic void beforeTextChanged(CharSequence s, int start, int count, int after) {}@Overridepublic void onTextChanged(CharSequence s, int start, int before, int count) {}@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部