7步精通Material Design核心动画:从Google I/O 2014实例学起

7步精通Material Design核心动画:从Google I/O 2014实例学起

【免费下载链接】google-io-2014 Demo for the Material Witness talk I gave at Google I/O. 【免费下载链接】google-io-2014 项目地址: https://gitcode.com/gh_mirrors/go/google-io-2014

你是否还在为Android动画效果生硬、界面交互缺乏质感而困扰?是否想掌握Material Design中那些令人惊艳的转场动画和视觉效果却不知从何入手?本文将通过解析Google I/O 2014官方示例项目"Material Witness",带你从零开始掌握五大核心动画技术,最终实现具有专业级视觉体验的Android应用。

读完本文你将获得:

  • 完整的Material Design动画实现路径
  • 5个核心动画效果的源代码级解析
  • 调色板动态提取与主题适配方案
  • Activity转场动画的最佳实践
  • 自定义View的动画性能优化技巧

项目概述:Material Witness是什么?

Material Witness是Google在2014年I/O大会上发布的官方示例项目,由Android开发专家Romain Guy亲自操刀,旨在展示Material Design设计规范下的前沿UI/UX实现方式。该项目作为"Material Witness"主题演讲的配套代码,完整呈现了Android Lollipop引入的全新动画API和设计理念。

项目主要技术亮点包括:

  • 自定义主题色彩系统
  • 动态调色板(Palette)技术
  • 圆形揭露动画(Circular Reveal)
  • Activity场景转场动画
  • 状态与导航栏着色
  • 路径追踪动画效果

mermaid

环境准备与项目构建

开发环境要求

  • Android Studio 1.0或更高版本
  • Android SDK API 21 (Lollipop)或更高
  • Gradle构建工具

项目获取与构建

# 克隆项目仓库
git clone https://gitcode.com/gh_mirrors/go/google-io-2014.git

# 进入项目目录
cd google-io-2014

# 使用Gradle构建项目
./gradlew build

项目结构采用标准Android应用架构,核心代码位于app/src/main/java/com/example/android/io2014/目录,资源文件位于app/src/main/res/目录。

核心动画技术解析

1. Activity场景转场动画

Material Design引入的转场动画框架允许开发者定义Activity之间的平滑过渡效果。在Material Witness项目中,MainActivity通过ActivityOptions.makeSceneTransitionAnimation实现了图片元素的共享转场:

// MainActivity.java
public void showPhoto(View view) {
    // ... 省略Intent配置代码 ...
    
    ImageView hero = (ImageView) ((View) view.getParent()).findViewById(R.id.photo);
    
    // 缓存图片用于转场
    sPhotoCache.put(intent.getIntExtra("photo", -1),
            ((BitmapDrawable) hero.getDrawable()).getBitmap());
    
    // 创建场景转场动画,共享"photo_hero"元素
    ActivityOptions options =
            ActivityOptions.makeSceneTransitionAnimation(this, hero, "photo_hero");
    startActivity(intent, options.toBundle());
}

转场动画的配置需要在主题中启用:

<!-- res/values/styles.xml -->
<style name="AppTheme" parent="android:Theme.Material.Light.DarkActionBar">
    <item name="android:windowContentTransitions">true</item>
    <item name="android:windowAllowEnterTransitionOverlap">true</item>
    <item name="android:windowAllowReturnTransitionOverlap">true</item>
    <item name="android:windowSharedElementEnterTransition">
        @transition/change_image_transform
    </item>
</style>

DetailActivity在转场结束后执行后续动画:

// DetailActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
    // ... 省略其他初始化代码 ...
    
    getWindow().getEnterTransition().addListener(new TransitionAdapter() {
        @Override
        public void onTransitionEnd(Transition transition) {
            // 转场结束后执行图片淡化效果
            ImageView hero = (ImageView) findViewById(R.id.photo);
            ObjectAnimator color = ObjectAnimator.ofArgb(hero.getDrawable(), "tint",
                    getResources().getColor(R.color.photo_tint), 0);
            color.start();

            // 显示按钮
            findViewById(R.id.info).animate().alpha(1.0f);
            findViewById(R.id.star).animate().alpha(1.0f);

            getWindow().getEnterTransition().removeListener(this);
        }
    });
}

2. 圆形揭露动画(Circular Reveal)

