zoukankan      html  css  js  c++  java
  • 第 13 章 使用 MediaPlayer 播放音频

    请参考教材,全面理解和完成本章节内容... ...

    接下来的三章,我们先暂停“陋习手记”应用的开发,转而开发另一个应用。使用MediaPlayer类,新应用可支持播放一段历史事件的音频文件,如图13-1所示。

    image

    图13-1 你好,月球!

    MediaPlayer是一个支持音频及视频文件播放的Android类,可播放不同来源(本地或网络流媒体)、多种格式(如WAV、MP3、Ogg Vorbis、MPEG-4以及3GPP)的多媒体文件。

    创建一个名为HelloMoon的项目。在新应用向导对话框中,修改PackageName为你的名字缩写,如图13-2所示。

    image

    图13-2 创建HelloMoon应用

    单击Next按钮继续。

    HelloMoon的项目Minimu SDK为API 16以上,如图13-2-1所示。

    image

    图13-2-1 选择Minimu SDK为API 16以上

    单击Next按钮继续。最后选择使用 Blank Activity模板,并通过模板创建一个名为HelloMoonActivity的activity。

    打开HelloMoonActivity.java文件, 将继承的超类改为FragmentActivity。如下所示:

    public class HelloMoonActivity extends FragmentActivity { ... ...}

    13.1 添加资源

      HelloMoon应用需要一个图片文件以及一个音频文件。这两个文件包含在配套资源文件中,下载Ch13文件包后,找到它们:

    • armstrong_on_moon.jpg
    • one_small_step.wav
    • apollo_17_stroll.mpg

    HelloMoon是个简单的小应用,按照Android屏幕密度基准(~160dpi),我们仅创建了一个符合该基准的图片文件armstrong_on_moon.jpg。将其复制至drawable目录下。

    音频文件one_small_step将会放置在res aw目录下,但项目中的res aw目录并非默认存在,因此必须手工添加它。(右键单击res目录,选择New → Directory菜单项。有时新建目录后,不显示,切换到Project模式后再切换回Android模式)然后将one_small_step.wav文件复制到新建的res aw目录下。目录raw负责存放那些不需要Android编译系统特别处理的各类文件。也可顺便将apollo_17_stroll.mpg复制到res aw目录下。本章末尾的挑战练习将会用到它。

    最后,打开res/values/strings.xml,对照代码清单13-1,添加HelloMoon应用所需的字符串资源。

    代码清单13-1 添加字符串资源(strings.xml)

    image

    (HelloMoon应用为什么在按钮上使用“Play”和“Stop”字符串资源而非图标文件?第15章我们将学习应用本地化的相关内容。使用图标文件会增加本地化的难度。)

    准备完必需的资源文件,下面我们来看看HelloMoon应用的整体设计图,如图13-3所示。

    image

    图13-3 HelloMoon应用的对象图解

    图中可以看出,HelloMoon应用包含一个HelloMoonActivity及其托管的HelloMoonFragment

    AudioPlayer是我们编写的类,用于封装MediaPlayer类。也可选择不封装MediaPlayer类,而让HelloMoonFragment直接与MediaPlayer进行交互。不过,为保持代码的整洁与独立,我们推荐封装MediaPlayer类的设计。

    创建AudioPlayer类之前,先来完成应用开发的其他部分。经过前几章的学习,我们应该已经掌握了以下基本的应用开发步骤:

    • 定义fragment的布局
    • 创建fragment类
    • 修改activity及其布局,实现对fragment的托管

    13.2 定义 HelloMoonFragment 布局文件

    TableLayout为根元素,新建一个fragment_hello_moon.xml的布局文件。新建后,参照图13-4,重新设计fragment_hello_moon.xml布局文件的定义。

    image

    图13-4 HelloMoon应用的布局图解

    TableLayout使用起来和LinearLayout差不多。可使用TableRow,而非嵌套使用的LinearLayout,来布置组件。联合使用TableLayoutTableRow,可更容易地布置形成排列整齐的视图。在HelloMoon应用里,TableLayout能够协助将两个按钮并排放置到同样大小的两列中。

    为什么没有将ImageView也放入TableRow中呢?TableRow子组件的行为方式类似于表里的单元格。这里我们希望让ImageView占据整个屏幕。如果将ImageView也定义为TableRow的子组件,则TableLayout也会让列中其他单元格占据整个屏幕。而作为TableLayout的直接子组件,ImageView可自由地按需配置显示,完全不影响两个按钮以等宽的两列并排显示。

    注意,TableRow组件无需声明高度和宽度的属性定义。实际上,它使用的是TableLayout的高度和宽度属性定义及其所有其他属性定义。不过,从另一个角度来看,嵌套的LinearLayout可以更灵活地布置并显示组件。

    在图形化工具中预览布局,现在应用背景使用浅白主题。但为了让界面酷一些,我们希望使用Holo Dark主题。接下来,我们来看看如何使用Holo Dark主题。

    手动重置应用主题

    在Android Studio中,我还没有找到合适的方法加入黑酷的“Holo Dark”主题,暂时忽略“手动重置应用主题”这件事!

    13.3 创建 HelloMoonFragment

    创建名为HelloMoonFragment的类,其超类为android.support.v4.app.Fragment。

    覆盖HelloMoonFragment.onCreateView(...)方法,实例化布局文件,并引用按钮,如代码清单13-3所示。

    代码清单13-3 初步配置HelloMoonFragment类(HelloMoonFragment.java)

    image

    13.4 使用布局 fragment

    在CriminalInten应用中,我们一直是通过在activity代码中添加fragment的方式来实现其托管的。而在HelloMoon应用中,我们将使用布局fragment,即在fragment元素节点中指定fragment的类。

    打开content_hello_moon.xml文件,以代码清单13-4所示的fragment元素替换原有内容。注意android:name改成你自己的名字。

    代码清单13-4 创建布局fragment(content_hello_moon.xml)

    image

    接下来,我们看一下HelloMoonActivity类,教材中 HelloMoonActivity继承了FragmentActivity类,我们改为继承AppCompatActivity,AppCompatActivity也是FragmentActivity的子类,没有什么影响。但Activity继承AppCompatActivity类后,应用会显示工具栏,下一章我们会详细介绍。

    另外,HelloMoonActivity类中没有托管fragment的代码,但setContentView()函数绑定的布局文件activity_hello_moon中指定了fragment类HelloMoonFragment及其布局文件fragment_hello_moonx.xml(实际是在include layout的content_hello_moon中指定的),如代码清单13-5和13-4所示:

    代码清单13-5 HelloMoonActivity继承AppCompatActivity类(HelloMoonActivity.java)

    image

    以上便是托管一个布局fragment的全部操作。总结来说就是,在布局中指定fragment类,随后通过布局它被添加给activity并显示在屏幕上。

    13.5 音频播放

    创建一个名为AudioPlayer的新类,添加存放MediaPlayer实例的成员变量,以及停止和播放该实例的方法,如代码清单13-6所示。

    代码清单13-6 使用MediaPlayer类的简单播放代码(AudioPlayer.java)

    image

    play(Context)方法中,选择调用MediaPlayer.create(Context, int) 方法。MediaPlayer需利用传入的Context来寻找音频文件的资源ID。(如果音频来自于其他渠道,如网络或本地URI,则应使用其他MediaPlayer.create(...)方法。)

    AudioPlayer.stop()方法中,释放MediaPlayer实例并将mPlayer变量设置为null。调用MediaPlayer.release()方法,可销毁该实例。

    销毁是“停止”的一种具有攻击意味的说法,但我们有充足的理由使用销毁一词。除非调用MediaPlayer.release()方法,否则MediaPlayer将一直占用着音频解码硬件及其他系统资源。而这些资源是由所有应用共享的。MediaPlayer类有一个stop()方法。该方法可使MediaPlayer实例进入停止状态,等需要时再重新启动。不过,对于简单的音频播放应用,建议使用release()方法销毁实例,并在需要时进行重建。

    基于以上原因,有一个简单可循的规则:只保留一个MediaPlayer实例,保留的时长即音频文件播放的时长。

    为强化该规则,我们来修改play(Context)方法。初始调用一个stop()方法,再设置一个监听器监听音频播放,音频文件播放一完成,就调用stop()方法。

    为了在HelloMoonFragment类获取当前的播放状态,我们添加isPlaying()方法获取mPlayer状态,如代码清单13-7所示。

    代码清单13-7 避免多MediaPlayer实例(AudioPlayer.java)

    image

    play(Context)方法的开头就调用stop()方法,可避免用户多次单击Play按钮创建多个MediaPlayer实例的情况发生。音频文件完成播放后,立即调用stop()方法,可尽可能快地释放MediaPlayer实例及其占用的资源。

    现在我在完善HelloMoonFragment类,因为fragment被销毁后,还需在HelloMoonFragment中调用AudioPlayer.stop()方法,以避免fragment被销毁后MediaPlayer还在不停播放。

    HelloMoonFragment中,覆盖onDestroy()方法,从而实现对AudioPlayer.stop()方法的调用,如代码清单13-8所示。

    代码清单13-8 覆盖onDestroy()方法(HelloMoonFragment.java)

    image

    HelloMoonFragment被销毁后,MediaPlayer仍可不停地播放,这是因为MediaPlayer运行在一个不同的线程上。我们会在其他章中学习到更多有关线程管理的知识,所以这里暂不对HelloMoon应用的多线程使用进行介绍。

    关联并设置playstop按钮

    返回HelloMoonFragment.java,创建AudioPlayer实例并设置play和stop按钮的监听器方法,即可实现音频的播放。另外,还需根据播放状态调整play按钮状态,我们用updateButtons()方法来更新按钮状态。如代码清单13-9所示。

    代码清单13-9 关联并设置Play按钮(HelloMoonFragment.java)

    image

    运行HelloMoon应用。单击Play按钮,欣赏一段历史事件记录音频。

  • 相关阅读:
    C语言I博客作业06
    C语言I博客作业05
    评分标准
    语言I博客作业04
    C语言I博客作业03
    C语言I博客作业02
    C语言I博客作业10
    第十三周助教总结
    C语言I博客作业09
    第十二周助教总结
  • 原文地址:https://www.cnblogs.com/jlxuqiang/p/4756183.html
Copyright © 2011-2022 走看看