Android 动画实例1_星星

ObjectAnimator 实现基本动画操作

使得界面的切换更协调

public final class ObjectAnimator extends ValueAnimator {

目录

 

1. 旋转

2. 移动

3.伸缩

4.淡入、淡出

5.颜色

6.组合

7. 完整代码

ic_star.xml (drawable)

strings.xml

dimens.xml

colors.xml

activity_main.xml

MainActivity.kt


1. 旋转

    // 旋转, 顺时针从-360 到0, 原始状态是0
    private fun rotater() {
        val animator = ObjectAnimator.ofFloat(star, View.ROTATION, -360f, 0f)
        animator.duration = 1000
        // 监听动画的开始 和 结束,
        // AnimatorListenerAdapter抽象类,实现了AnimatorListener 和AnimatorPauseListener, 覆写需要的即可
        animator.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationStart(animation: Animator?) {
                rotateButton.isEnabled = false
            }

            override fun onAnimationEnd(animation: Animator?) {
                rotateButton.isEnabled = true
            }
        })
        animator.start()
    }

监听动画状态可以写成扩展函数,如:

    // 写成 ObjectAnimator 的拓展函数, 监听动画的开始和结束,控制控件的点击状态
    // AnimatorListenerAdapter抽象类,实现了AnimatorListener 和AnimatorPauseListener, 覆写需要的即可
    private fun ObjectAnimator.disableViewDuringAnimation(view: View) {
        addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationStart(animation: Animator?) {
                view.isEnabled = false
            }

            override fun onAnimationEnd(animation: Animator?) {
                view.isEnabled = true
            }
        })
    }

2. 移动

    // 移动
    private fun translater() {
        val animator = ObjectAnimator.ofFloat(star, View.TRANSLATION_X, 200f)
        animator.repeatCount = 1
        animator.repeatMode = ObjectAnimator.REVERSE // 返回到原点
        animator.disableViewDuringAnimation(translateButton)
        animator.start()
    }

3.伸缩

   //伸缩
    private fun scaler() {
        // PropertyValuesHolder 保持动画处理的一些属性
        val scaleX = PropertyValuesHolder.ofFloat(View.SCALE_X, 4f)
        val scaleY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 4f)
        val animator = ObjectAnimator.ofPropertyValuesHolder(star, scaleX, scaleY)
        animator.repeatCount = 1
        animator.repeatMode = ObjectAnimator.REVERSE
        animator.disableViewDuringAnimation(scaleButton)
        animator.start()
    }

4.淡入、淡出

    // 淡入、淡出
    private fun fader() {
        // 0f  完全透明;  1f 完全不透明
        val animator = ObjectAnimator.ofFloat(star, View.ALPHA, 0f)
        animator.repeatCount = 1
        animator.repeatMode = ObjectAnimator.REVERSE
        animator.disableViewDuringAnimation(fadeButton)
        animator.start()
    }

5.颜色

    // 颜色
    private fun colorizer() {
        // ofArgb 渐变效果比 ofInt 效果好,
        var animator = ObjectAnimator.ofArgb(star.parent,
            "backgroundColor", Color.BLACK, Color.RED)
        animator.duration = 500
        animator.repeatCount = 1
        animator.repeatMode = ObjectAnimator.REVERSE
        animator.disableViewDuringAnimation(colorizeButton)
        animator.start()
    }

6.组合

    private fun shower() {
        // 父容器
        val container = star.parent as ViewGroup
        val containerW = container.width
        val containerH = container.height
        // 原始星星的大小
        var starW: Float = star.width.toFloat()
        var starH: Float = star.height.toFloat()

        //创建新图片并且添加到父容器中
        val newStar = AppCompatImageView(this)
        newStar.setImageResource(R.drawable.ic_star)
        newStar.layoutParams = FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.WRAP_CONTENT,
            FrameLayout.LayoutParams.WRAP_CONTENT
        )
        container.addView(newStar)

        // 新图片的大小(通过伸缩) 和位置(平移)
        newStar.scaleX = Math.random().toFloat() * 1.5f + .1f
        newStar.scaleY = newStar.scaleX
        starW *= newStar.scaleX
        starH *= newStar.scaleY
        newStar.translationX = Math.random().toFloat() * containerW - starW / 2 //左到右,最左或最右一半显示

        // 创建动画器
        val mover = ObjectAnimator.ofFloat(newStar, View.TRANSLATION_Y,
            -starH, containerH + starH) //从顶部到底部 (刚出现到完全消失)
        mover.interpolator = AccelerateInterpolator(1f) //加速插值器 (平移)
        val rotator =
            ObjectAnimator.ofFloat(newStar, View.ROTATION, (Math.random() * 1080).toFloat())
        rotator.interpolator = LinearInterpolator() // 旋转线性插值器

        // AnimatorSet 组合使用
        val set = AnimatorSet()
        set.playTogether(mover, rotator)
        set.duration = (Math.random() * 1500 + 500).toLong()

        // 动画结束时,移除view
        set.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator?) {
                container.removeView(newStar)
            }
        })

        // 启动动画
        set.start()
    }

7. 完整代码

ic_star.xml (drawable)

