Android开发动画篇—动画过程中监控帧数,实现布局替换

333 阅读3分钟

监控帧数代码

java代码

import android.os.Bundle;
import android.view.Choreographer;
import android.view.View;
import android.view.animation.ScaleAnimation;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;

import androidx.appcompat.app.AppCompatActivity;

import com.example.myandroid.R;

public class FrameAnimateTest extends AppCompatActivity {
    private FrameLayout parentLayout;
    private RelativeLayout blueLayout;
    private RelativeLayout redLayout;
    private int currentFrameCount = 0;
    private static final int TARGET_FRAME_RED = 30;
    private static final int TARGET_FRAME_BLUE = 200;

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

        parentLayout = findViewById(R.id.parentLayout);
        blueLayout = findViewById(R.id.blueLayout);
        redLayout = findViewById(R.id.redLayout);

        startFrameMonitor();
        startScalingAnimation();
    }

    private void startFrameMonitor() {
        Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
            @Override
            public void doFrame(long frameTimeNanos) {
                currentFrameCount++;
                if (currentFrameCount == TARGET_FRAME_RED) {
                    switchToRedLayout();
                } else if (currentFrameCount == TARGET_FRAME_BLUE) {
                    switchToBlueLayout();
                }
                // Continue monitoring the frames
                Choreographer.getInstance().postFrameCallback(this);
            }
        });
    }

    private void switchToRedLayout() {
        blueLayout.setVisibility(View.GONE);
        redLayout.setVisibility(View.VISIBLE);
    }

    private void switchToBlueLayout() {
        redLayout.setVisibility(View.GONE);
        blueLayout.setVisibility(View.VISIBLE);
    }

    private void startScalingAnimation() {
        ScaleAnimation scaleAnimation = new ScaleAnimation(
                1.0f, 6.0f, 1.0f, 6.0f,  // Scale from 50dp to 300dp
                ScaleAnimation.RELATIVE_TO_SELF, 0.5f,
                ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
        scaleAnimation.setDuration(10000); // Animate over 10 seconds
        scaleAnimation.setFillAfter(true);
        parentLayout.startAnimation(scaleAnimation);
    }
}

xml代码

<?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">

    <FrameLayout
        android:id="@+id/parentLayout"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_gravity="center"
        android:background="#CCCCCC">

        <RelativeLayout
            android:id="@+id/blueLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#0000FF" />

        <RelativeLayout
            android:id="@+id/redLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#FF0000"
            android:visibility="gone" />
    </FrameLayout>
</FrameLayout>

讲解

在一个 Android Activity 中实现了一个动画效果,即一个父布局逐渐从 50dp 放大到 300dp,同时在特定帧数时切换子布局的可见性。从蓝色到红色再回到蓝色。

重要部分讲解

1. 布局文件 activity_main.xml

这个布局文件定义了一个 FrameLayout,作为我们的父布局,用于进行缩放动画。父布局中有两个子布局,一个蓝色 (blueLayout),一个红色 (redLayout):

<FrameLayout
    android:id="@+id/parentLayout"
    android:layout_width="50dp"
    android:layout_height="50dp"
    android:layout_gravity="center"
    android:background="#CCCCCC">

    <RelativeLayout
        android:id="@+id/blueLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#0000FF" />

    <RelativeLayout
        android:id="@+id/redLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#FF0000"
        android:visibility="gone" />
</FrameLayout>

**

  • FrameLayout 充当父布局,设置初始宽度和高度为 50dp。
  • 子布局 blueLayout 默认为可见 (visibility="visible"),redLayout 初始为隐藏 (visibility="gone"),此时只显示蓝色布局。

2. MainActivity.java

onCreate 方法

当 Activity 被创建时,初始化布局并启动动画和帧监控器。

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

    parentLayout = findViewById(R.id.parentLayout);
    blueLayout = findViewById(R.id.blueLayout);
    redLayout = findViewById(R.id.redLayout);

    startFrameMonitor();
    startScalingAnimation();
}

**

  • setContentView(R.layout.activity_main):加载布局文件。
  • findViewById(...):获取布局组件实例。
  • startFrameMonitor():启动帧监控。
  • startScalingAnimation():启动放大动画。
startScalingAnimation 方法
private void startScalingAnimation() {
    ScaleAnimation scaleAnimation = new ScaleAnimation(
            1.0f, 6.0f, 1.0f, 6.0f,  // Scale from 50dp to 300dp
            ScaleAnimation.RELATIVE_TO_SELF, 0.5f,
            ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
    scaleAnimation.setDuration(10000); // Animate over 10 seconds
    scaleAnimation.setFillAfter(true);
    parentLayout.startAnimation(scaleAnimation);
}

**

  • 缩放动画:创建 ScaleAnimation,定义从缩放倍数 1.0f 到 6.0f,表示从初始尺寸放大到 6 倍(50dp 到 300dp)。
  • 动画中心点:动画的中心点是布局自身的中心 (RELATIVE_TO_SELF, 0.5f)。
  • 动画时长:持续时间为10秒 (10,000 毫秒)。
  • 动画结束时保持最终状态:设置 scaleAnimation.setFillAfter(true),以确保动画结束后保持最后的状态(即,放大后的尺寸)。
startFrameMonitor 方法
private void startFrameMonitor() {
    Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
        @Override
        public void doFrame(long frameTimeNanos) {
            currentFrameCount++;
            if (currentFrameCount == TARGET_FRAME_RED) {
                switchToRedLayout();
            } else if (currentFrameCount == TARGET_FRAME_BLUE) {
                switchToBlueLayout();
            }
            // Continue monitoring the frames
            Choreographer.getInstance().postFrameCallback(this);
        }
    });
}

**

  • 帧监控:使用 Choreographer 进行帧回调,每次回调自增 currentFrameCount

  • 布局切换

    • 在第 30 帧时,调用 switchToRedLayout() 切换到红色布局。
    • 在第 200 帧时,调用 switchToBlueLayout() 切换回蓝色布局。
  • 继续监测: 每一帧监测后再次设置回调,以便继续监测下一帧。

switchToRedLayout 和 switchToBlueLayout 方法

这些方法负责在特定帧数时切换布局的可见性:

private void switchToRedLayout() {
    blueLayout.setVisibility(View.GONE);
    redLayout.setVisibility(View.VISIBLE);
}

private void switchToBlueLayout() {
    redLayout.setVisibility(View.GONE);
    blueLayout.setVisibility(View.VISIBLE);
}

**

  • switchToRedLayout:隐藏蓝色布局,显示红色布局。
  • switchToBlueLayout:隐藏红色布局,显示蓝色布局。

总结

整个程序通过一种简单而有效的方式,在Java中使用 ScaleAnimation 结合 Choreographer 进行帧监控,以便在动画执行到指定帧时切换子布局的可见性。这种方法确保了在复杂动画中能够精确掌控视觉元素的切换时间点。