Android動畫總結——View動畫、屬性動畫、幀動畫 [復制鏈接]

2019-8-6 10:13
Torr 閱讀:1371 評論:2 贊:0
Tag:  動畫

在App中合理地使用動畫能夠獲得友好愉悅的用戶體驗,Android中的動畫有View動畫、屬性動畫、幀動畫、布局動畫、轉場動畫等,在5.x以后有又新增了矢量動畫,這些動畫在平常開發中使用較為普遍,所以有必要做一次完整的總結。


一、View動畫

View動畫定義了漸變Alpha、旋轉Rotate、縮放Scale、平移Translate四種基本動畫,并且通過這四種基本動畫的組合使用,可以實現多種交互效果。
View動畫使用非常簡單,不僅可以通過XML文件來定義動畫,同樣可以通過Java代碼來實現動畫過程。

1.Xml文件定義View動畫

通過xml來定義View動畫涉及到一些公有的屬性(在AndroidStudio上不能提示):

android:duration     動畫持續時間
android:fillAftertrue動畫結束時,View將保持動畫結束時的狀態
android:fillBeforetrue動畫結束時,View將還原到開始開始時的狀態
android:repeatCount  動畫重復執行的次數
android:repeatMode   動畫重復模式 ,重復播放時restart重頭開始,reverse重復播放時倒敘回放,該屬性需要和android:repeatCount一起使用
android:interpolator 插值器,相當于變速器,改變動畫的不同階段的執行速度

這些屬性是從Animation中繼承下來的,在alpharotatescaletranslate標簽中都可以直接使用。
利用xml文件定義View動畫需要在工程的res目錄下創建anim文件夾,所有的xml定義的View動畫都要放在anim目錄下。
漸變view_anim_alpha.xml

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
       android:duration="2000"
       android:fromAlpha="1.0"
       android:toAlpha="0">
</alpha>

旋轉view_anim_rotate.xml

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="2000"
        android:fillAfter="true"
        android:fromDegrees="0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toDegrees="360">
</rotate>

縮放view_anim_scale.xml

<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
       android:duration="2000"
       android:fromXScale="1.0"
       android:fromYScale="1.0"
       android:pivotX="50%"
       android:pivotY="50%"
       android:toXScale="0.5"
       android:toYScale="0.5">
</scale>

平移view_anim_translate.xml

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
           android:duration="2000"
           android:fromXDelta="0"
           android:fromYDelta="0"
           android:toXDelta="100%"
           android:toYDelta="100%">
</translate>

rotatescale動畫的android:pivotXandroid:pivotY屬性、translate動畫的android:toXDeltaandroid:toYDelta屬性的取值都可以是都可以數值、百分數、百分數p,比如:5050%50%p,他們取值的代表的意義各不相同:
50表示以View左上角為原點沿坐標軸正方向(x軸向右,y軸向下)偏移50px的位置;
50%表示以View左上角為原點沿坐標軸正方向(x軸向右,y軸向下)偏移View寬度或高度的50%處的位置;
50%p表示以View左上角為原點沿坐標軸正方向(x軸向右,y軸向下)偏移父控件寬度或高度的50%處的位置(p表示相對于ParentView的位置)。

"50"位置點

"50%"位置點

"50%p"位置點

通過定義xml動畫資源文件,在Activity中調用:

public void clickToAlpha(View view) {
    Animation alphaAnim = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.view_anim_alpha);
    mTargetView.startAnimation(alphaAnim);
}

public void clickToRotate(View view) {
    Animation rotateAnim = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.view_anim_rotate);
    mTargetView.startAnimation(rotateAnim);
}

public void clickToScale(View view) {
    Animation scaleAnim = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.view_anim_scale);
    mTargetView.startAnimation(scaleAnim);
}

public void clickToTranslate(View view) {
    Animation translateAnim = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.view_anim_translate);
    mTargetView.startAnimation(translateAnim);
}

public void clickToSet(View view) {
    Animation setAnim = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.view_anim_set);
    mTargetView.startAnimation(setAnim);
}
2.Java代碼實現View動畫

