ExpectAnim 开源项目使用教程:声明式动画编程新范式

ExpectAnim 开源项目使用教程:声明式动画编程新范式

【免费下载链接】ExpectAnim Describe your animation and run ! 【免费下载链接】ExpectAnim 项目地址: https://gitcode.com/gh_mirrors/ex/ExpectAnim

还在为 Android 动画的复杂实现而头疼吗?ExpectAnim 提供了一个革命性的解决方案——通过声明式语法描述动画期望,让复杂的多视图动画变得简单直观。本文将带你全面掌握这个强大的动画库。

什么是 ExpectAnim?

ExpectAnim 是一个创新的 Android 动画库,采用声明式编程范式(Declarative Programming Paradigm)。与传统命令式动画不同,你只需描述视图的最终状态期望,库会自动计算并执行所有中间动画步骤。

核心优势

  • 声明式语法:描述"期望什么"而非"如何实现"
  • 多视图协同:自动处理视图间的依赖关系
  • 流畅性能:基于 Android 原生动画系统
  • 丰富特性:支持位置、缩放、旋转、透明度等全方位动画

快速开始

添加依赖

在项目的 build.gradle 中添加依赖:

dependencies {
    implementation 'com.github.florent37:expectanim:1.0.8'
}

基础使用示例

new ExpectAnim()
    .expect(avatarView)
    .toBe(
        bottomOfParent().withMarginDp(16),
        leftOfParent().withMarginDp(16),
        width(40).toDp().keepRatio()
    )
    .toAnimation()
    .setDuration(1500)
    .start();

核心概念详解

1. 动画期望(Expectations)

ExpectAnim 提供了丰富的期望类型,可以分为以下几类:

位置相关期望
// 相对其他视图
toRightOf(otherView).withMarginDp(16)
toLeftOf(otherView)
aboveOf(otherView)
belowOf(otherView)

// 相对父容器
topOfParent().withMarginDp(20)
bottomOfParent()
leftOfParent()
rightOfParent()

// 居中相关
centerInParent(true, true)  // 水平和垂直居中
centerHorizontalInParent()
centerVerticalInParent()
sameCenterAs(otherView, true, false)  // 水平中心对齐

// 屏幕外
outOfScreen(Gravity.BOTTOM)  // 移出屏幕底部
变换相关期望
// 缩放
scale(0.5f, 0.5f)  // 缩放50%
width(100).toDp()   // 设置宽度
height(200)         // 设置高度
sameScaleAs(otherView)
sameWidthAs(otherView)

// 旋转
rotated(45f)        // 旋转45度
flippedHorizontally()  // 水平翻转
flippedVertically()    // 垂直翻转
withCameraDistance(1000f)  // 3D透视效果

// 透明度
alpha(0.5f)         // 半透明
visible()           // 完全显示
invisible()         // 完全隐藏
自定义属性期望
toHaveTextColor(Color.RED)        // 文字颜色动画
toHaveBackgroundAlpha(0.5f)       // 背景透明度

2. 链式调用与多视图动画

ExpectAnim 的强大之处在于可以同时处理多个视图的复杂动画:

new ExpectAnim()
    .expect(avatar)
    .toBe(
        bottomOfParent().withMarginDp(36),
        leftOfParent().withMarginDp(16),
        width(40).toDp().keepRatio()
    )
    .expect(username)
    .toBe(
        toRightOf(avatar).withMarginDp(16),
        sameCenterVerticalAs(avatar),
        toHaveTextColor(Color.WHITE)
    )
    .expect(button)
    .toBe(
        rightOfParent().withMarginDp(20),
        bottomOfParent().withMarginDp(12),
        toHaveBackgroundAlpha(0f)
    )
    .toAnimation()
    .setDuration(1500)
    .start();

高级特性

1. 滚动联动动画

ExpectAnim 完美支持与 ScrollView 的联动,创建流畅的视差效果:

private ExpectAnim expectAnimMove;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_scroll);
    
    expectAnimMove = new ExpectAnim()
        .expect(headerImage)
        .toBe(
            scale(0.7f, 0.7f),
            topOfParent().withMarginDp(10)
        )
        .expect(title)
        .toBe(
            alpha(0.8f),
            sameCenterVerticalAs(headerImage)
        )
        .toAnimation();

    scrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
        @Override
        public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, 
                                 int oldScrollX, int oldScrollY) {
            final float percent = (scrollY * 1f) / v.getMaxScrollAmount();
            expectAnimMove.setPercent(percent);
        }
    });
}

2. 动画序列(Concat)

使用 ExpectAnim.concat() 创建连续的动画序列:

ExpectAnim.concat(
    new ExpectAnim()
        .expect(view1)
        .toBe(
            withCameraDistance(500f),
            flippedHorizontally()
        )
        .toAnimation()
        .setDuration(1000),
    new ExpectAnim()
        .expect(view2)
        .toBe(
            withCameraDistance(1000f),
            flippedVertically()
        )
        .toAnimation()
        .setDuration(500)
).start();

3. 即时应用与重置

// 立即应用动画最终状态(无动画效果)
new ExpectAnim()
    .expect(view)
    .toBe(outOfScreen(Gravity.BOTTOM))
    .toAnimation()
    .setNow();

// 重置到初始状态
expectAnim.reset();

实战案例

案例1:用户资料卡片动画

public class ProfileActivity extends AppCompatActivity {