圆形揭露动画是Material Design中最具代表性的动画效果之一,通过ViewAnimationUtils.createCircularReveal实现。在项目中,点击信息按钮时展示信息面板的动画就是典型应用:

// DetailActivity.java
private void toggleInformationView(View view) {
    final View infoContainer = findViewById(R.id.information_container);

    // 计算动画起始点(按钮中心)
    int cx = (view.getLeft() + view.getRight()) / 2;
    int cy = (view.getTop() + view.getBottom()) / 2;
    
    // 计算动画半径(容器对角线的一半)
    float radius = Math.max(infoContainer.getWidth(), infoContainer.getHeight()) * 2.0f;

    Animator reveal;
    if (infoContainer.getVisibility() == View.INVISIBLE) {
        // 显示容器并执行揭露动画
        infoContainer.setVisibility(View.VISIBLE);
        reveal = ViewAnimationUtils.createCircularReveal(
                infoContainer, cx, cy, 0, radius);
        reveal.setInterpolator(new AccelerateInterpolator(2.0f));
    } else {
        // 执行反向揭露动画并隐藏容器
        reveal = ViewAnimationUtils.createCircularReveal(
                infoContainer, cx, cy, radius, 0);
        reveal.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                infoContainer.setVisibility(View.INVISIBLE);
            }
        });
        reveal.setInterpolator(new DecelerateInterpolator(2.0f));
    }
    reveal.setDuration(600);
    reveal.start();
}

圆形揭露动画参数说明:

参数说明
view执行动画的目标视图
centerX动画圆心X坐标
centerY动画圆心Y坐标
startRadius动画起始半径
endRadius动画结束半径

3. 路径动画(Path Animation)

AnimatedPathView类实现了SVG路径的动画绘制效果,用于实现星形收藏图标的绘制动画:

// AnimatedPathView.java
public void reveal() {
    // 路径绘制动画
    ObjectAnimator svgAnimator = ObjectAnimator.ofFloat(this, "phase", 0.0f, 1.0f);
    svgAnimator.setDuration(mDuration);
    svgAnimator.start();

    // 填充透明度动画
    setFillAlpha(0.0f);
    ObjectAnimator fillAnimator = ObjectAnimator.ofFloat(this, "fillAlpha", 0.0f, 1.0f);
    fillAnimator.setDuration(mFillDuration);
    fillAnimator.setStartDelay(mFillOffset); // 延迟开始填充动画
    fillAnimator.start();
}

SVG路径解析和绘制逻辑:

// AnimatedPathView.java
private void updatePathsPhaseLocked() {
    for (SvgHelper.SvgPath path : mPaths) {
        path.renderPath.reset();
        // 根据当前相位计算路径段
        path.measure.getSegment(0.0f, mPhase * path.length, path.renderPath, true);
    }
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    synchronized (mSvgLock) {
        canvas.save();
        canvas.translate(getPaddingLeft(), getPaddingTop());
        mFillPaint.setAlpha((int) (mFillAlpha * 255.0f));
        for (SvgHelper.SvgPath path : mPaths) {
            // 根据相位计算透明度
            int alpha = (int) (Math.min(mPhase * mFadeFactor, 1.0f) * 255.0f);
            path.paint.setAlpha(alpha);

            // 绘制填充和路径
            canvas.drawPath(path.path, mFillPaint);
            canvas.drawPath(path.renderPath, path.paint);
        }
        canvas.restore();
    }
}

4. 动态调色板(Palette)

Palette类允许从图片中提取主色调,实现UI与内容的视觉协调:

// DetailActivity.java
private void colorize(Bitmap photo) {
    // 从图片生成调色板
    Palette palette = Palette.generate(photo);
    applyPalette(palette);
}

