3

由于版权原因,我们将不提供任何音频文件。建议你自己选择一个 可用且有趣的音频文件,并添加到自己的 Music Player 应用中。

提示:

  • 在 MainActivity 中使 MediaPlayer 对象成为全局变量。
  • MediaPlayer 教程

在 Android 中提供资源(包括对“raw”文件夹的解释)

4

MediaPlayback 文章

Android 支持的媒体格式

5

MediaPlayer 文档展示了完整的状态机图表

6

Developer 开发推广大使 Ian Lake “正确的 Media Playback” - Big Android BBQ 演讲 | Youtube

 7

在这节课中,我想暂停一下,并为你提供两种选择,让你自己选择一种冒险方式。

第 1 种选择:

如果你想挑战下自己,可以跳过接下来的几个视频,并自己修改应用,最终点击每个单词的 时候,应用能播放正确的音频文件。你可以点击 此链接,下载所有单词的音频文件。暂时不需要修改用户界面。

完成后,请继续进入第 4 节课的“Andrea 采访”部分

第 2 种选择:

如果你还没法自己构建该功能,也完全不用担心。我们预料到多数学员都需要和我们一起 接受挑战,完成这节课。我们将逐步完成任务,同时让编程任务对你来说充满挑战性。 请继续观看吧!

10

下载 number_one.mp3 文件

提示: 首先,当你点击列表项时,弹出提示消息。然后修改代码,点击列表项时 能够播放音频文件。

练习完成前后的差异。

表情符号来自 http://emojione.com

11

下载剩余的音频文件

将音频文件添加到应用的 res/raw 目录下面,应该如下所示:

res/raw/color_black.mp3
res/raw/color_brown.mp3
等等...

练习完成前后的差异。


表情符号来自 http://emojione.com

 12

onItemClick() 方法的参考文档。

onItemClick() 方法的参考文档。

代码的当前状态

如果你跳过了之前的几个视频,独立完成挑战,欢迎回来!此时,当你点击 每个单词的时候,你的应用应该能播放相应的音频文件。恭喜你能够使功能正常运转! 你可以将你的代码与我们在 GitHub 上的最新版本的代码进行对比。

调试提示

在继续创建应用的其他功能之前,我想介绍一条快速调试提示(如果你尚未 听说过的话)

你可以使用日志语句来了解任何 Java 对象的状态。最简单的方式是实现 toString() 方法, 并输出 Java 对象的内容。该方法旨在将整个对象当做字符串,通常用于调试目的。

Word 类示例

我来演示下 Word 对象示例。在 Word 类中,重写 toString 方法。

1) 将光标放在文件的空白处(即在现有方法之外,但依然位于该类中)。

查看示例:

 

 

2) 然后使用键盘快捷键自动“生成一个方法”(Getter、Setter、 构造函数、toString 等等)”。在 Windows 上,键盘快捷键是 ALT + Insert。在 Mac 上,键盘快捷键是 CMD + N。

3) 你应该会看到下面这个弹出对话框。选择 toString()。

 

 

4) 点击“确定”接受默认的选项。

5) Android Studio 将自动生成这个 toString() 方法并将其添加到你的 Word 类中。 此方法连结了各种变量和文本,以便将 Word 类中的所有变量都输出为字符串。你可以看到, 该方法的返回值是一个字符串。

/**
     * Returns the string representation of the {@link Word} object.
     */
     @Override
      public String toString() { return "Word{" + "mDefaultTranslation='" + mDefaultTranslation + '\'' + ", mMiwokTranslation='" + mMiwokTranslation + '\'' + ", mAudioResourceId=" + mAudioResourceId + ", mImageResourceId=" + mImageResourceId + '}'; }

6) 一旦你具有了 Word 对象,例如位于 OnItemClickListener 的 onItemClick() 方法中,就可以向日志中输出 Word 对象了。

 

// Set a click listener to play the audio when the list item is clicked onlistView.setOnItemClickListener( new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) { // Get the {@link Word} object at the given position the user clicked on Word word = words.get(position); Log.v("NumbersActivity", "Current word: " + word); // Create and setup the {@link MediaPlayer} for the audio resource associated with the current word mMediaPlayer = MediaPlayer.create(PhrasesActivity.this, word.getAudioResourceId()); // Start the audio file mMediaPlayer.start(); } } );

