Android动画(五):SVG动画

本系列将介绍以下内容:
在这里插入图片描述

Android动画

1 SVG简介

SVG的全称是Scalable Vector Graphics(可缩放矢量图),它是矢量图,是专门用于网络的矢量图形标准。
与矢量图对应的是位图Bitmap。位图由一个个像素点组成,当图片放大到一定大小时会出现马赛克现象。而矢量图是由一个个点组成,经过数学计算利用直线和曲线绘制而成,无论如何放大都不会出现马赛克现象。

SVG与Bitmap对比:
1、SVG使用XML格式定义图形,可被多种工具读取和修改,如记事本。
2、SVG由点存储,由计算机根据点信息绘图,不会失真,无须根据分辨率适配多套图标。
3、SVG的占用空间比Bitmap小。如一张500px*500px的图像,转成SVG后占用空间大小约20KB,而PNG图像需要约732KB。
4、SVG可转换为Path路径,与Path动画结合组成更丰富的动画。

2 vector标签

在Android中,SVG矢量图是使用标签定义的,存放在res/drawable目录中。

如定义一个vector文件svg_line.xml:

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="200dp"
    android:height="100dp"
    android:viewportWidth="100"
    android:viewportHeight="50">
    <path
        android:name="bar"
        android:pathData="M50,25 L100,25"
        android:strokeWidth="2"
        android:strokeColor="@android:color/darker_gray"
        android:trimPathStart="0" />
</vector>

在这里插入图片描述

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="200dp"
    android:height="100dp"
    android:viewportWidth="100"
    android:viewportHeight="50">

    <group
        android:pivotX="50"
        android:pivotY="25"
        android:rotation="90">
        <path
            android:name="bar"
            android:pathData="M50,23 L100,23"
            android:strokeWidth="2"
            android:strokeColor="@android:color/darker_gray" />
    </group>

</vector>

在这里插入图片描述
vector标签指定的是画布大小,path标签则指定的是路径内容,group标签用于定义一系列路径或者将path标签分组。更多标签属性及其用法请自行学习。

3 引入SVG图像

Android是不支持SVG图像解析的,必须将SVG图像转换为vector标签描述。可以通过在线转换网站将SVG图像转换为对应的vector标签代码,也可以通过Android Studio引入。如图所示:
在这里插入图片描述

通过Android Studio引入SVG

4 显示SVG图像

4.1 引入兼容包

dependencies {
	implementation libs.appcompat
}

使用Gradle Plugin 2.0以上的版本:

android {
    namespace 'com.example.mysvg'
    compileSdk 34
    
    defaultConfig {
        vectorDrawables.useSupportLibrary = true
    }
}

4.2 使用SVG图像

1、对于ImageView和ImageButton这样的控件,要兼容SVG图像,只需要将android:src属性替换为app:srcCompat属性即可。

<ImageView
    android:id="@+id/iv"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:contentDescription="@string/app_name"
    app:srcCompat="@drawable/svg_line" />

或,在代码中使用:

imageView.setImageResource(R.drawable.svg_line);

2、在Button和RadioButton中使用
此时需要通过selector标签来实现,如定义文件selector_svg_line.xml:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/svg_line" android:state_pressed="true" />
    <item android:drawable="@drawable/svg_line" />
</selector>

在Button中使用:

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/selector_svg_line" />

使用时,23.2.0版本需要在代码中添加静态代码块,新版已不需要:

public class SVGShowActivity extends AppCompatActivity {
    /*
     该代码块放在前部是解决23.2.0版本中的bug,新版不需要了
     */
    static {
        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.svg_show_activity);
        // 直接用Button即可
        
    }
}

5 动态Vector

1、首先还是需要一个vector文件svg_line.xml(见上文),然后定义一个Animator文件以表示对该vector图像做动画,如res/anim/anim_trim_start.xml:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:propertyName="trimPathStart"
    android:valueFrom="0"
    android:valueTo="1" />

注意:objectAnimator 所指定的动画是对应vector中path标签的,该示例的动画效果是动态改变path标签的android:trimPathStart属性值,从0变到1。

2、有了图像和动画这两个文件后,需要使用专门用于vector图像与动画关联的animated-vector标签,如res/drawable/line_animated_vector.xml:

<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/svg_line">

    <target
        android:name="bar"
        android:animation="@anim/anim_trim_start" />