private void applyPalette(Palette palette) {
    // 设置窗口背景色
    getWindow().setBackgroundDrawable(new ColorDrawable(palette.getDarkMutedColor().getRgb()));

    // 设置文本颜色
    TextView titleView = (TextView) findViewById(R.id.title);
    titleView.setTextColor(palette.getVibrantColor().getRgb());

    TextView descriptionView = (TextView) findViewById(R.id.description);
    descriptionView.setTextColor(palette.getLightVibrantColor().getRgb());

    // 设置按钮颜色
    colorRipple(R.id.info, palette.getDarkMutedColor().getRgb(),
            palette.getDarkVibrantColor().getRgb());
    colorRipple(R.id.star, palette.getMutedColor().getRgb(),
            palette.getVibrantColor().getRgb());

    // 设置信息面板背景色
    View infoView = findViewById(R.id.information_container);
    infoView.setBackgroundColor(palette.getLightMutedColor().getRgb());

    // 设置星形动画颜色
    AnimatedPathView star = (AnimatedPathView) findViewById(R.id.star_container);
    star.setFillColor(palette.getVibrantColor().getRgb());
    star.setStrokeColor(palette.getLightVibrantColor().getRgb());
}

Palette提供的颜色提取方法:

方法说明
getVibrantColor()获取鲜艳的颜色
getDarkVibrantColor()获取深鲜艳的颜色
getLightVibrantColor()获取浅鲜艳的颜色
getMutedColor()获取柔和的颜色
getDarkMutedColor()获取深柔和的颜色
getLightMutedColor()获取浅柔和的颜色

5. 自定义View动画

AnimatedPathView是一个完全自定义的View,展示了如何实现复杂的路径动画效果:

// AnimatedPathView.java
public class AnimatedPathView extends View {
    private final Paint mStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private final Paint mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private final SvgHelper mSvg = new SvgHelper(mStrokePaint);
    private List<SvgHelper.SvgPath> mPaths = new ArrayList<SvgHelper.SvgPath>(0);
    private float mPhase; // 路径动画相位
    private float mFillAlpha; // 填充透明度
    
    // ... 省略构造函数和其他方法 ...
    
    public void setPhase(float phase) {
        mPhase = phase;
        synchronized (mSvgLock) {
            updatePathsPhaseLocked();
        }
        invalidate();
    }
    
    public void setFillAlpha(float fillAlpha) {
        mFillAlpha = fillAlpha;
        invalidate();
    }
    
    // ... 省略其他方法 ...
}

项目架构与关键组件

主要Activity关系

mermaid

自定义View组件

项目包含多个自定义View组件,实现了Material Design的核心视觉效果:

  1. AnimatedPathView:实现SVG路径动画,用于星形收藏按钮
  2. CardFrameLayout:卡片布局容器,支持阴影和轮廓
  3. SvgHelper:SVG路径解析工具类
  4. TransitionAdapter:转场动画监听器适配器

资源组织

项目资源组织遵循Android最佳实践:

res/
├── anim/              # 补间动画定义
├── drawable/          # 通用Drawable资源
├── drawable-xxhdpi/   # 高分辨率图片资源
├── layout/            # 布局文件
├── menu/              # 菜单定义
├── raw/               # 原始资源(SVG)
├── transition/        # 转场动画定义
└── values/            # 数值资源
    ├── colors.xml     # 颜色定义
    ├── dimens.xml     # 尺寸定义
    ├── strings.xml    # 字符串资源
    └── attrs.xml      # 自定义View属性

实战应用:构建自己的Material Design应用

基于Material Witness项目的核心技术,我们可以构建自己的Material Design应用。以下是实现步骤:

步骤1:配置开发环境

确保Android Studio版本不低于1.0,并在build.gradle中配置正确的依赖:

android {
    compileSdkVersion 21
    buildToolsVersion "21.1.2"
    
    defaultConfig {
        minSdkVersion 21
        targetSdkVersion 21
    }
}

dependencies {
    implementation 'com.android.support:palette-v7:21.0.3'
}

步骤2:实现Activity转场动画

  1. 在主题中启用转场动画
  2. 使用ActivityOptions.makeSceneTransitionAnimation创建转场
  3. 实现共享元素转场

步骤3:添加圆形揭露效果

public void showRevealAnimation(View view) {
    // 获取目标视图
    final View revealView = findViewById(R.id.reveal_view);
    
    // 计算动画起始点
    int cx = (view.getLeft() + view.getRight()) / 2;
    int cy = (view.getTop() + view.getBottom()) / 2;
    
    // 计算动画半径
    int finalRadius = Math.max(revealView.getWidth(), revealView.getHeight());
    
    // 创建并启动揭露动画
    Animator anim = ViewAnimationUtils.createCircularReveal(
        revealView, cx, cy, 0, finalRadius);
    
    // 设置可见性并启动动画
    revealView.setVisibility(View.VISIBLE);
    anim.start();
}

