Google在发布Android 5.0的时候,提供了对Vector的支持,实现了矢量图中Path部分,当然,这不是主要的,主要的是矢量图动画的支持,让我们的app更是充满乐趣!
先上下仿指纹动画的效果图:
从效果图上我们看出,这每一条线都是贝塞尔曲线,由起点,控制点和终点决定着每一条线,对应着矢量图中的path。
1.矢量图xml
看代码片断(在drawable中建一个bezier_svg.xml文件):
<path
android:name="line_13"
android:pathData="M250, 155 C 255, 171 260, 204 247, 234"
android:strokeColor="@android:color/holo_red_dark"
android:strokeLineCap="round"
android:strokeWidth="4" />
解析:
pathData由一串字母组成,有大小写区别,大写是基于原点坐标的位置,小写是基于当前坐标每户的位置。
M(250,155):相当于path中的moveTo方法,将画笔移动到对应的坐标。
C(255,171 || 260,204 || 247,234)(竖线是特意加进去以便观察):这里面有三个坐标,对应着三次贝塞尔曲线的控制点1,控制点2,终点。对贝塞尔曲线有疑问的可以百度。
另外,还有其他的字母,但这里没用到,也一并解析一下:
Q(x, y z, w):对应着二次贝塞尔曲线的控制点(x,y)、终点(z,w)。
L(x,y):以x,y点为终点画一条直线
A(rx,ry,rotation,large_arc_flag,sweep_flag,x,y):这里面参数较多,对应着一个椭圆的各个参数,rx ry为x和y轴的半径,rotation为x轴的旋转角度,两个flat对应的是弧度和绘画方向,最后的x,y是本次椭圆绘画的终点坐标(画到这个点结束,并不是把椭圆画完整)。
贝塞尔曲线示例:
有了贝塞尔曲线的所有path,这还不算完,关键是要让它动起来。
2.动画xml
既然要创建动画,我们在res下建立animator文件夹,建一个anim_trim_path_end.xml文件:
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="700"
android:interpolator="@android:interpolator/linear"
android:propertyName="trimPathEnd"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType" />
这里面有个新的东西trimPathEnd,这个是android矢量图做动画的关键,从0到1,0代表着从空白的开始画path,1即为百分之百的画完了。如果不明白,直接看网上的一个图,一个正方形trimPathEnd从0到1:
当然,对path动画除了这个属性外还有个对应的trimPathStart,这个和trimPathEnd刚好相反,代表着擦除动画,0代表着一画完整的path,0.5表示擦了一半,即只显示path的后半段路径,1表示擦除完全,将会什么也不显示。
有了动画xml之外,还得有一个东西,关连动画xml和矢量图xml。那就是
3.关连xml
在res/drawable中建一个bezier_anim12.xml,对应代码:
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/bezier_svg">
<target
android:name="line_121"
android:animation="@animator/anim_trim_path_end" />
<target
android:name="line_122"
android:animation="@animator/anim_trim_path_end" />
<target
android:name="line_123"
android:animation="@animator/anim_trim_path_end" />
</animated-vector>
name对应着矢量图path中的name,animation对应着动画中的xml,不同的target对应着不同的path。
有了这个xml,我们可以在布局文件里使用它:
<ImageView
android:id="@+id/bezier"
android:layout_width="400dp"
android:layout_height="400dp"
android:layout_centerHorizontal="true"
android:background="#88aa88"
android:src="@drawable/bezier_anim12" />
Activity代码:
public class MainActivity extends AppCompatActivity {
public @BindView(R.id.bezier) ImageView bezier;
public @BindView(R.id.text) TextView text;
public int stage = 1;
public HashMap<Integer, AnimatedVectorDrawable> vectorDrawableHashMap = new HashMap<>();
public AnimatedVectorDrawable bezierAnimator;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
initVectorDrawable();
}
public void initVectorDrawable() {
text.setText(String.valueOf(stage));
vectorDrawableHashMap.put(1, (AnimatedVectorDrawable) getResources().getDrawable(R.drawable.bezier_anim01, null));
vectorDrawableHashMap.put(2, (AnimatedVectorDrawable) getResources().getDrawable(R.drawable.bezier_anim02, null));
vectorDrawableHashMap.put(3, (AnimatedVectorDrawable) getResources().getDrawable(R.drawable.bezier_anim03, null));
vectorDrawableHashMap.put(4, (AnimatedVectorDrawable) getResources().getDrawable(R.drawable.bezier_anim04, null));
vectorDrawableHashMap.put(5, (AnimatedVectorDrawable) getResources().getDrawable(R.drawable.bezier_anim05, null));
vectorDrawableHashMap.put(6, (AnimatedVectorDrawable) getResources().getDrawable(R.drawable.bezier_anim06, null));
vectorDrawableHashMap.put(7, (AnimatedVectorDrawable) getResources().getDrawable(R.drawable.bezier_anim07, null));
vectorDrawableHashMap.put(8, (AnimatedVectorDrawable) getResources().getDrawable(R.drawable.bezier_anim08, null));
vectorDrawableHashMap.put(9, (AnimatedVectorDrawable) getResources().getDrawable(R.drawable.bezier_anim09, null));
vectorDrawableHashMap.put(10, (AnimatedVectorDrawable) getResources().getDrawable(R.drawable.bezier_anim10, null));
vectorDrawableHashMap.put(11, (AnimatedVectorDrawable) getResources().getDrawable(R.drawable.bezier_anim11, null));
vectorDrawableHashMap.put(12, (AnimatedVectorDrawable) getResources().getDrawable(R.drawable.bezier_anim12, null));
}
private void drawFingerprint() {
if (bezierAnimator != null && bezierAnimator.isRunning()) {
return;
}
text.setText(String.valueOf(stage));
bezierAnimator = vectorDrawableHashMap.get(stage);
bezier.setImageDrawable(bezierAnimator);
bezierAnimator.start();
stage++;
if (stage > 12) {
stage = 1;
}
}
@OnClick({R.id.bezier, R.id.text})
public void onClick(View view) {
switch (view.getId()) {
case R.id.bezier:
drawFingerprint();
break;
case R.id.text:
drawFingerprint();
break;
}
}
}
这里使用了butterknife插件,可以支持对资源、事件等的绑定,在这里我就不具体讲了,可以百度一下,这个插件可以让我们不用再去什么findViewById,不用去setXXXLestener啥的。
具体代码已上传到github:https://github.com/jxr202/VivoFingerprintAnimator
有兴趣大家一块讨论。
上学的时候,老师曾教导了我一句话,挺深刻的:不积跬步,无以至千里。。。