<!--
  ~ Copyright (C) 2019 The Android Open Source Project
  ~
  ~ Licensed under the Apache License, Version 2.0 (the "License");
  ~ you may not use this file except in compliance with the License.
  ~ You may obtain a copy of the License at
  ~
  ~      http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing, software
  ~ distributed under the License is distributed on an "AS IS" BASIS,
  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License.
  -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:height="24dp" android:width="24dp"
        android:viewportHeight="806.7" android:viewportWidth="807.1"  >
    <path android:fillColor="#FFFF00" android:pathData="M403.2,23.7l123.6,250l275.7,39.8l-199.6,194.3l47.4,275.2l-247.1,-130l-246.4,130l47.4,-275.2l-199.6,-194.3l275.7,-39.8z"/>
</vector>

strings.xml

<resources>
    <string name="app_name">PropertyAnimation</string>
    <string name="rotate">Rotate</string>
    <string name="translate">Translate</string>
    <string name="scale">Scale</string>
    <string name="fade">Fade</string>
    <string name="shower">Shower</string>
</resources>

dimens.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="standard_margin">8dp</dimen>
</resources>

colors.xml

<resources>
    <color name="primaryColor">#66bb6a</color>
    <color name="primaryLightColor">#98ee99</color>
    <color name="primaryDarkColor">#338a3e</color>
    <color name="primaryTextColor">#000000</color>
    <color name="black">#000</color>
</resources>

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ Copyright (C) 2019 The Android Open Source Project
  ~ Licensed under the Apache License, Version 2.0 (the "License");
  ~ you may not use this file except in compliance with the License.
  ~ You may obtain a copy of the License at
  ~      http://www.apache.org/licenses/LICENSE-2.0
  ~ Unless required by applicable law or agreed to in writing, software
  ~ distributed under the License is distributed on an "AS IS" BASIS,
  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License.
  -->

<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

    <Button
            android:text="@string/rotate"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/rotateButton" app:layout_constraintEnd_toStartOf="@+id/translateButton"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:layout_margin="@dimen/standard_margin" android:textSize="12sp"/>

    <Button
            android:text="@string/translate"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/translateButton" app:layout_constraintEnd_toStartOf="@+id/scaleButton"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toEndOf="@+id/rotateButton"
            android:layout_margin="@dimen/standard_margin" android:textSize="12sp"
            android:layout_marginStart="14dp" android:layout_marginTop="16dp"/>

    <Button
            android:text="@string/scale"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/scaleButton"
            app:layout_constraintEnd_toStartOf="@+id/fadeButton"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toEndOf="@+id/translateButton"
            android:layout_margin="@dimen/standard_margin" android:textSize="12sp"
            android:layout_marginStart="15dp" android:layout_marginTop="@dimen/standard_margin"/>

    <Button
            android:text="@string/fade"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/fadeButton" app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toEndOf="@+id/scaleButton"
            android:layout_margin="@dimen/standard_margin" android:textSize="12sp"
            android:layout_marginTop="@dimen/standard_margin" app:layout_constraintEnd_toEndOf="parent"
            android:layout_marginStart="15dp"/>

    <Button
            android:text="@string/shower"
            android:layout_width="0dp"
            android:layout_height="wrap_content" android:id="@+id/showerButton"
            app:layout_constraintTop_toBottomOf="@+id/rotateButton"
            app:layout_constraintStart_toStartOf="@+id/scaleButton"
            android:layout_marginEnd="@dimen/standard_margin"
            android:layout_marginBottom="@dimen/standard_margin"
            app:layout_constraintEnd_toEndOf="parent" />

    <Button
            android:text="Background Color"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            app:layout_constraintEnd_toEndOf="@+id/translateButton"
            app:layout_constraintTop_toBottomOf="@+id/rotateButton"
            android:id="@+id/colorizeButton"
            android:layout_marginStart="@dimen/standard_margin"
            android:layout_marginBottom="@dimen/standard_margin"
            app:layout_constraintStart_toStartOf="parent"/>

    <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:background="@color/black"
            app:layout_constraintTop_toBottomOf="@+id/showerButton"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintVertical_bias="0.0"
            app:layout_constraintStart_toStartOf="parent">

        <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:srcCompat="@drawable/ic_star"
                android:id="@+id/star"
                android:layout_gravity="center"/>

    </FrameLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.kt

/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.samples.propertyanimation

import android.animation.*
import android.graphics.Color
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.view.animation.AccelerateInterpolator
import android.view.animation.LinearInterpolator
import android.widget.Button
import android.widget.FrameLayout
import android.widget.ImageView
import androidx.appcompat.widget.AppCompatImageView


class MainActivity : AppCompatActivity() {

    lateinit var star: ImageView
    lateinit var rotateButton: Button
    lateinit var translateButton: Button
    lateinit var scaleButton: Button
    lateinit var fadeButton: Button
    lateinit var colorizeButton: Button
    lateinit var showerButton: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        star = findViewById(R.id.star)
        rotateButton = findViewById<Button>(R.id.rotateButton)
        translateButton = findViewById<Button>(R.id.translateButton)
        scaleButton = findViewById<Button>(R.id.scaleButton)
        fadeButton = findViewById<Button>(R.id.fadeButton)
        colorizeButton = findViewById<Button>(R.id.colorizeButton)
        showerButton = findViewById<Button>(R.id.showerButton)