注意: 如果你(使用“+”运算符)将字符串与 Word 对象相连结,那么 Java 将 在后台对该对象调用 toString() 方法。意味着,下面这两个语句是相同的:

 

Log.v("NumbersActivity", "Current word: " + word);

Log.v("NumbersActivity", "Current word: " + word.toString());

7) 接着,在 Android logcat 中,你将看到类似于下面的输出内容。 我用绿色字体颜色标出了日志标记 (NumbersActivity),并用紫色字体颜色 标出了 toString() 的返回值。

 

 

总结下,我们刚刚介绍了如何重写任何类中的 toString() 方法,这是可选内容。 但是,却是很有用的练习过程。因为在未来,你可能想要在日志中输出某个对象的当前状态(而不是在 Android Studio 中使用逐步调试器)。

16

提示: 仅在调用 mediaPlayer.start() 方法之后再设置该回调,否则将无法触发该回调。

 17

代码段:

用来释放媒体播放器资源的辅助方法。确保你的类别 Activity 文件中具有一个全局 MediaPlayer 变量,叫做“mMediaPlayer”,以便使代码能运行。

其他资源:

  • 关于 MediaPlayer release() 方法的文档。
  • MediaPlayer 状态图表。

练习完成前后的差异。

表情符号来自 http://emojione.com

20

我们演示过的生命周期示例应用的 MainActivity.java 代码

21

提示: MediaPlayer release() 方法

练习完成前后的差异。

22

请略读以下文章并回答练习题中的问题。

  • 如何正确地处理音频中断(作者:开发者推广大使 Joanna Smith)
  • 管理 Audio Focus

23

在这门课程中,我们不会创建自己的服务,但是你可以点击此处,详细了解 Android 中的服务。

暂时只需了解 AudioManager 是一种系统服务即可。你可以像对待任何其他 Java 类一样与其互动。

 24

要了解可能的 streamType 值列表,请参阅 AudioManager 类中名称以“STREAM_”开头的常量。

要了解可能的 durationHint 值列表,请参阅 requestAudioFocus 方法的参数说明部分的常量。

提示: 它们都应该是 AudioManager 类的整型常量 (全部为大写字母)

 25

要了解可能的 streamType 值列表,请参阅 AudioManager 类中名称以“STREAM_”开头的常量。

要了解可能的 durationHint 值列表,请参阅 requestAudioFocus 方法的参数说明部分的常量。

提示: 它们都应该是 AudioManager 类的整型常量 (全部为大写字母)

 26

之前的测试题的解决方案:

  • “了解 Audio Focus”测试题的解决方案
  • “请求 Audio Focus”测试题的解决方案
  • “Audio Focus 状态”测试题的解决方案

文章:

  • 如何正确地处理音频中断
  • 管理 Audio Focus

参考文档:

  • AudioManager
  • OnAudioFocusStateChangeListener

(可选)中途检查点:在尝试完成第 2 项任务后, 如果你想对照我们的实现来检查下你的应用,请参阅此处关于 OnAudioFocusChangeListener 的代码。

练习完成前后的差异。

表情符号来自 http://emojione.com

27

从材料设计网站下载 播放箭头图标 white 36dp 版本请注意向应用中添加所有密度版本的图标(从 mdpi 到 xxxhdpi)。

练习完成前后的差异。

29

触摸反馈在屏幕上用户与 UI 元素互动的接触点为用户提供了即时外观确认。 你在 Android 中开发的应用一定要具有触摸反馈。这样可以让你的应用 看起来响应速度很快,即时尚未发生任何其他行为。

自从在 Lollipop 中推出材料设计后,当你与 UI 元素互动的时候, 都会出现圆形涟漪动画效果。

 

 

在更早版本的 Android 设备上,出现的是静态彩色按下状态。

 

 

在上一门课程中,当我们制作 Just Java 应用时,我们使用了 Android 框架中的标准按钮。当你触摸这些按钮的时候,会出现标准反馈。

 

 