在平常的業務邏輯中也可以直接用Java代碼來實現Veiw動畫,Android系統給我們提供了AlphaAnimationRotateAnimationScaleAnimationTranslateAnimation四個動畫類分別來實現View的漸變、旋轉、縮放、平移動畫。
漸變:

public void clickToAlpha(View view) {
    AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
    alphaAnimation.setDuration(2000);
    mTargetView.startAnimation(alphaAnimation);
}

旋轉:

public void clickToRotate(View view) {
    RotateAnimation rotateAnimation = new RotateAnimation(
            0, 360,
            Animation.RELATIVE_TO_SELF, 0.5f,
            Animation.RELATIVE_TO_SELF, 0.5f);
    rotateAnimation.setDuration(2000);
    mTargetView.startAnimation(rotateAnimation);
}

縮放:

public void clickToScale(View view) {
    ScaleAnimation scaleAnimation = new ScaleAnimation(
            1, 0.5f,
            1, 0.5f,
            Animation.RELATIVE_TO_SELF, 0.5f,
            Animation.RELATIVE_TO_SELF, 0.5f);
    scaleAnimation.setDuration(2000);
    mTargetView.startAnimation(scaleAnimation);
}

平移:

public void clickToTranslate(View view) {
    TranslateAnimation translateAnimation = new TranslateAnimation(
            Animation.RELATIVE_TO_SELF, 0,
            Animation.RELATIVE_TO_SELF, 1,
            Animation.RELATIVE_TO_SELF, 0,
            Animation.RELATIVE_TO_SELF, 1);
    translateAnimation.setDuration(2000);
    mTargetView.startAnimation(translateAnimation);
}

組合:

public void clickToSet(View view) {
    AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
    alphaAnimation.setDuration(2000);

    RotateAnimation rotateAnimation = new RotateAnimation(
            0, 360,
            Animation.RELATIVE_TO_SELF, 0.5f,
            Animation.RELATIVE_TO_SELF, 0.5f);
    rotateAnimation.setDuration(2000);

    ScaleAnimation scaleAnimation = new ScaleAnimation(
            1, 0.5f,
            1, 0.5f,
            Animation.RELATIVE_TO_SELF, 0.5f,
            Animation.RELATIVE_TO_SELF, 0.5f);
    scaleAnimation.setDuration(2000);

    TranslateAnimation translateAnimation = new TranslateAnimation(
            Animation.RELATIVE_TO_SELF, 0,
            Animation.RELATIVE_TO_SELF, 1,
            Animation.RELATIVE_TO_SELF, 0,
            Animation.RELATIVE_TO_SELF, 1);
    translateAnimation.setDuration(2000);

    AnimationSet animationSet = new AnimationSet(true);
    animationSet.addAnimation(alphaAnimation);
    animationSet.addAnimation(rotateAnimation);
    animationSet.addAnimation(scaleAnimation);
    animationSet.addAnimation(translateAnimation);

    mTargetView.startAnimation(animationSet);
}
View動畫效果

View動畫可以設置一個動畫執行的監聽器:

animation.setAnimationListener(new Animation.AnimationListener() {
    @Override
    public void onAnimationStart(Animation animation) {
        // 動畫開始
    }

    @Override
    public void onAnimationEnd(Animation animation) {
        // 動畫結束
    }

    @Override
    public void onAnimationRepeat(Animation animation) {
        //動畫重復
    }
});

通過設置監聽器可以在動畫執行的開始、結束、重復時做一些其他的業務邏輯。


二、屬性動畫

所謂屬性動畫,就是改變對象Object的屬性來實現動畫過程。屬性動畫是對View的動畫的擴展,通過它可以實現更多漂亮的動畫效果。同時屬性動畫的作用對象不僅僅是View,任何對象都可以。
屬性動畫的作用效果就是:在一個指定的時間段內將對象的一個屬性的屬性值動態地變化到另一個屬性值。

1.ObjectAnimator

ObjectAnimator是最常用的屬性動畫執行類。

private void startJavaPropertyAnimator() {
    ObjectAnimator
            .ofFloat(mImageView, "rotationY", 0f, 360f)
            .setDuration(2000)
            .start();
}

上面的代碼就是通過ObjectAnimator在2000ms內將mImageViewrotationY屬性的屬性值從0f變化的360f

ObjectAnimator實現屬性動畫