    @BindView(R.id.avatar) View avatar;
    @BindView(R.id.name) View name;
    @BindView(R.id.bio) View bio;
    @BindView(R.id.follow_btn) View followBtn;
    
    private ExpectAnim profileAnim;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_profile);
        ButterKnife.bind(this);

        // 初始化状态
        new ExpectAnim()
            .expect(avatar).toBe(scale(0.8f, 0.8f), alpha(0f))
            .expect(name).toBe(alpha(0f), translatedX(100f))
            .expect(bio).toBe(alpha(0f), translatedY(50f))
            .expect(followBtn).toBe(alpha(0f), scale(0.5f, 0.5f))
            .toAnimation()
            .setNow();

        profileAnim = new ExpectAnim()
            .expect(avatar)
            .toBe(
                atItsOriginalScale(),
                alpha(1f),
                centerHorizontalInParent()
            )
            .expect(name)
            .toBe(
                atItsOriginalPosition(),
                alpha(1f),
                belowOf(avatar).withMarginDp(16)
            )
            .expect(bio)
            .toBe(
                atItsOriginalPosition(),
                alpha(1f),
                belowOf(name).withMarginDp(8)
            )
            .expect(followBtn)
            .toBe(
                atItsOriginalScale(),
                alpha(1f),
                belowOf(bio).withMarginDp(24),
                centerHorizontalInParent()
            )
            .toAnimation()
            .setDuration(1200)
            .setInterpolator(new OvershootInterpolator());
    }

    @OnClick(R.id.show_profile)
    public void showProfile() {
        profileAnim.start();
    }
}

案例2:购物车添加动画

public class ProductActivity extends AppCompatActivity {

    @BindView(R.id.product_image) View productImage;
    @BindView(R.id.cart_icon) View cartIcon;
    
    public void onAddToCartClicked() {
        // 创建商品飞到购物车的动画
        new ExpectAnim()
            .expect(productImage)
            .toBe(
                sameCenterAs(cartIcon, true, true),
                scale(0.3f, 0.3f),
                alpha(0f)
            )
            .toAnimation()
            .setDuration(800)
            .addEndListener(new AnimationEndListener() {
                @Override
                public void onAnimationEnd(ExpectAnim expectAnim) {
                    // 动画结束后恢复原状
                    productImage.setAlpha(1f);
                    productImage.setScaleX(1f);
                    productImage.setScaleY(1f);
                    productImage.setTranslationX(0);
                    productImage.setTranslationY(0);
                    
                    // 显示添加成功提示
                    showAddSuccess();
                }
            })
            .start();
    }
}

性能优化建议

1. 避免过度使用

// 不推荐:每个小变化都创建新动画
view.setOnClickListener(v -> {
    new ExpectAnim().expect(v).toBe(scale(1.1f, 1.1f)).toAnimation().start();
});

// 推荐:复用动画实例
private ExpectAnim scaleAnim;

void initAnimations() {
    scaleAnim = new ExpectAnim()
        .expect(button)
        .toBe(scale(1.1f, 1.1f))
        .toAnimation()
        .setDuration(200);
}

void onButtonClick() {
    scaleAnim.reset();
    scaleAnim.start();
}

2. 合理设置持续时间

// 根据动画复杂度设置合适的持续时间
new ExpectAnim()
    .expect(view1).toBe(moveToPosition())
    .expect(view2).toBe(scaleChange())
    .expect(view3).toBe(rotation())
    .toAnimation()
    .setDuration(800)  // 复杂动画适当延长
    .start();

3. 使用合适的插值器

new ExpectAnim()
    .expect(view)
    .toBe(/* expectations */)
    .toAnimation()
    .setInterpolator(new AccelerateDecelerateInterpolator())  // 平滑加速减速
    .setDuration(600)
    .start();

常见问题解答

Q1: 如何处理视图依赖关系?

ExpectAnim 自动处理视图间的依赖关系,你只需按逻辑顺序声明期望即可。

Q2: 支持自定义属性动画吗?

是的,可以通过扩展 CustomAnimExpectation 类来实现自定义属性动画。

Q3: 如何监听动画状态?

expectAnim.addStartListener(expectAnim -> {
    // 动画开始
});

expectAnim.addEndListener(expectAnim -> {
    // 动画结束
});

Q4: 支持 Proguard 混淆吗?

在 proguard-rules.pro 中添加:

-keep class com.github.florent37.expectanim.*{ *; }
-dontwarn com.github.florent37.expectanim.**

总结

ExpectAnim 通过声明式编程范式彻底改变了 Android 动画的开发方式:

  1. 极简语法:用描述代替命令,代码更清晰
  2. 强大功能:支持复杂的多视图协同动画
  3. 优秀性能:基于原生动画系统,流畅稳定
  4. 丰富生态:提供全面的期望类型和扩展能力

无论你是要创建简单的视图过渡,还是复杂的交互动画,ExpectAnim 都能提供优雅的解决方案。开始使用这个强大的库,让你的应用动画变得更加出色!

提示:在实际项目中,建议先从简单的动画开始,逐步掌握复杂的多视图动画技巧。合理规划动画逻辑,避免过度动画影响用户体验。

【免费下载链接】ExpectAnim Describe your animation and run ! 【免费下载链接】ExpectAnim 项目地址: https://gitcode.com/gh_mirrors/ex/ExpectAnim

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值