7步精通Material Design核心动画:从Google I/O 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场景转场动画
- 状态与导航栏着色
- 路径追踪动画效果
环境准备与项目构建
开发环境要求
- 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关系
自定义View组件
项目包含多个自定义View组件,实现了Material Design的核心视觉效果:
- AnimatedPathView:实现SVG路径动画,用于星形收藏按钮
- CardFrameLayout:卡片布局容器,支持阴影和轮廓
- SvgHelper:SVG路径解析工具类
- 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转场动画
- 在主题中启用转场动画
- 使用
ActivityOptions.makeSceneTransitionAnimation创建转场 - 实现共享元素转场
步骤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的实现方式。
性能优化与最佳实践
动画性能优化
- 使用硬件加速:确保在Manifest中启用硬件加速
<application
android:hardwareAccelerated="true">
-
避免过度绘制:通过Hierarchy Viewer检查并优化视图层级
-
使用属性动画:优先使用ObjectAnimator而非View动画
-
后台加载资源:如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最佳实践
-
遵循材质属性:确保阴影、 elevation和Z轴顺序正确
-
合理使用调色板:从内容中提取颜色,保持UI一致性
-
有意义的动画:动画应具有目的性,避免无意义的动画效果
-
响应式交互:所有可交互元素都应有状态反馈
总结与扩展学习
通过对Google I/O 2014 Material Witness项目的深入分析,我们掌握了Material Design核心动画技术的实现方法,包括转场动画、圆形揭露、路径动画和动态调色板等关键技术。这些技术虽然是在2014年发布的,但至今仍是Android UI开发的基础。
进一步学习资源
-
官方文档:
-
高级主题:
- 自定义转场动画
- 动画状态列表(AnimationStateList)
- 共享元素转场进阶
-
相关库:
项目改进建议
- 适配现代Android版本:将项目迁移至AndroidX
- 添加深色主题支持:实现深色/浅色主题切换
- 优化图片加载:使用Glide或Picasso替代原生加载
- 添加动画插值器选择:允许用户选择不同动画效果
掌握这些核心动画技术后,你可以为自己的Android应用打造出专业级的Material Design用户界面,提升应用的视觉吸引力和用户体验。记住,优秀的动画应该是自然、有意义且不干扰用户的——它应该让用户界面感觉更加流畅和直观,而不是成为注意力的焦点。
希望本文能够帮助你更好地理解和应用Material Design动画技术。如有任何问题或建议,欢迎在评论区留言讨论。
如果觉得本文对你有帮助,请点赞、收藏并关注作者,获取更多Android开发优质内容!
下一篇预告:《Jetpack Compose中的Material Design 3动画完全指南》
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