ObjectAnimtor可以用ofIntofFloatofObject等靜態方法,傳入動畫作用的目標Object、屬性字段、屬性開始值、屬性中間值、屬性結束值等參數來構造動畫對象。
在動畫更新的過程中,通過不斷去調用對象屬性的setter方法改變屬性值,不斷重繪實現動畫過程。如果沒有給定動畫開始屬性值,那么系統會通過反射去獲取Object對象的初始值作為動畫的開始值。
屬性動畫也同樣可以通過xml文件來定義,同樣在工程的res目錄下創建animator文件夾,xml文件定義的objectAnimator動畫要放在該文件夾下。

property_animator.xml

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

Java代碼調用:

private void startXmlPropertyAnimator() {
    Animator animator = AnimatorInflater.loadAnimator(getApplicationContext(), 
    R.animator.property_animator);
    animator.setTarget(mImageView);
    animator.start();
}

最終效果如上圖。
屬性動畫也同樣可以組合使用,通過AnimatorSet類和xml文件的set標簽都可以同時改變對象的多個屬性,實現更加豐富的動畫效果。
通過AnimatorSet創建動畫集:

private void startJavaPropertyAnimatorSet() {
    Animator scaleXAnimator = ObjectAnimator.ofFloat(mImageView, "scaleX", 1, 0.5f);
    scaleXAnimator.setDuration(2000);
    Animator scaleYAnimator = ObjectAnimator.ofFloat(mImageView, "scaleY", 1, 0.5f);
    scaleYAnimator.setDuration(2000);
    Animator rotationXAnimator = ObjectAnimator.ofFloat(mImageView, "rotationX", 0, 360);
    rotationXAnimator.setDuration(2000);
    Animator rotationYAnimator = ObjectAnimator.ofFloat(mImageView, "rotationY", 0, 360);
    rotationYAnimator.setDuration(2000);
    AnimatorSet animatorSet = new AnimatorSet();
    animatorSet.play(scaleXAnimator)
            .with(scaleYAnimator)
            .before(rotationXAnimator)
            .after(rotationYAnimator);
    animatorSet.start();
}

AnimatorSet通過beforewithafter三個方法可以組合多個屬性動畫,with表示與給定動畫同時執行,before在給定動畫執行之前執行,after表示在給定動畫執行之后執行。

AnimatorSet構建屬性動畫集

通過xml文件定義屬性動畫集:
property_animator_set.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="2000"
        android:propertyName="scaleX"
        android:valueFrom="1"
        android:valueTo="0.5"
        android:valueType="floatType"/>
    <objectAnimator
        android:duration="2000"
        android:propertyName="scaleY"
        android:valueFrom="1"
        android:valueTo="0.5"
        android:valueType="floatType"/>
    <objectAnimator
        android:duration="2000"
        android:propertyName="alpha"
        android:valueFrom="1"
        android:valueTo="0.5"
        android:valueType="floatType"/>
</set>

在Java代碼中調用屬性動畫集:

private void startxmlPropertyAnimatorSet() {
    Animator animator = AnimatorInflater.loadAnimator(getApplicationContext(), 
    R.animator.property_animator_set);
    animator.setTarget(mImageView);
    animator.start();
}

xml定義屬性動畫集

同樣,屬性動畫也可以添加動畫執行監聽器:

animator.addListener(new Animator.AnimatorListener() {
    @Override
    public void onAnimationStart(Animator animation) {
        // 動畫開始
    }

    @Override
    public void onAnimationEnd(Animator animation) {
        // 動畫結束
    }

    @Override
    public void onAnimationCancel(Animator animation) {
        // 動畫取消
    }

    @Override
    public void onAnimationRepeat(Animator animation) {
        // 動畫重復
    }
});

在監聽到屬性動畫開始、結束、取消、重復時可以去做一些其他的邏輯業務。

2.ValueAnimator

ValueAnimatorObjectAnimator的父類,它繼承自AnimatorValueAnimaotor同樣提供了ofIntofFloatofObject等靜態方法,傳入的參數是動畫過程的開始值、中間值、結束值來構造動畫對象。可以將ValueAnimator看著一個值變化器,即在給定的時間內將一個目標值從給定的開始值變化到給定的結束值。在使用ValueAnimator時通常需要添加一個動畫更新的監聽器,在監聽器中能夠獲取到執行過程中的每一個動畫值。

