本章介绍App开发常见的多媒体技术,主要包括如何使用各种图像控件实现自定义相册、如何使用几种主要的音频播放技术、如何使用几种常见的视频播放控件、如何在屏幕上划分多窗口进行特殊处理。
相册
本节介绍自定义相册的实现过程,首先说明使用画廊或循环视图如何实现简单的相册,接着阐述使用图像切换器如何实现相册的左右滑动功能,然后分别介绍卡片视图与调色板的用法,并结合上述图像控件完成一个图片查看器------青青相册。
画廊Gallery
前几章使用文件对话框打开图片时只能看到图片的文件名,看不到图片的缩略图,对用户来说很不方便,因为光看文件名怎么知道这张图片什么模样呢?如果是在电脑上,就可以查看一组图片的缩略图列表,很容易找到想要的图片。在手机上可以使用相应的图像控件做出缩略图展示的相册效果。
画廊Gallery是专门用于展示图片列表的控件,左右滑动手势即可展示内嵌的图片列表,画面效果类似于一个平面万花筒。尽管Android将Gallery标记为Deprecation(表示已废弃),建议开发者采用HorizontalScrollView或ViewPager代替,不过Gallery用来轮播图片是一个挺好的选择。不妨了解一下Gallery控件,并结合其他控件加深对图像开发的理解。
下面是Gallery的常用方法说明。
- setSpacing:设置图片之间的间隔大小,对应的XML属性是spacing。
- setUnselectedAlpha:设置未选定图片的透明度,对应的XML属性是unselectedAlpha。取值范围为0.0~1.0,0.0表示完全透明,1.0表示完全不透明。
- setAdapter:设置画廊的适配器。
- getSelectedItemId:获取当前选中的视图序号。
- setSelection:设置当前选中第几个视图。
- setOnItemClickListener:设置单项的点击监听器。
使用画廊看起来很简单,接下来试着用Gallery结合ImageView实现观看画廊的相册效果。首先在布局文件中放置一个框架布局FrameLayout,里面放一个画廊控件与一个图像视图控件,ImageView设置为充满整个屏幕,Gallery放在屏幕下方;然后监听Gallery控件的单项点击事件,当用户点击指定图片项时,使用ImageView控件填充该图片,也就是点小图看大图。
图像切换器ImageSwitcher
读者可能已经发现,前面Gallery相册在切换大图时比较生硬,前后两张图片闪一下就切过去了,用户体验不够友好。有没有办法让图片切换自然一些呢,比如通过渐变动画的方式?答案肯定是有的,就是把占据整个屏幕的图像视图ImageView换成图像切换器ImageSwitcher,然后通过ImageSwitcher实现前后图片的切换动画。
ImageSwitcher继承自视图动画器ViewAnimator,用于承载前后两个图像的变换动画。
下面介绍ImageSwitcher的常用方法。
- setFactory:设置一个视图工厂。该视图工厂由ViewFactory派生而来,需重写makeView方法返回工厂的具体视图。对于ImageSwitcher来说,工厂返回的是ImageView对象。
- setImageResource:设置当前图像的资源ID。该方法与下面的setImageDrawable方法和setImageURI方法为三选一操作,调用了其中一个方法,就无须调用另外两个方法。
- setImageDrawable:设置当前图像的Drawable对象。
- setImageURI:设置当前图像的URI地址。
- setInAnimation:设置后一个图像的进入动画。
- setOutAnimation:设置前一个图像的退出动画。
音频播放
本节介绍了音频播放的几种方式,首先说明了铃声工具的适用场合与简单用法,接着阐述了声音池的运用场景,它的优缺点,以及基本用法,然后说明了音轨的产生背景,以及如何进行音轨的录制和播放。
铃声Ringtone
在第9章的时候,提到媒体播放器MediaPlayer既可用来播放视频,也可用来播放音频。可在具体的使用场合,MediaPlayer不可避免地存在某些播音方面的不足之处,主要包括:
(1)MediaPlayer的初始化比较消耗资源,尤其是播放短小铃音时反应偏慢。
(2)一个MediaPlayer同时只能播放一个媒体文件,无法同时播放多个声音。
(3)MediaPlayer只能播放已经完成编码的音频文件,无法直接播放原始音频,也不能流式播放(即边录边播)。
以上问题各有不同的解决方案,对第一个问题来说,Android提供了铃声工具Ringtone处理铃音的播放。而铃声对象则是通过铃声管理器RingtoneManager的getRingtone方法来获取,具体而言,铃声管理器允许获得三种来源的铃声,说明如下:
(1)系统自带的铃音,其Uri的获取方式举例如下:
RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE); // 来电铃音
铃声管理器支持的系统铃音类型取值说明见表13-2。
(2)内部存储与SD卡上的铃音文件,其Uri的获取方式举例如下:
Uri.parse("file:///system/media/audio/ui/camera_click.ogg"); // 相机快门声
(3)App工程中res/raw目录下的铃音文件,其Uri的获取方式举例如下:
Uri.parse("android.resource://"+getPackageName()+"/"+R.raw.ring); // 从资源文件中获取铃音
通过铃声管理器获得铃声对象之后,才能进行铃声的播放操作。下面是Ringtone的常用方法说明:
play:开始播放铃声。
stop:停止播放铃声。
isPlaying:判断铃声是否正在播放。
声音池SoundPool
对于MediaPlayer无法同时播放多个声音的问题,Android提供了声音池工具SoundPool,使用声音池即可对多个声音的播放进行调度。
使用SoundPool可以事先加载多个音频,在需要时再播放指定音频,这样有几个好处:
(1)资源占用量小,不像MediaPlayer那么耗资源。
(2)相对MediaPlayer来说,延迟时间非常小。
(3)可以同时播放多个音频,从而实现游戏过程中多个声音叠加的情景。
当然,SoundPool带来方便的同时也做了一部分牺牲,下面是它的一些使用限制:
(1)SoundPool最大只能申请1MB的内存,这意味着它只能播放一些很短的声音片段,不能用于播放歌曲或者游戏背景音乐。
(2)虽然SoundPool提供了pause和stop方法,但是轻易不要使用这两个方法,因为它们可能会让App异常或崩溃。
(3)SoundPool建议播放ogg格式的音频,据说它对Wav格式的支持不太好。
(4)待播放的音频要提前加载进声音池,不要等到要播放的时候才加载,否则可能播放没声音。因为SoundPool不会等音频加载完了才播放,而MediaPlayer会等待加载完毕才播放。
下面是SoundPool的常用方法说明。
- 构造函数:可设置最大音频个数、音频类型、音频质量。其中音频类型一般是AudioManager.STREAM_MUSIC,音频质量取值为0到100。
- load:加载指定的音频文件。返回值为该音频的编号。
- unload:卸载指定编号的音频。
- play:播放指定编号的音频。可同时设置左右声道的音量(取值为0.0到1.0)、优先级(0为最低)、是否循环播放(0为只播放一次,-1为无限循环)、播放速率(取值为0.5-2.0,其中1.0为正常速率)。
- setVolume:设置指定编号音频的音量大小。
- setPriority:设置指定编号音频的优先级。
- setLoop:设置指定编号的音频是否循环播放。
- setRate:设置指定编号音频的播放速率。
- pause:暂停播放指定编号的音频。
- resume:恢复播放指定编号的音频。
- autoPause:暂停所有正在播放的音频。
- autoResume:恢复播放所有被暂停的音频。
- stop:停止播放指定编号的音频。
- release:释放所有音频资源。
- setOnLoadCompleteListener:设置音频加载完毕的监听器。需实现接口OnLoadCompleteListener的onLoadComplete方法,该方法在音频加载结束后触发。
视频播放
本节介绍视频播放的相关技术,首先说明视频视图的工作原理,并结合拖动条实现简单的视频播放器,接着阐述媒体控制条的用法,以及媒体控制条与视频视图的两种绑定方式,最后演示了如何实现自定义样式的视频播放控制条。
视频视图VideoView
第9章在介绍录像放映功能时使用了MediaPlayer结合SurfaceView播放视频文件,其中通过SurfaceView显示视频的画面,通过MediaPlayer设置播放参数并控制视频的播放操作。不过仅仅播一个视频就得如此深入掌握技术细节未免太兴师动众了,因此Android推出了视频视图VideoView,该控件内部集成了SurfaceView和MediaPlayer,从而实现视频画面与视频操作的统一管理,为开发者进行视频开发提供便利。
下面是VideoView的常用方法说明。
- setVideoPath:设置视频文件的路径。
- setMediaController:设置媒体控制条的对象。
- setOnPreparedListener:设置预备播放监听器。需实现监听器OnPreparedListener的onPrepared方法,该方法在准备播放时调用。
- setOnCompletionListener:设置结束播放监听器。需实现监听器OnCompletionListener的onCompletion方法,该方法在结束播放时调用。
- setOnErrorListener:设置播放异常监听器。需实现监听器OnErrorListener的onError方法,该方法在播放出现异常时调用。
- setOnInfoListener:设置播放信息监听器。需实现监听器OnInfoListener的onInfo方法,该方法在播放需要传递某种消息时调用,如开始/结束缓冲。
- requestFocus:请求获得焦点。该方法要在start方法前调用。
- start:开始播放视频。
- pause:暂停播放视频。
- resume:恢复播放视频。
- suspend:结束播放并释放资源。
- seekTo:拖动视频到指定进度开始播放。
- getDuration:获得视频的总时长。
- getCurrentPosition:获得当前的播放位置。该方法返回值与getDuration相等时,表示播放到了末尾。
- isPlaying:判断是否正在播放。
- getBufferPercentage:获得已缓冲的比例。返回值在0到1之间。
由于VideoView只是一个播放界面,本身不会显示进度条,因此实际开发中至少得给它配备一个拖动条SeekBar,一方面用来展示当前的播放进度,另一方面用来拖动播放位置。
在VideoView的方法中,SeekBar主要用到了三个方法,第一个getDuration方法获得的总时长对应拖动条的最大进度值,第二个getCurrentPosition方法对应拖动条的当前进度值,第三个seekTo方法是在用户拖动SeekBar结束后调用。为VideoView加上SeekBar,即可实现基本的播放控制操作。
媒体控制条MediaController
使用拖动条主要完成两个播放控制功能:显示当前播放进度和拖动到指定位置播放。这两个基本功能显然不够全面,对于一个视频播放器来说,至少还得实现下列基础功能:
(1)暂停功能和暂停之后的恢复播放功能。
(2)查看视频的总时长和当前已播放的时长。
(3)快进和快退功能。
前面介绍VideoView的常用方法时提到setMediaController方法可设置媒体控制条的对象,这个媒体控制条就是MediaController,它的界面跟Windows上的播放条几乎一模一样,并支持一些基本的播放控制操作:显示当前的播放进度、拖动到指定位置播放、暂停播放与恢复播放、查看视频的总时长和已播放时长、对视频做快进或快退操作等。
下面是MediaController的常用方法说明。
- setMediaPlayer:设置媒体播放器的对象,即VideoView对象。该方法与setAnchorView只能调用一个。
- setAnchorView:设置绑定的主视图,其实一般是一个VideoView对象。该方法与setMediaPlayer只能调用一个。
- show:显示媒体控制条。
- hide:隐藏媒体控制条。
- isShowing:判断媒体控制条是否正在显示。
- setPrevNextListeners:设置前一个按钮与后一个按钮的点击监听器(OnClickListener)。如果没调用该方法,那么前一个按钮与后一个按钮都不会展示。
多窗口
手机屏幕允许分成多个窗口,每个任务使用一个小窗,如此才能达成多项事务并行不悖的目标。为了实现将屏幕分窗口运行的功能,可采取分屏、画中画、自定义悬浮窗等技术手段,下面分别进行详细介绍。
分屏------多窗口模式
现在的手机屏幕越来越大,使得在屏幕上同时开多个窗口不再奢侈,因此Android从7.0开始顺势推出了分屏功能,也被称作多窗口模式。比如把竖长的手机屏幕分成上下两个窗口,一边在上面的窗口中观看电影,一边在下面的窗口中聊天,可谓娱乐、工作两不误。那么分屏功能需要开发者进行哪些适配工作呢?接下来就详细阐述如何开关分屏模式,以及在编码的时候有哪些注意的地方。