欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  web前端

animation之objectAnimator_html/css_WEB-ITnose

程序员文章站 2022-05-06 08:17:14
...
这次借助github上的开源项目ShapeLoadingView来学习下ObjectAnimator和animatorSet.
代码结构目录:
  1. ShapeLoadingView.java
  2. LoadingView.java
    LoadingView是绘制三个基本图形的类。
    ShapeLoadingView初始化图形并操作图形进行动画。
    下面上加了注释的代码:
package com.mingle.widget;import android.annotation.TargetApi;import android.content.Context;import android.content.res.TypedArray;import android.os.Build;import android.text.TextUtils;import android.util.AttributeSet;import android.view.Gravity;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.view.animation.AccelerateInterpolator;import android.view.animation.DecelerateInterpolator;import android.widget.FrameLayout;import android.widget.ImageView;import android.widget.TextView;import com.mingle.shapeloading.R;import com.nineoldandroids.animation.Animator;import com.nineoldandroids.animation.AnimatorSet;import com.nineoldandroids.animation.ObjectAnimator;/** * Created by zzz40500 on 15/4/6. */public class LoadingView extends FrameLayout {    private static final int ANIMATION_DURATION = 500;    private static  float mDistance = 200;    private ShapeLoadingView mShapeLoadingView;    private ImageView mIndicationIm;    private TextView mLoadTextView;    private int mTextAppearance;    private String mLoadText;    public LoadingView(Context context) {        super(context);    }    public LoadingView(Context context, AttributeSet attrs) {        //构造函数        super(context, attrs, 0);        init(context, attrs);    }    private void init(Context context, AttributeSet attrs) {        //这里是通过自定义属性来显示字符串        TypedArray typedArray = context                .obtainStyledAttributes(attrs, R.styleable.LoadingView);        mLoadText = typedArray.getString(R.styleable.LoadingView_loadingText);        mTextAppearance = typedArray.getResourceId(R.styleable.LoadingView_loadingTextAppearance, -1);        typedArray.recycle();    }    public LoadingView(Context context, AttributeSet attrs, int defStyleAttr) {        //构造函数        super(context, attrs, defStyleAttr);        init(context, attrs);    }    //这里定义了一个针对LL版本的构造函数,我这可能因为sdk版本这里会报错,如果报错注释掉就行了    @TargetApi(Build.VERSION_CODES.LOLLIPOP)    public LoadingView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {        super(context, attrs, defStyleAttr, defStyleRes);        init(context, attrs);    }    //dp和像素的转换    public int dip2px(float dipValue) {        final float scale = getContext().getResources().getDisplayMetrics().density;        return (int) (dipValue * scale + 0.5f);    }    //引入布局    @Override    protected void onFinishInflate() {        super.onFinishInflate();        View view = LayoutInflater.from(getContext()).inflate(R.layout.load_view, null);        mDistance = dip2px(54f);        LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);        layoutParams.gravity = Gravity.CENTER;        mShapeLoadingView = (ShapeLoadingView) view.findViewById(R.id.shapeLoadingView);        mIndicationIm = (ImageView) view.findViewById(R.id.indication);        mLoadTextView = (TextView) view.findViewById(R.id.promptTV);        if (mTextAppearance != -1) {            mLoadTextView.setTextAppearance(getContext(), mTextAppearance);        }        setLoadingText(mLoadText);        //显示绘画布局        addView(view, layoutParams);        //这里是设计一个延时 每隔900调用一次跌落,相当于900ms是一次动画的周期        this.postDelayed(new Runnable() {            @Override            public void run() {                freeFall();            }        }, 900);    }    public void setLoadingText(CharSequence loadingText) {        if (TextUtils.isEmpty(loadingText)) {            mLoadTextView.setVisibility(GONE);        } else {            mLoadTextView.setVisibility(VISIBLE);        }        mLoadTextView.setText(loadingText);    }    /** * 上抛,上抛是动画的核心,上抛是两个组合动作:1,图形进行旋转;2,图形向上平移,同时还有下面阴影部分随着图形位置变化 * 进行的跟随变化。这里使用了ObjectAnimator来控制每个动画的动作,最后使用AnimatorSet将三个部分组合在一起。 * 看一下具体的动作 */    public void upThrow() {        //mShapeLoadingView就是LoadingView里面绘制的图形买第一个objectAnimator控制它进行平移        //使用objectAnimator.ofFloat及参数translationY来进行纵向的平移        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mShapeLoadingView, "translationY", mDistance, 0);        //动画下部的阴影这里使用ofFloat及参数scaleX来进行X轴的缩放,02f-1是缩放比例 阴影在20%到100%之间变化        ObjectAnimator scaleIndication = ObjectAnimator.ofFloat(mIndicationIm, "scaleX", 0.2f, 1);        //这段是对图形做一个旋转的动作        ObjectAnimator objectAnimator1 = null;        switch (mShapeLoadingView.getShape()) {            case SHAPE_RECT:                objectAnimator1 = ObjectAnimator.ofFloat(mShapeLoadingView, "rotation", 0, -120);                break;            case SHAPE_CIRCLE:                objectAnimator1 = ObjectAnimator.ofFloat(mShapeLoadingView, "rotation", 0, 180);                break;            case SHAPE_TRIANGLE:                objectAnimator1 = ObjectAnimator.ofFloat(mShapeLoadingView, "rotation", 0, 180);                break;        }        //设置animation的持续时间,通过setDuration.        objectAnimator.setDuration(ANIMATION_DURATION);        objectAnimator1.setDuration(ANIMATION_DURATION);        //设置一个减速插值器        objectAnimator.setInterpolator(new DecelerateInterpolator(factor));        objectAnimator1.setInterpolator(new DecelerateInterpolator(factor));        AnimatorSet animatorSet = new AnimatorSet();        animatorSet.setDuration(ANIMATION_DURATION);        //animatorSet的方法playtogther让三个动画同时运行        animatorSet.playTogether(objectAnimator, objectAnimator1, scaleIndication);        animatorSet.addListener(new Animator.AnimatorListener() {            @Override            public void onAnimationStart(Animator animation) {            }            @Override            public void onAnimationEnd(Animator animation) {                freeFall();            }            @Override            public void onAnimationCancel(Animator animation) {            }            @Override            public void onAnimationRepeat(Animator animation) {            }        });        animatorSet.start();    }    public float factor = 1.2f;    /** * 下落 */    public void freeFall() {        //主要的点和上抛一致不讲了        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mShapeLoadingView, "translationY", 0, mDistance);        ObjectAnimator scaleIndication = ObjectAnimator.ofFloat(mIndicationIm, "scaleX", 1, 0.2f);        objectAnimator.setDuration(ANIMATION_DURATION);        objectAnimator.setInterpolator(new AccelerateInterpolator(factor));        AnimatorSet animatorSet = new AnimatorSet();        animatorSet.setDuration(ANIMATION_DURATION);        animatorSet.playTogether(objectAnimator, scaleIndication);        animatorSet.addListener(new Animator.AnimatorListener() {            @Override            public void onAnimationStart(Animator animation) {            }            @Override            public void onAnimationEnd(Animator animation) {                //下落到底端改变图形                mShapeLoadingView.changeShape();                upThrow();            }            @Override            public void onAnimationCancel(Animator animation) {            }            @Override            public void onAnimationRepeat(Animator animation) {            }        });        animatorSet.start();    }}
package com.mingle.widget;import android.annotation.TargetApi;import android.content.Context;import android.graphics.Canvas;import android.graphics.Paint;import android.graphics.Path;import android.os.Build;import android.util.AttributeSet;import android.view.View;import com.mingle.shapeloading.R;/** * Created by zzz40500 on 15/4/4. */public class ShapeLoadingView extends View {    private static final float genhao3 = 1.7320508075689f;    private static  final  float mTriangle2Circle =0.25555555f;    private Shape mShape = Shape.SHAPE_CIRCLE;    /** * 用贝赛尔曲线画圆 */    private float mMagicNumber = 0.55228475f;    public ShapeLoadingView(Context context) {        super(context);        init();    }    public ShapeLoadingView(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public ShapeLoadingView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @TargetApi(Build.VERSION_CODES.LOLLIPOP)    public ShapeLoadingView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {        super(context, attrs, defStyleAttr, defStyleRes);        init();    }    private void init() {        mPaint = new Paint();        mPaint.setColor(getResources().getColor(R.color.triangle));        mPaint.setAntiAlias(true);        //看到网上说这个FILL_AND_STROKE有去锯齿的作用        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);        setBackgroundColor(getResources().getColor(R.color.view_bg));    }    public boolean mIsLoading = false;    private Paint mPaint;    private float mControlX = 0;    private float mControlY = 0;    private float mAnimPercent;    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        //绘制三个图形的三角形方框圆形的位置,作者在这里标记动画可以优化,估计会有后续修改        if(getVisibility()==GONE){            return;        }        // FIXME: 15/6/15 动画待优化        switch (mShape) {            case SHAPE_TRIANGLE:                if (mIsLoading) {                    mAnimPercent += 0.1611113;                    // triangle to circle                    Path path = new Path();                    path.moveTo(relativeXFromView(0.5f), relativeYFromView(0f));                    if (mAnimPercent >= 1) {                        mShape = Shape.SHAPE_CIRCLE;                        mIsLoading = false;                        mAnimPercent=1;                    }                    float controlX = mControlX - relativeXFromView(mAnimPercent* mTriangle2Circle)                            * genhao3;                    float controlY = mControlY - relativeYFromView(mAnimPercent* mTriangle2Circle);                    path.quadTo(relativeXFromView(1) - controlX, controlY, relativeXFromView(0.5f + genhao3 / 4), relativeYFromView(0.75f));                    path.quadTo(relativeXFromView(0.5f), relativeYFromView(0.75f + 2 * mAnimPercent* mTriangle2Circle), relativeXFromView(0.5f - genhao3 / 4), relativeYFromView(0.75f));                    path.quadTo(controlX, controlY, relativeXFromView(0.5f), relativeYFromView(0f));                    path.close();                    canvas.drawPath(path, mPaint);                    invalidate();                } else {                    Path path = new Path();                    mPaint.setColor(getResources().getColor(R.color.triangle));                    path.moveTo(relativeXFromView(0.5f), relativeYFromView(0f));                    path.lineTo(relativeXFromView(1), relativeYFromView(genhao3 / 2f));                    path.lineTo(relativeXFromView(0), relativeYFromView(genhao3/2f));                    mControlX = relativeXFromView(0.5f - genhao3 / 8.0f);                    mControlY = relativeYFromView(3 / 8.0f);                    mAnimPercent = 0;                    path.close();                    canvas.drawPath(path, mPaint);                }                break;            case SHAPE_CIRCLE:                if (mIsLoading) {                    float magicNumber = mMagicNumber + mAnimPercent;                    mAnimPercent += 0.12;                    if (magicNumber + mAnimPercent >= 1.9f) {                        mShape = Shape.SHAPE_RECT;                        mIsLoading = false;                    }                    Path path = new Path();                    path.moveTo(relativeXFromView(0.5f), relativeYFromView(0f));                    path.cubicTo(relativeXFromView(0.5f + magicNumber / 2), relativeYFromView(0f),                            relativeXFromView(1), relativeYFromView(0.5f - magicNumber / 2),                            relativeXFromView(1f), relativeYFromView(0.5f));                    path.cubicTo(                            relativeXFromView(1), relativeXFromView(0.5f + magicNumber / 2),                            relativeXFromView(0.5f + magicNumber / 2), relativeYFromView(1f),                            relativeXFromView(0.5f), relativeYFromView(1f));                    path.cubicTo(relativeXFromView(0.5f - magicNumber / 2), relativeXFromView(1f),                            relativeXFromView(0), relativeYFromView(0.5f + magicNumber / 2),                            relativeXFromView(0f), relativeYFromView(0.5f));                    path.cubicTo(relativeXFromView(0f), relativeXFromView(0.5f - magicNumber / 2),                            relativeXFromView(0.5f - magicNumber / 2), relativeYFromView(0),                            relativeXFromView(0.5f), relativeYFromView(0f));                    path.close();                    canvas.drawPath(path, mPaint);                    invalidate();                } else {      mPaint.setColor(getResources().getColor(R.color.circle));                    Path path = new Path();                    float magicNumber = mMagicNumber;                    path.moveTo(relativeXFromView(0.5f), relativeYFromView(0f));                    path.cubicTo(relativeXFromView(0.5f + magicNumber / 2), 0,                            relativeXFromView(1), relativeYFromView(magicNumber / 2),                            relativeXFromView(1f), relativeYFromView(0.5f));                    path.cubicTo(                            relativeXFromView(1), relativeXFromView(0.5f + magicNumber / 2),                            relativeXFromView(0.5f + magicNumber / 2), relativeYFromView(1f),                            relativeXFromView(0.5f), relativeYFromView(1f));                    path.cubicTo(relativeXFromView(0.5f - magicNumber / 2), relativeXFromView(1f),                            relativeXFromView(0), relativeYFromView(0.5f + magicNumber / 2),                            relativeXFromView(0f), relativeYFromView(0.5f));                    path.cubicTo(relativeXFromView(0f), relativeXFromView(0.5f - magicNumber / 2),                            relativeXFromView(0.5f - magicNumber / 2), relativeYFromView(0),                            relativeXFromView(0.5f), relativeYFromView(0f));                    mAnimPercent = 0;                    path.close();                    canvas.drawPath(path, mPaint);                }                break;            case SHAPE_RECT:                if (mIsLoading) {                    mAnimPercent += 0.15;                    if (mAnimPercent >= 1) {                        mShape = Shape.SHAPE_TRIANGLE;                        mIsLoading = false;                        mAnimPercent = 1;                    }                    Path path = new Path();                    path.moveTo(relativeXFromView(0.5f * mAnimPercent), 0);                    path.lineTo(relativeYFromView(1 - 0.5f * mAnimPercent), 0);                    float distanceX = (mControlX) * mAnimPercent;                    float distanceY = (relativeYFromView(1f) - mControlY) * mAnimPercent;                    path.lineTo(relativeXFromView(1f) - distanceX, relativeYFromView(1f) - distanceY);                    path.lineTo(relativeXFromView(0f) + distanceX, relativeYFromView(1f) - distanceY);                    path.close();                    canvas.drawPath(path, mPaint);                    invalidate();                } else {                    mPaint.setColor(getResources().getColor(R.color.rect));                    mControlX = relativeXFromView(0.5f - genhao3 / 4);                    mControlY = relativeYFromView(0.75f);                    Path path = new Path();                    path.moveTo(relativeXFromView(0f), relativeYFromView(0f));                    path.lineTo(relativeXFromView(1f), relativeYFromView(0f));                    path.lineTo(relativeXFromView(1f), relativeYFromView(1f));                    path.lineTo(relativeXFromView(0f), relativeYFromView(1f));                    path.close();                    mAnimPercent = 0;                    canvas.drawPath(path, mPaint);                }                break;        }    }    private float relativeXFromView(float percent) {        return getWidth() * percent;    }    private float relativeYFromView(float percent) {        return getHeight() * percent;    }    public void changeShape() {        mIsLoading = true;        invalidate();    public enum Shape {        SHAPE_TRIANGLE, SHAPE_RECT, SHAPE_CIRCLE    }    @Override    public void setVisibility(int visibility) {        super.setVisibility(visibility);        if(visibility==VISIBLE){            invalidate();        }    }    public Shape getShape() {        return mShape;    }}

这个开源项目我们学习的两个主要知识
1.使用path绘制图形
2.ObjectAnimation&AnimatorSet
看了这个项目是不是可以用这两个知识点做一个自己喜欢的动画?
just do it.

版权声明:本文为博主原创文章,未经博主允许不得转载。