private void startValueAnimator() {
    ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
    valueAnimator.setDuration(300);
    valueAnimator.start();
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            // 動畫更新過程中的動畫值,可以根據動畫值的變化來關聯對象的屬性,實現屬性動畫
            float value = (float) animation.getAnimatedValue();
            Log.d("ValueAnimator", "動畫值:" + value);
        }
    });
}

在300ms內將數值0變化到1的動畫值的變化log:

02-25 23:16:57.586 D/ValueAnimator: 動畫值:0.0
02-25 23:16:57.596 D/ValueAnimator: 動畫值:0.007902175
02-25 23:16:57.616 D/ValueAnimator: 動畫值:0.029559612
02-25 23:16:57.636 D/ValueAnimator: 動畫值:0.066987276
02-25 23:16:57.646 D/ValueAnimator: 動畫值:0.118102014
02-25 23:16:57.666 D/ValueAnimator: 動畫值:0.18128797
02-25 23:16:57.686 D/ValueAnimator: 動畫值:0.2545482
02-25 23:16:57.706 D/ValueAnimator: 動畫值:0.33063102
02-25 23:16:57.716 D/ValueAnimator: 動畫值:0.4166157
02-25 23:16:57.736 D/ValueAnimator: 動畫值:0.5052359
02-25 23:16:57.746 D/ValueAnimator: 動畫值:0.5936906
02-25 23:16:57.766 D/ValueAnimator: 動畫值:0.67918396
02-25 23:16:57.786 D/ValueAnimator: 動畫值:0.7545208
02-25 23:16:57.796 D/ValueAnimator: 動畫值:0.82671034
02-25 23:16:57.826 D/ValueAnimator: 動畫值:0.88857293
02-25 23:16:57.836 D/ValueAnimator: 動畫值:0.93815327
02-25 23:16:57.856 D/ValueAnimator: 動畫值:0.9721882
02-25 23:16:57.876 D/ValueAnimator: 動畫值:0.99384415
02-25 23:16:57.886 D/ValueAnimator: 動畫值:1.0

ValueAnimator的使用一般會結合更新監聽器AnimatorUpdateListener,大多數時候是在自定義控件時使用。
下面是用ValueAnimator自定義控件實現動畫打開關閉效果。
expanded_veiw.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"
              android:background="@android:color/white"
              android:divider="@drawable/divider"
              android:orientation="vertical"
              android:showDividers="middle">

    <LinearLayout
        android:id="@+id/ll_expanded_question"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:gravity="center_vertical">

        <TextView
            android:id="@+id/tv_expanded_question"
            android:layout_width="0dp"
            android:layout_height="48dp"
            android:layout_weight="1"
            android:gravity="center_vertical"
            android:padding="8dp"
            android:text="如何把一本很難的書看懂?"
            android:textColor="#999999"
            android:textSize="16sp"/>

        <ImageView
            android:id="@+id/iv_expanded_indicator"
            android:layout_width="16dp"
            android:layout_height="16dp"
            android:layout_marginRight="16dp"
            android:src="@drawable/img_up"/>

    </LinearLayout>


    <TextView
        android:id="@+id/tv_expanded_answer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:text="多讀幾遍。真的特別有用。至少看三遍。從開頭看,看到中間,重頭再來,再看得多一點,在從新開始,建議看到快結束時再從新開始。"
        android:textColor="#999999"
        android:textSize="16sp"/>
</LinearLayout>
public class ExpandedView extends FrameLayout {

    private TextView mTvAnswer;
    private boolean isClosed;
    private ImageView mIvIndicator;

