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

Android 属性动画 满足一般项目中的需求

程序员文章站 2022-05-29 19:37:49
...
轻轻的我走了, 
正如我轻轻的来;
我轻轻的招手,
作别西天的云彩。
开头全靠吼啊,下笔如有神啊~

支持原创,也不算原创了哈哈~

本文链接

废话不多说,本文是属性动画的入门,基础篇。

写此博客加深一下自己的印象,也为之后的工作行个方便。

先上个效果图:

Android 属性动画 满足一般项目中的需求


ok,说下属性动画和补间动画区别:

    本人理解很简单: 补间动画实现的只是效果,布局的位置并没有改变,

如果你把view从顶部移动到了底部,在底部是点击不到这个view的。 属性动画移动的是属性值,可以点击。而且属性动画的可定制化比较强,缺点是没有补间动画自然。

代码:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button btnAlpha,btnScaleX,btnRotation,btnTranslate;
    private Button btnScaleRotation,btnRotationTrans,btnAlphaRepeat,btnTransRepeat;
    private Button btnColor,btnJiBu;
    private StepArcView strepView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
    }

    private void initView(){
        btnAlpha = findViewById(R.id.btn_alpha);
        btnAlpha.setOnClickListener(this);
        btnScaleX = findViewById(R.id.btn_scaleX);
        btnScaleX.setOnClickListener(this);
        btnRotation = findViewById(R.id.btn_rotation);
        btnRotation.setOnClickListener(this);
        btnTranslate = findViewById(R.id.btn_translate);
        btnTranslate.setOnClickListener(this);
        btnScaleRotation = findViewById(R.id.btn_scale_rotation);
        btnScaleRotation.setOnClickListener(this);
        btnRotationTrans = findViewById(R.id.btn_romation_trans);
        btnRotationTrans.setOnClickListener(this);
        btnAlphaRepeat = findViewById(R.id.btn_alpha_repeat);
        btnAlphaRepeat.setOnClickListener(this);
        btnTransRepeat = findViewById(R.id.btn_translate_repeat);
        btnTransRepeat.setOnClickListener(this);
        btnColor = findViewById(R.id.btn_color);
        btnColor.setOnClickListener(this);
        btnJiBu = findViewById(R.id.btn_jibu);
        btnJiBu.setOnClickListener(this);

        strepView = findViewById(R.id.step_view);


        final ImageView ivSca = findViewById(R.id.ivSca);
        ivSca.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ScaleAnimation animation =
                        new ScaleAnimation(1f,1.5f,1f,1.5f, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
                animation.setRepeatCount(-1);
                animation.setDuration(600);
                animation.setInterpolator(new DecelerateInterpolator());
                ivSca.startAnimation(animation);
            }
        });
    }



    /**
     * ObjectAnimator是ValueAnimator的子类
     * 第一个参数:所要作用的目标控件
     * 第二个参数:所要操作该控件的属性值
     * 第三个参数:所要操作的属性的开始值
     * 第四个参数:所要操作属性的结束值
     */
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_alpha:
                //透明度
                ObjectAnimator anim = ObjectAnimator.ofFloat(btnAlpha, "alpha", 0f, 1f);
                anim.setDuration(1500);
                anim.start();
                break;
            case R.id.btn_scaleX:
                /**动画组合**/
                //PropertyValuesHolder 将动画属性和值暂时的存储起来,后一起执行
                PropertyValuesHolder holderX = PropertyValuesHolder.ofFloat("scaleX", 0f, 1f);
                PropertyValuesHolder holderY= PropertyValuesHolder.ofFloat("scaleY", 0f, 1f);
                /**同时播放两个动画**/
                ObjectAnimator.ofPropertyValuesHolder(btnScaleX, holderX, holderY).setDuration(1500).start();
                break;
            case R.id.btn_rotation:
                /**旋转**/
                ObjectAnimator rota = ObjectAnimator.ofFloat(btnRotation,"rotationX", 0f, 360f);
                rota.setDuration(1500);
                rota.start();
                break;
            case R.id.btn_translate:
                //平移
                ObjectAnimator trans = ObjectAnimator.ofFloat(btnTranslate,"translationY", 100f, 0f);
                trans.setDuration(1500);
                trans.start();
                break;
            case R.id.btn_scale_rotation:
                /**动画组合**/
                AnimatorSet animatorSetGroup1 = new AnimatorSet();
                ObjectAnimator objectAnimatorScaleX1 = ObjectAnimator.ofFloat(btnScaleRotation, "scaleX", 0f, 1f);
                ObjectAnimator objectAnimatorScaleY1 = ObjectAnimator.ofFloat(btnScaleRotation, "scaleY", 0f, 1f);
                ObjectAnimator objectAnimatorRotateX1 = ObjectAnimator.ofFloat(btnScaleRotation, "rotationX", 0f, 360f);
                ObjectAnimator objectAnimatorRotateY1 = ObjectAnimator.ofFloat(btnScaleRotation, "rotationY", 0f, 360f);
                animatorSetGroup1.setDuration(1500);
                animatorSetGroup1.play(objectAnimatorScaleX1).with(objectAnimatorScaleY1)
                        .before(objectAnimatorRotateX1).before(objectAnimatorRotateY1);
                animatorSetGroup1.start();
                break;
            case R.id.btn_romation_trans:
                AnimatorSet animatorSetGroup2 = new AnimatorSet();
                ObjectAnimator objectAnimatorTranslate2 = ObjectAnimator.ofFloat(btnRotationTrans, "translationX", 150f, 0f);
                ObjectAnimator objectAnimatorRotateX2 = ObjectAnimator.ofFloat(btnRotationTrans, "rotationX", 0f, 360f);
                ObjectAnimator objectAnimatorRotateY2 = ObjectAnimator.ofFloat(btnRotationTrans, "rotationY", 0f, 360f);
                animatorSetGroup2.setDuration(1500);
                animatorSetGroup2.play(objectAnimatorTranslate2).after(objectAnimatorRotateX2)
                        .after(objectAnimatorRotateY2);
                animatorSetGroup2.start();
                break;
            case R.id.btn_alpha_repeat:
                ObjectAnimator alphaRepeat = ObjectAnimator.ofFloat(btnAlphaRepeat, "alpha", 0f, 1f);
                alphaRepeat.setDuration(150);
                alphaRepeat.setRepeatCount(8);
                alphaRepeat.start();
                break;
            case R.id.btn_translate_repeat:
                ObjectAnimator transRepeat = ObjectAnimator.ofFloat(btnTransRepeat, "translationX", -10f, 10f);
                transRepeat.setDuration(150);
                transRepeat.setRepeatCount(8);
                transRepeat.start();
                transRepeat.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        ObjectAnimator translation = ObjectAnimator.ofFloat(btnTransRepeat, "translationX", 10f,0f);
                        translation.setDuration(150);
                        translation.start();
                        super.onAnimationEnd(animation);
                    }
                });
                break;
            case R.id.btn_color:
                ObjectAnimator colorAnim = ObjectAnimator.ofInt(btnColor, "backgroundColor", Color.BLUE, Color.YELLOW, Color.RED);
                colorAnim.setDuration(3000);
                colorAnim.start();
                break;
            case R.id.btn_jibu:
                strepView.setCurrentCount(7000, 1000);
                break;

        }
    }
}

