简单分析下 Google 最近的开源播放器

项目地址
Google 最近开源了一款播放器,android-UniversalMusicPlayer,可以在 phones, tablets, Auto, Wear and Cast devices 提供一致的用户体验。
而且项目不算复杂,再加上编译起来很简单,就是 Android Studio 直接导入就可以了,很适合我这种新手学习。

总体 UI 分析

主页面
播放页面
UI 比较重要的类有 MusicPlayerActivity, PlaceholderActivity, MediaBrowserFragment, FullScreenPlayerActivity, PlaybackControlsFragment。
其中 MusicPlayerActivity, PlaceholderActivity 都是继承 BaseActivity, FullScreenPlayerActivity 是继承 ActionBarCastActivity,而 BaseActivity 是 ActionBarCastActivity 的子类。
ActionBarCastActivity 主要实现了 DrawLayout, Toolbar 之类的功能,而 BaseActivity 在此之上又添加了与 PlaybackControlsFragment 交互的功能。

DrawerLayout

值得注意的是,这里的 DrawerLayout 是在不同的 Activity 之中进行切换,而不是在 fragment 之间。主要实现的方法主要是通过继承一个含有 DrawLayout 的 Activity,在这里即是 ActionBarCastActivity。可以参考 该问题
还有一个比较奇特的效果是,DrawerLayout 原本被 status bar 和 actionbar 挡住的部分可以显示出来了。
DrawLayout
具体做法分析可以参考源码和文章 如何将 DrawerLayout 显示在 ActionBar/Toolbar 和 status bar 之间

Animation

Drawble Animation

Playing Animation
在播放时 listview 图标会有变动的效果。这个主要是在 MediaItemViewHolder.java 通过 Drawble Animation 实现的。
官方文档
主要实现代码:

1
2
3
4
5
AnimationDrawable animation = (AnimationDrawable)activity.getDrawable(R.drawable.ic_equalizer_white_36dp);
holder.mImageView.setImageDrawable(animation);
holder.mImageView.setImageTintList(sColorStatePlaying);
holder.mImageView.setVisibility(View.VISIBLE);
animation.start();

1
2
3
4
5
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
<item android:drawable="@drawable/ic_equalizer1_white_36dp" android:duration="200"/>
<item android:drawable="@drawable/ic_equalizer2_white_36dp" android:duration="200"/>
<item android:drawable="@drawable/ic_equalizer3_white_36dp" android:duration="200"/>
</animation-list>

切换 Drawer Activity Fragment 的动画效果

ActionBarCastActivity.java

1
2
3
Bundle extras = ActivityOptions.makeCustomAnimation(ActionBarCastActivity.this, R.anim.fade_in, R.anim.fade_out).toBundle();
Class activityClass = mDrawerMenuContents.getActivity(position);
startActivity(new Intent(ActionBarCastActivity.this, activityClass), extras);

MusicPlayerActivity.java

1
2
3
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setCustomAnimations(R.animator.slide_in_from_right, R.animator.slide_out_to_left, R.animator.slide_in_from_left, R.animator.slide_out_to_right);
transaction.replace(R.id.container, fragment);

小技巧

tools:xxx

在显示布局时,TextView 可能需要文字填充来看看效果,但是又嫌调试后删去 android:text=”…”麻烦,就可以用 tools:text=”…”。
其中 tools:xxx 在用来预览界面方面还是很方便的。比如在看 activity_player.xml 预览时会提示 fragment 可能动态布局,请选择一个布局进行显示。进行选择后,就会在 fragment 里添加 tools:layout=”@layout/fragment_playback_controls”,这样在预览时就可以看到 PlaybackControlsFragment 的具体布局,但对运行时又没有影响。
关于 tools:xxx 的用法可参考 http://tools.android.com/tech-docs/tools-attributes。

android:tint

在播放器里,像播放按钮一共有两种颜色,白色和蓝色。我们可以使用两个不同的图标来完成这种效果,也可以通过 android:tint 来使图标简单地变色,有点像 ps 里添加蒙版的感觉。
xml 里的用法是 android:tint,可参考 fragment_playback_controls.xml 里的 ImageButton。
Java 里的用法 setImageTintList(),可参考 MediaItemViewHolder.java 里相关代码。

1
2
sColorStateNotPlaying = ColorStateList.valueOf(ctx.getResources().getColor(R.color.media_item_icon_not_playing));
holder.mImageView.setImageTintList(sColorStatePlaying);