    public ExpandedView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    private void init(Context context) {
        View view = LayoutInflater.from(context).inflate(R.layout.expanded_view, this, true);
        LinearLayout llQuestion = (LinearLayout) view.findViewById(R.id.ll_expanded_question);
        llQuestion.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                anim();
            }
        });
        mTvAnswer = (TextView) view.findViewById(R.id.tv_expanded_answer);
        mIvIndicator = (ImageView) view.findViewById(R.id.iv_expanded_indicator);
    }

    private void anim() {
        // 指示器旋轉
        ValueAnimator valueAnimator1 = isClosed
                ? ValueAnimator.ofFloat(180, 0)
                : ValueAnimator.ofFloat(0, 180);
        valueAnimator1.setDuration(500);
        valueAnimator1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value = (float) animation.getAnimatedValue();
                mIvIndicator.setRotation(value);
            }
        });
        valueAnimator1.start();

        // 打開開關閉操作
        final int answerHeight = mTvAnswer.getMeasuredHeight();
        ValueAnimator valueAnimator2 = isClosed
                ? ValueAnimator.ofInt(-answerHeight, 0)
                : ValueAnimator.ofInt(0, -answerHeight);
        valueAnimator2.setDuration(500);
        final MarginLayoutParams params = (MarginLayoutParams) mTvAnswer.getLayoutParams();
        valueAnimator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();
                params.bottomMargin = value;
                mTvAnswer.setLayoutParams(params);
            }
        });
        valueAnimator2.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                isClosed = !isClosed;
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
        valueAnimator2.start();
    }
}

ValueAnimator自定義控件效果圖
3.TypeEvaluator

ObjectAnimatorValueAnimator都有ofObject方法,傳入的都有一個TypeEvaluator類型的參數。TypeEvaluator是一個接口,里面也只有一個抽象方法:

public T evaluate(float fraction, T startValue, T endValue);

再看ofInt方法中沒有傳入該參數,但實際上調用ofInt方法時,系統已經有實現了TypeEvaluator接口的IntEvaluator,它的源碼也非常簡單:

public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
    int startInt = startValue;
    return (int)(startInt + fraction * (endValue - startInt));
}

fraction范圍為0到1,表示動畫執行過程中已完成程度。
泛型T即為動畫執行的屬性類型。
所以我們要用屬性動畫來執行復雜對象的動畫過程,就需要自定義TypeEvaluator,實現動畫邏輯。
先來定義一個對象

public class Circle {

    private int raduis;         // 半徑
    private int color;          // 顏色
    private int elevation;      // 高度

    public Circle(int raduis, int color, int elevation) {
        this.raduis = raduis;
        this.color = color;
        this.elevation = elevation;
    }

    public int getRaduis() {
        return raduis;
    }

    public void setRaduis(int raduis) {
        this.raduis = raduis;
    }

    public int getColor() {
        return color;
    }

    public void setColor(int color) {
        this.color = color;
    }

    public int getElevation() {
        return elevation;
    }

    public void setElevation(int elevation) {
        this.elevation = elevation;
    }
}

CircleEvaluator,實現TypeEvaluator接口:

public class CircleEvaluator implements TypeEvaluator<Circle> {
    @Override
    public Circle evaluate(float fraction, Circle startValue, Circle endValue) {

        int startRaduis = startValue.getRaduis();
        int endRaduis = endValue.getRaduis();
        int raduis = (int) (startRaduis + fraction * (endRaduis - startRaduis));

        int startColor = startValue.getColor();
        int endColor = endValue.getColor();
        int startColorRed = Color.red(startColor);
        int startColorGreen = Color.green(startColor);
        int startColorBlue = Color.blue(startColor);
        int endColorRed = Color.red(endColor);
        int endColorGreen = Color.green(endColor);
        int endColorBlue = Color.blue(endColor);
        int colorRed = (int) (startColorRed + fraction * (endColorRed - startColorRed));
        int colorGreen = (int) (startColorGreen + fraction * (endColorGreen - startColorGreen));
        int colorBlue = (int) (startColorBlue + fraction * (endColorBlue - startColorBlue));
        int color = Color.rgb(colorRed, colorGreen, colorBlue);

        int startElevation = startValue.getElevation();
        int endElevation = endValue.getElevation();
        int elevation = (int) (startElevation + fraction * (endElevation - startElevation));

        return new Circle(raduis, color, elevation);
    }
}

自定義控件CircleView,將Circle作為它的一個屬性:

public class CircleView extends View {
    private Circle circle;
    private Paint mPaint;