xml:

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.lgoutech.animationstudy.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        >
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="透明度"
            android:id="@+id/btn_alpha"
            />
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="缩放"
            android:id="@+id/btn_scaleX"
            />
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="旋转"
            android:id="@+id/btn_rotation"
            />
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="位移"
            android:id="@+id/btn_translate"
            />
    </LinearLayout>


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        >
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="缩放后旋转"
            android:id="@+id/btn_scale_rotation"
            />
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="旋转后位移"
            android:id="@+id/btn_romation_trans"
            />
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="闪烁"
            android:id="@+id/btn_alpha_repeat"
            />
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="抖动"
            android:id="@+id/btn_translate_repeat"
            />
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        >
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="颜色"
            android:id="@+id/btn_color"
            />
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="计步"
            android:id="@+id/btn_jibu"
            />
    </LinearLayout>
    <com.lgoutech.animationstudy.qqyundong.StepArcView
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:id="@+id/step_view"
        />


    <ImageView
        android:layout_width="200dp"
        android:layout_height="50dp"
        android:src="@mipmap/ic_launcher"
        android:scaleType="centerCrop"
        android:layout_gravity="center"
        android:id="@+id/ivSca"
        />

</LinearLayout>

计步器自定义:

public class StepArcView extends View {

    /**
     * 圆弧的宽度
     */
    private float borderWidth = 38f;
    /**
     * 画步数的数值的字体大小
     */
    private float numberTextSize = 0;
    /**
     * 步数
     */
    private String stepNumber = "0";
    /**
     * 开始绘制圆弧的角度
     */
    private float startAngle = 135;
    /**
     * 终点对应的角度和起始点对应的角度的夹角
     */
    private float angleLength = 270;
    /**
     * 所要绘制的当前步数的红色圆弧终点到起点的夹角
     */
    private float currentAngleLength = 0;
    /**
     * 动画时长
     */
    private int animationLength = 3000;