步骤4:实现动态调色板

public void applyPaletteFromImage(Bitmap bitmap) {
    // 异步生成调色板
    Palette.from(bitmap).generate(new Palette.PaletteAsyncListener() {
        @Override
        public void onGenerated(Palette palette) {
            // 获取各种色调
            Palette.Swatch vibrant = palette.getVibrantSwatch();
            if (vibrant != null) {
                // 应用到UI元素
                toolbar.setBackgroundColor(vibrant.getRgb());
                getWindow().setStatusBarColor(
                    darkenColor(vibrant.getRgb()));
                textView.setTextColor(vibrant.getTitleTextColor());
            }
        }
    });
}

// 辅助方法:加深颜色
private int darkenColor(int color) {
    float[] hsv = new float[3];
    Color.colorToHSV(color, hsv);
    hsv[2] *= 0.8f; // 降低亮度20%
    return Color.HSVToColor(hsv);
}

步骤5:创建自定义动画View

继承View类,实现自定义绘制和动画逻辑,参考AnimatedPathView的实现方式。

性能优化与最佳实践

动画性能优化

  1. 使用硬件加速:确保在Manifest中启用硬件加速
<application 
    android:hardwareAccelerated="true">
  1. 避免过度绘制:通过Hierarchy Viewer检查并优化视图层级

  2. 使用属性动画:优先使用ObjectAnimator而非View动画

  3. 后台加载资源:如AnimatedPathView中使用线程加载SVG资源

// AnimatedPathView.java
@Override
protected void onSizeChanged(final int w, final int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);

    if (mLoader != null) {
        try {
            mLoader.join();
        } catch (InterruptedException e) {
            Log.e(LOG_TAG, "Unexpected error", e);
        }
    }

    // 在后台线程加载SVG资源
    mLoader = new Thread(new Runnable() {
        @Override
        public void run() {
            mSvg.load(getContext(), mSvgResource);
            synchronized (mSvgLock) {
                mPaths = mSvg.getPathsForViewport(
                        w - getPaddingLeft() - getPaddingRight(),
                        h - getPaddingTop() - getPaddingBottom());
                updatePathsPhaseLocked();
            }
        }
    }, "SVG Loader");
    mLoader.start();
}

Material Design最佳实践

  1. 遵循材质属性:确保阴影、 elevation和Z轴顺序正确

  2. 合理使用调色板:从内容中提取颜色,保持UI一致性

  3. 有意义的动画:动画应具有目的性,避免无意义的动画效果

  4. 响应式交互:所有可交互元素都应有状态反馈

总结与扩展学习

通过对Google I/O 2014 Material Witness项目的深入分析,我们掌握了Material Design核心动画技术的实现方法,包括转场动画、圆形揭露、路径动画和动态调色板等关键技术。这些技术虽然是在2014年发布的,但至今仍是Android UI开发的基础。

进一步学习资源

  1. 官方文档

  2. 高级主题

    • 自定义转场动画
    • 动画状态列表(AnimationStateList)
    • 共享元素转场进阶
  3. 相关库

项目改进建议

  1. 适配现代Android版本:将项目迁移至AndroidX
  2. 添加深色主题支持:实现深色/浅色主题切换
  3. 优化图片加载:使用Glide或Picasso替代原生加载
  4. 添加动画插值器选择:允许用户选择不同动画效果

掌握这些核心动画技术后,你可以为自己的Android应用打造出专业级的Material Design用户界面,提升应用的视觉吸引力和用户体验。记住,优秀的动画应该是自然、有意义且不干扰用户的——它应该让用户界面感觉更加流畅和直观,而不是成为注意力的焦点。

希望本文能够帮助你更好地理解和应用Material Design动画技术。如有任何问题或建议,欢迎在评论区留言讨论。

如果觉得本文对你有帮助,请点赞、收藏并关注作者,获取更多Android开发优质内容!

下一篇预告:《Jetpack Compose中的Material Design 3动画完全指南》

【免费下载链接】google-io-2014 Demo for the Material Witness talk I gave at Google I/O. 【免费下载链接】google-io-2014 项目地址: https://gitcode.com/gh_mirrors/go/google-io-2014

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

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

抵扣说明:

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

余额充值