</animated-vector>

在animated-vector标签中,通过android:drawable属性指定vector图像文件。
通过target标签将路径与动画关联,其中,target标签的android:name属性就是指定的vector文件中的path标签的android:name,这两个name属性值必须相同,代表着对该path标签做动画。target标签的android:animation属性用来指定path标签所对应的动画。
animated-vector标签中可以有多个target标签,每个target标签可以将一个path与Animator相关联。

示例,布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="开始动画" />

    <ImageView
        android:id="@+id/anim_img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:srcCompat="@drawable/svg_line" />

</LinearLayout>

逻辑代码(详见注释):

package com.example.mysvg;

import android.graphics.drawable.Animatable;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat;


public class SVGAnimActivity extends AppCompatActivity {
    /*
    该代码块放在前部是解决23.2.0版本中的bug,新版不需要了
     */
    static {
        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.svg_anim_activity);

        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ImageView imageView = findViewById(R.id.anim_img);
				
				// 加载animated-vector文件
                AnimatedVectorDrawableCompat animatableDrawable = AnimatedVectorDrawableCompat.create(
                        SVGAnimActivity.this,
                        R.drawable.line_animated_vector
                );
                imageView.setImageDrawable(animatableDrawable);

                ((Animatable) imageView.getDrawable()).start();
            }
        });
    }

}

效果图:
在这里插入图片描述

SVG动画

6 搜索动画示例

vector文件res/drawable/vector_search_bar.xml:

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="150dp"
    android:height="24dp"
    android:viewportWidth="150"
    android:viewportHeight="24">

    <!--搜索条-->
    <path
        android:name="search"
        android:pathData="M141,17 A9,9 0 1,1 142,16 L149,23"
        android:strokeWidth="2"
        android:strokeColor="@android:color/darker_gray" />

    <!--底部横线-->
    <path
        android:name="bar"
        android:pathData="M0,23 L149,23"
        android:strokeWidth="2"
        android:strokeColor="@android:color/darker_gray"
        android:trimPathStart="1" />

</vector>

起点动画文件res/anim/anim_bar_trim_start.xml:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500"
    android:propertyName="trimPathStart"
    android:valueFrom="0"
    android:valueTo="1"
    android:valueType="floatType" />

终点动画文件res/anim/anim_search_trim_end.xml:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500"
    android:propertyName="trimPathEnd"
    android:valueFrom="0"
    android:valueTo="1"
    android:valueType="floatType" />

关联文件res/drawable/animated_vecotr_search.xml:

<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/vector_search_bar">
    <target
        android:name="search"
        android:animation="@anim/anim_search_trim_end" />
    <target
        android:name="bar"
        android:animation="@anim/anim_bar_trim_start" />
</animated-vector>

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginLeft="20dp"
    android:layout_marginTop="20dp"
    android:orientation="vertical">

    <EditText
        android:id="@+id/edit"
        android:layout_width="150dp"
        android:layout_height="24dp"
        android:background="@null"
        android:hint="点击输入" />

    <ImageView
        android:id="@+id/anim_img"
        android:layout_width="150dp"
        android:layout_height="24dp" />
</FrameLayout>

代码:

package com.example.mysvg;

import android.graphics.drawable.Animatable;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat;

public class SearchEditActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.svg_edit_search_activity);

        final ImageView imageView = findViewById(R.id.anim_img);
        imageView.setFocusable(true);
        imageView.setFocusableInTouchMode(true);
        imageView.requestFocus();
        imageView.requestFocusFromTouch();

        EditText editText = findViewById(R.id.edit);
        editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (hasFocus) {
                    // 加载animated-vector文件
                    AnimatedVectorDrawableCompat animatedVectorDrawableCompat = AnimatedVectorDrawableCompat.create(
                            SearchEditActivity.this,
                            R.drawable.animated_vecotr_search
                    );
                    
                    imageView.setImageDrawable(animatedVectorDrawableCompat);
                    ((Animatable) imageView.getDrawable()).start();
                }
            }
        });
    }

}

效果图:
在这里插入图片描述

搜索动画

参考文献:
[1] UML中的类图及类图之间的关系
[2] 启舰.Android自定义控件开发入门与实战[M].北京:电子工业出版社,2018

微信公众号:TechU
在这里插入图片描述

  • 25
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值