    public CircleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        circle = new Circle(168, Color.RED, 0);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        setElevation(circle.getElevation());
        mPaint.setColor(circle.getColor());
        canvas.drawCircle(getMeasuredHeight() / 2, getMeasuredHeight() / 2, circle.getRaduis(), mPaint);
    }

    public void setCircle(Circle circle) {
        this.circle = circle;
        postInvalidate();
    }

    public Circle getCircle() {
        return circle;
    }
}

ObjectAnimator使用:

private void start1() {
    Circle startCircle = new Circle(168, Color.RED, 0);
    Circle middleCircle = new Circle(300, Color.GREEN, 15);
    Circle endCircle = new Circle(450, Color.BLUE, 30);
    ObjectAnimator.ofObject(mCircleView, "circle", new CircleEvaluator(), startCircle, middleCircle, endCircle)
            .setDuration(5000)
            .start();
}

ValueAnimator使用:

private void start2() {
    Circle startCircle = new Circle(168, Color.RED, 0);
    Circle middleCircle = new Circle(300, Color.GREEN, 15);
    Circle endCircle = new Circle(450, Color.BLUE, 30);
    ValueAnimator valueAnimator = ValueAnimator.ofObject(new CircleEvaluator(), startCircle, middleCircle, endCircle);
    valueAnimator.setDuration(5000);
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            Circle circle = (Circle) animation.getAnimatedValue();
            mCircleView.setCircle(circle);
        }
    });
    valueAnimator.start();
}

自定義TypeEvaluator效果圖

需要注意的是,系統調用獲取控件setter、getter方法是通過反射獲取的,屬性的名稱必須和getter、setter方法名稱后面的字符串一致,比如上面的getter、setter方法分別為getCirclesetCircle,那么屬性名字就必須為circle


三、幀動畫

幀動畫需要開發者制定好動畫每一幀,系統一幀一幀的播放圖片。

private void start1() {
    AnimationDrawable ad = new AnimationDrawable();
    for (int i = 0; i < 7; i++) {
        Drawable drawable = getResources().getDrawable(getResources().getIdentifier("ic_fingerprint_" + i, "drawable", getPackageName()));
        ad.addFrame(drawable, 100);
    }
    ad.setOneShot(false);
    mImageView.setImageDrawable(ad);
    ad.start();
}

幀動畫同樣也可以在xml文件中配置,直接在工程drawable目錄新建animation-list標簽:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
<item android:drawable="@drawable/ic_fingerprint_0" android:duration="100"/>
<item android:drawable="@drawable/ic_fingerprint_1" android:duration="100"/>
<item android:drawable="@drawable/ic_fingerprint_2" android:duration="100"/>
<item android:drawable="@drawable/ic_fingerprint_3" android:duration="100"/>
<item android:drawable="@drawable/ic_fingerprint_4" android:duration="100"/>
<item android:drawable="@drawable/ic_fingerprint_5" android:duration="100"/>
<item android:drawable="@drawable/ic_fingerprint_6" android:duration="100"/>
</animation-list>
private void start2() {
    mImageView.setImageResource(R.drawable.frame_anim);
    AnimationDrawable animationDrawable = (AnimationDrawable) mImageView.getDrawable();
    animationDrawable.start();
}

其中android:onshot屬性和setOneShot方法表示是否只執行一次。
AnimationDrawable實際是上是一個Drawable動畫,動畫執行的過程總會給你不斷重繪Drawable的每一幀圖像,實現動態播放效果。

幀動畫效果圖



我來說兩句
您需要登錄后才可以評論 登錄 | 立即注冊
facelist
所有評論(2)
俗不可耐 2019-8-6 18:04
別的我不知道 我喜歡楊冪
回復
15801260778 2019-8-15 16:32
666,總結的太棒了
回復
領先的中文移動開發者社區
18620764416
7*24全天服務
意見反饋:[email protected]

掃一掃關注我們

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 粵ICP備15117877號 )

阿拉斯加垂钓APP下载
上海快3 体彩20选5 青海十一选五 美国职业棒球比分牌kpc 股票涨跌的本质 即时赔率指数 云南时时彩 股票指数期货有哪些 赢策配资 众诚速配 黑龙江11选5 风速配资 雷速体育足球比分 14场胜负 永之胜配资 黑龙江p62