    public StepArcView(Context context) {
        super(context);


    }

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

    public StepArcView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        /**中心点的x坐标*/
        float centerX = (getWidth()) / 2;
        /**指定圆弧的外轮廓矩形区域*/
        RectF rectF = new RectF(0 + borderWidth, borderWidth, 2 * centerX - borderWidth, 2 * centerX - borderWidth);

        /**【第一步】绘制整体的黄色圆弧*/
        drawArcYellow(canvas, rectF);
        /**【第二步】绘制当前进度的红色圆弧*/
        drawArcRed(canvas, rectF);
        /**【第三步】绘制当前进度的红色数字*/
        drawTextNumber(canvas, centerX);
        /**【第四步】绘制"步数"的红色数字*/
        drawTextStepString(canvas, centerX);
    }

    /**
     * 1.绘制总步数的黄色圆弧
     *
     * @param canvas 画笔
     * @param rectF  参考的矩形
     */
    private void drawArcYellow(Canvas canvas, RectF rectF) {
        Paint paint = new Paint();
        /** 默认画笔颜色,黄色 */
        paint.setColor(Color.YELLOW);
        /** 结合处为圆弧*/
        paint.setStrokeJoin(Paint.Join.ROUND);
        /** 设置画笔的样式 Paint.Cap.Round ,Cap.SQUARE等分别为圆形、方形*/
        paint.setStrokeCap(Paint.Cap.ROUND);
        /** 设置画笔的填充样式 Paint.Style.FILL  :填充内部;Paint.Style.FILL_AND_STROKE  :填充内部和描边;  Paint.Style.STROKE  :仅描边*/
        paint.setStyle(Paint.Style.STROKE);
        /**抗锯齿功能*/
        paint.setAntiAlias(true);
        /**设置画笔宽度*/
        paint.setStrokeWidth(borderWidth);

        /**绘制圆弧的方法
         * drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)//画弧,
         参数一是RectF对象,一个矩形区域椭圆形的界限用于定义在形状、大小、电弧,
         参数二是起始角(度)在电弧的开始,圆弧起始角度,单位为度。
         参数三圆弧扫过的角度,顺时针方向,单位为度,从右中间开始为零度。
         参数四是如果这是true(真)的话,在绘制圆弧时将圆心包括在内,通常用来绘制扇形;如果它是false(假)这将是一个弧线,
         参数五是Paint对象;
         */
        canvas.drawArc(rectF, startAngle, angleLength, false, paint);

    }

    /**
     * 2.绘制当前步数的红色圆弧
     */
    private void drawArcRed(Canvas canvas, RectF rectF) {
        Paint paintCurrent = new Paint();
        paintCurrent.setStrokeJoin(Paint.Join.ROUND);
        paintCurrent.setStrokeCap(Paint.Cap.ROUND);//圆角弧度
        paintCurrent.setStyle(Paint.Style.STROKE);//设置填充样式
        paintCurrent.setAntiAlias(true);//抗锯齿功能
        paintCurrent.setStrokeWidth(borderWidth);//设置画笔宽度
        paintCurrent.setColor(Color.RED);//设置画笔颜色
        canvas.drawArc(rectF, startAngle, currentAngleLength, false, paintCurrent);
    }

    /**
     * 3.圆环中心的步数
     */
    private void drawTextNumber(Canvas canvas, float centerX) {
        Paint vTextPaint = new Paint();
        vTextPaint.setTextAlign(Paint.Align.CENTER);
        vTextPaint.setAntiAlias(true);//抗锯齿功能
        vTextPaint.setTextSize(numberTextSize);
        Typeface font = Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
        vTextPaint.setTypeface(font);//字体风格
        vTextPaint.setColor(Color.RED);
        Rect bounds_Number = new Rect();
        vTextPaint.getTextBounds(stepNumber, 0, stepNumber.length(), bounds_Number);
        canvas.drawText(stepNumber, centerX, getHeight() / 2 + bounds_Number.height() / 2, vTextPaint);

    }

    /**
     * 4.圆环中心[步数]的文字
     */
    private void drawTextStepString(Canvas canvas, float centerX) {
        Paint vTextPaint = new Paint();
        vTextPaint.setTextSize(dipToPx(16));
        vTextPaint.setTextAlign(Paint.Align.CENTER);
        vTextPaint.setAntiAlias(true);//抗锯齿功能
        vTextPaint.setColor(Color.BLUE);
        String stepString = "步数";
        Rect bounds = new Rect();
        vTextPaint.getTextBounds(stepString, 0, stepString.length(), bounds);
        canvas.drawText(stepString, centerX, getHeight() / 2 + bounds.height() + getFontHeight(numberTextSize), vTextPaint);
    }

    /**
     * 获取当前步数的数字的高度
     *
     * @param fontSize 字体大小
     * @return 字体高度
     */
    public int getFontHeight(float fontSize) {
        Paint paint = new Paint();
        paint.setTextSize(fontSize);
        Rect bounds_Number = new Rect();
        paint.getTextBounds(stepNumber, 0, stepNumber.length(), bounds_Number);
        return bounds_Number.height();
    }

    /**
     * dip 转换成px
     *
     * @param dip
     * @return
     */

    private int dipToPx(float dip) {
        float density = getContext().getResources().getDisplayMetrics().density;
        return (int) (dip * density + 0.5f * (dip >= 0 ? 1 : -1));
    }

    /**
     * 所走的步数进度
     *
     * @param totalStepNum  设置的步数
     * @param currentCounts 所走步数
     */
    @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
    public void setCurrentCount(int totalStepNum, int currentCounts) {
        stepNumber = currentCounts + "";
        setTextSize(currentCounts);
        /**如果当前走的步数超过总步数则圆弧还是270度,不能成为园*/
        if (currentCounts > totalStepNum) {
            currentCounts = totalStepNum;
        }
        /**所走步数占用总共步数的百分比*/
        float scale = (float) currentCounts / totalStepNum;
        /**换算成弧度最后要到达的角度的长度-->弧长*/
        float currentAngleLength = scale * angleLength;
        /**开始执行动画*/
        setAnimation(0, currentAngleLength, animationLength);
    }

    /**
     * 为进度设置动画
     * ValueAnimator是整个属性动画机制当中最核心的一个类,属性动画的运行机制是通过不断地对值进行操作来实现的,
     * 而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。
     * 它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,
     * 我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,
     * 那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。
     *
     * @param last
     * @param current
     */
    @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
    private void setAnimation(float last, float current, int length) {
        ValueAnimator progressAnimator = ValueAnimator.ofFloat(last, current);
        progressAnimator.setDuration(length);
        progressAnimator.setTarget(currentAngleLength);
        progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                currentAngleLength = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        progressAnimator.start();
    }

    /**
     * 设置文本大小,防止步数特别大之后放不下,将字体大小动态设置
     *
     * @param num
     */
    public void setTextSize(int num) {
        String s = String.valueOf(num);
        int length = s.length();
        if (length <= 4) {
            numberTextSize = dipToPx(50);
        } else if (length > 4 && length <= 6) {
            numberTextSize = dipToPx(40);
        } else if (length > 6 && length <= 8) {
            numberTextSize = dipToPx(30);
        } else if (length > 8) {
            numberTextSize = dipToPx(25);
        }
    }

}
代码:链接地址