在 Miwok 应用中,我们创建了自己的可点击视图——MainActivity 中的类别视图 以及类别 Activity 中的列表项。我们需要自己处理按下状态。

 

 

将试图设为 ?android:attr/selectableItemBackground 背景后, 该视图默认地将具有一个透明背景。当你触摸或按下该视图时,它将显示一个 按下状态(即圆形涟漪动画效果)。这是 Android 框架中定义的默认触摸反馈行为。

修改 Miwok 应用

在上一道练习题中,你修改了 Numbers 类别 TextView,使其具有可点击项背景颜色。 但是我们失去了背景颜色。

1) 在这个编程任务中,请将 activity_main.xml 中的内容替换为下面的 XML。 我们还在此 代码片段 中提供了相关代码。每个 TextView 都位于一个 FrameLayout 中,并具有背景颜色,请将 TextView 的背景颜色设为 ?android:attr/selectableItemBackground 。

这样,每个类别视图都能保留背景颜色,以及触摸反馈。 顺便提下,FrameLayout 是一种 ViewGroup,通常包含 1 个子视图。

在 activity_numbers.xml 文件中

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
  xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/tan_background" android:orientation="vertical" tools:context="com.example.android.miwok.MainActivity"> <!-- Numbers category --> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/category_numbers"> <TextView android:id="@+id/numbers" style="@style/CategoryStyle" android:background="?android:attr/selectableItemBackground" android:text="@string/category_numbers" /> </FrameLayout> <!-- Family category --> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/category_family"> <TextView android:id="@+id/family" style="@style/CategoryStyle" android:background="?android:attr/selectableItemBackground" android:text="@string/category_family" /> </FrameLayout> <!-- Colors category --> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/category_colors"> <TextView android:id="@+id/colors" style="@style/CategoryStyle" android:background="?android:attr/selectableItemBackground" android:text="@string/category_colors" /> </FrameLayout> <!-- Phrases category --> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/category_phrases"> <TextView android:id="@+id/phrases" style="@style/CategoryStyle" android:background="?android:attr/selectableItemBackground" android:text="@string/category_phrases" /> </FrameLayout> </LinearLayout>

2) 然后在设备上运行应用,测试并确保 MainActivity 中的每个按钮 都具有触摸反馈。

注意: 你可以将背景属性移到 styles.xml 中的CategoryStyle 定义中, 而我直接在这里的行内定义了,使你更容易添加到你的应用中。

更多资源

我们向你显示了在 MainActivity 中添加触摸反馈的简单方式,避免过于深入得讲解一个新的概念而跑题(例如状态列表 drawable 和为了向后兼容旧版设备 而管理资源)。但是,当前的方法的确向视图层级结构中引入了更多视图,因此效率更低。

如果你想了解利用自定义 drawable 设置触摸反馈的更适当、高效方式,请观看中级优达学城 Android 应用开发课程。或者,你可以 参阅这个关于涟漪动画的 Codepath 教程

30

现在,你需要对所有的类别 Activity(NumbersActivity、FamilyActivity 等等) 进行最后的更改,当列表项被点击时能够显示按下状态。

第 1 个选项

在列表项布局中,你可以添加一个新的视图并覆盖 RelativeLayout 中的所有其他视图, 并与该 RelativeLayout 的宽度和高度相匹配。该视图的背景为 "?android:attr/selectableItemBackground”,这样默认情况下,该视图将为透明的 使你能够看到列表项中的内容。当列表项被点击时,它将显示按下状态(在 Lollipop 及更改版本的设备上显示灰色涟漪动画效果)。

修改后的 list_item.xml 文件的 代码片段

第 2 个选项

这次,不再向布局中添加新的视图,而是在单词列表布局中,对 ListView XML 元素添加 android:drawSelectorOnTop="true"属性。做出这行代码更改后,每个列表项都将显示按下状态。

在 word_list.xml 中:

<?xml version="1.0" encoding="utf-8"?>
<ListView 
   xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/list" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:drawSelectorOnTop="true"/>

两个选项都是可行的。在做出这一更改后,测试下应用。到此第 4 节课就学完了!