        rotateButton.setOnClickListener {
            rotater()
        }

        translateButton.setOnClickListener {
            translater()
        }

        scaleButton.setOnClickListener {
            scaler()
        }

        fadeButton.setOnClickListener {
            fader()
        }

        colorizeButton.setOnClickListener {
            colorizer()
        }

        showerButton.setOnClickListener {
            shower()
        }
    }

    // 写成 ObjectAnimator 的拓展函数, 监听动画的开始和结束,控制控件的点击状态
    // AnimatorListenerAdapter抽象类,实现了AnimatorListener 和AnimatorPauseListener, 覆写需要的即可
    private fun ObjectAnimator.disableViewDuringAnimation(view: View) {
        addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationStart(animation: Animator?) {
                view.isEnabled = false
            }

            override fun onAnimationEnd(animation: Animator?) {
                view.isEnabled = true
            }
        })
    }

    // 旋转, 顺时针从-360 到0, 原始状态是0
    private fun rotater() {
        val animator = ObjectAnimator.ofFloat(star, View.ROTATION, -360f, 0f)
        animator.duration = 1000
        // 监听动画的开始 和 结束,
        // AnimatorListenerAdapter抽象类,实现了AnimatorListener 和AnimatorPauseListener, 覆写需要的即可
        animator.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationStart(animation: Animator?) {
                rotateButton.isEnabled = false
            }

            override fun onAnimationEnd(animation: Animator?) {
                rotateButton.isEnabled = true
            }
        })
        animator.start()
    }

    // 移动
    private fun translater() {
        val animator = ObjectAnimator.ofFloat(star, View.TRANSLATION_X, 200f)
        animator.repeatCount = 1
        animator.repeatMode = ObjectAnimator.REVERSE // 返回到原点
        animator.disableViewDuringAnimation(translateButton)
        animator.start()
    }

    //伸缩
    private fun scaler() {
        // PropertyValuesHolder 保持动画处理的一些属性
        val scaleX = PropertyValuesHolder.ofFloat(View.SCALE_X, 4f)
        val scaleY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 4f)
        val animator = ObjectAnimator.ofPropertyValuesHolder(star, scaleX, scaleY)
        animator.repeatCount = 1
        animator.repeatMode = ObjectAnimator.REVERSE
        animator.disableViewDuringAnimation(scaleButton)
        animator.start()
    }

    // 淡入、淡出
    private fun fader() {
        // 0f  完全透明;  1f 完全不透明
        val animator = ObjectAnimator.ofFloat(star, View.ALPHA, 0f)
        animator.repeatCount = 1
        animator.repeatMode = ObjectAnimator.REVERSE
        animator.disableViewDuringAnimation(fadeButton)
        animator.start()
    }

    // 颜色
    private fun colorizer() {
        // ofArgb 渐变效果比 ofInt 效果好,
        var animator = ObjectAnimator.ofArgb(
            star.parent,
            "backgroundColor", Color.BLACK, Color.RED
        )
        animator.duration = 500
        animator.repeatCount = 1
        animator.repeatMode = ObjectAnimator.REVERSE
        animator.disableViewDuringAnimation(colorizeButton)
        animator.start()
    }

    private fun shower() {
        // 父容器
        val container = star.parent as ViewGroup
        val containerW = container.width
        val containerH = container.height
        // 原始星星的大小
        var starW: Float = star.width.toFloat()
        var starH: Float = star.height.toFloat()

        //创建新图片并且添加到父容器中
        val newStar = AppCompatImageView(this)
        newStar.setImageResource(R.drawable.ic_star)
        newStar.layoutParams = FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.WRAP_CONTENT,
            FrameLayout.LayoutParams.WRAP_CONTENT
        )
        container.addView(newStar)

        // 新图片的大小(通过伸缩) 和位置(平移)
        newStar.scaleX = Math.random().toFloat() * 1.5f + .1f
        newStar.scaleY = newStar.scaleX
        starW *= newStar.scaleX
        starH *= newStar.scaleY
        newStar.translationX = Math.random().toFloat() * containerW - starW / 2 //左到右,最左或最右一半显示

        // 创建动画器
        val mover = ObjectAnimator.ofFloat(newStar, View.TRANSLATION_Y,
            -starH, containerH + starH) //从顶部到底部 (刚出现到完全消失)
        mover.interpolator = AccelerateInterpolator(1f) //加速插值器 (平移)
        val rotator =
            ObjectAnimator.ofFloat(newStar, View.ROTATION, (Math.random() * 1080).toFloat())
        rotator.interpolator = LinearInterpolator() // 旋转线性插值器

        // AnimatorSet 组合使用
        val set = AnimatorSet()
        set.playTogether(mover, rotator)
        set.duration = (Math.random() * 1500 + 500).toLong()

        // 动画结束时,移除view
        set.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator?) {
                container.removeView(newStar)
            }
        })

        // 启动动画
        set.start()
    }

}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值