HelloMoon应用介绍
2016-02-25 11:17
190 查看
HelloMoon是《Android权威编程指南》一书中的一个应用,通过这个应用,可以学习到如下知识点:
通过MediaPlayer播放音频文件;
使用 setRetainInstance(boolean) 方法保留fragment实例;
在res资源目录下使用配置修饰符,使应用本地化。
应用只包含一个activity/fragment,点击播放,将播放一段音频,点击停止,音频将停止播放。最终效果如下所示:
本文将分为下面几个部分:
fragment布局及逻辑;
封装MediaPlayer类,实现音频播放;
介绍配置修饰符的使用规则。
上述XML需注意:
无需为TableRow设置宽、高,宽高均由其父容器决定;
设置layout_weight = “0”表示为TableRow分配的空间比重将由其子组件的大小决定。
fragment逻辑:
代码为两个按钮绑定了监听器,用于控制音频的播放和停止,同时还创建了一个AudioPlayer对象,该对象封装了用于控制音频、视频播放的类MediaPlayer。下面是AudioPlayer的逻辑实现:
MediaPlayer是独占性的:被MediaPlayer绑定了的资源将无法被其他类使用,所以,在不需要MediaPlayer的时候,要及时释放其绑定的资源;
使用MediaPlayer.setOnCompletionListener(OnCompletionListener listener),为MediaPlayer绑定监听器,当资源播放完成后,OnCompletionListener 接口中的方法onCompletion(MediaPlayer mp)被回调,可在该方法中处理MediaPlayer的善后工作。
当屏幕配置发生改变时(如旋转屏幕,系统语言改变等),fragment中播放的音频(MediaPlayer)将随fragment的实例被一并销毁,为了保证音频不被重置,应在Fragment.onCreate()方法中调用方法setRetainInstance(true),该方法(默认情况下参数为false)可保证当fragment的宿主activity被销毁并重建时,其托管的fragment实例不被销毁,而只是将该fragment的视图随activity销毁。在activity销毁并重建实例的时间很短,fragment没有被任何activity托管,因此setRetainInstance(true)的适用条件仅是当设备的配置发生改变时,托管的activity正在被销毁的情形。另外,如需持久地保存数据,应使用回调方法onSaveInstanceState方法,该方法用于保存并回复应用的UI状态,有关onSaveInstanceState的用法,请参见我的博文《GeoQuiz的个人见解》。onSaveInstanceState与setRetainInstance方法的主要区别是数据可以保存多久,若只是短暂的保留数据,能应对设备配置的改变,使用后者就行了,因为fragment保留数据的时间就是该fragment的生命周期的时间;若需要持久地保存数据,就得用前者:当用户离开应用后,如系统因回收内存需要销毁activity,则保留的fragment也会被随之销毁。
移动国家代码(通常附有移动网络码)
语言代码(通常附有地区代码)
布局方向
最小宽度
可用宽度
可用高度
屏幕尺寸
屏幕纵横比
屏幕方位
UI模式
夜间模式
屏幕显示目睹
触摸屏类型
键盘可用性
首选输入法
导航键可用性
非文本导航方法
API级别
下面是HelloMoon应用中,res目录下使用配置修饰符的情形:
res/raw、res/layout、res/drawable、res/values目录表示在缺省情况下的配置修饰符,例如,当系统语言切换至中文、而res目录中并没有提供values-zh目录时,应用将使用res/values中的内容;再比如,除非设备本版本等于或高于API 11,否则应用将使用默认的res/values中的资源;
可以在同一资源目录上使用读个配置修饰符,各配置修饰符必须按照上述优先级的顺序排列,如res/values-zh-land;但res/values-land-zh无效;
配置修饰符的匹配规则如下:
–1、首先排除不兼容的目录——比如,在竖屏浏览应用时,res/values-land和values-zh-land目录中的内容将被忽略。特别注意的是,Android对屏幕显示密度采用了不同的处理方式,该兼容匹配规则不适用于它,Android会选择其认为最合适的资源来匹配设备配置,也就是说,系统最终选择的匹配方案可能与开发者适配的方案有出入。
–2、按优先级筛选不兼容目录:系统会按优先级从高到低的顺序匹配修饰符,若没有为该优先级设置修饰符,则将该优先级忽略,继续按次优先级的修饰符进行筛选:当系统语言设置为中文,并将屏幕旋转至横屏时,系统会这样匹配修饰符:首先系统会寻找res目录下是否有被优先级最高的MCC修饰的目录(移动国家代码 MCC:Mobile Country Code),显然在本应用中没有,故比较次优先级的“语言代码”,由于系统语言是中文,故筛选出res/values-zh、res/values-zh-land目录,接着筛选下一个优先级“布局方向”,由于屏幕为横屏,最终筛选的结果是使用res/values-zh-land目录。当没有任何可匹配的结果被筛选出来时,系统将使用缺省的资源目录,也就是说,缺省的资源目录必须设置,否则当系统没有匹配的资源时,程序将崩溃。
通过MediaPlayer播放音频文件;
使用 setRetainInstance(boolean) 方法保留fragment实例;
在res资源目录下使用配置修饰符,使应用本地化。
应用只包含一个activity/fragment,点击播放,将播放一段音频,点击停止,音频将停止播放。最终效果如下所示:
本文将分为下面几个部分:
fragment布局及逻辑;
封装MediaPlayer类,实现音频播放;
介绍配置修饰符的使用规则。
fragment逻辑和布局
首先为fragment设置布局:<!-- fragment_hello_mooon.xml --> <!-- fragment的布局 --> <TableLayout 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" tools:context=".HelloMoonFragment" > <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" android:contentDescription="@string/hellomoon_description" android:scaleType="centerInside" android:src="@drawable/armstrong_on_moon" /> <TableRow android:layout_weight="0" android:gravity="center|bottom" > <Button android:id="@+id/hellomoon_playButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hellomoon_play" > </Button> <Button android:id="@+id/hellomoon_stopButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hellomoon_stop" /> </TableRow> </TableLayout>
上述XML需注意:
无需为TableRow设置宽、高,宽高均由其父容器决定;
设置layout_weight = “0”表示为TableRow分配的空间比重将由其子组件的大小决定。
fragment逻辑:
//HelloMoonFragment.java //fragment的实现逻辑 public class HelloMoonFragment extends Fragment { private Button mPlayButton; private Button mStopButton; private AudioPlayer mAudioPlayer = new AudioPlayer();; @Override public void onCreate(@Nullable Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); // 该方法可Fragment实例一直存在(即保存了MediaPlayer实例),不会因为旋转屏幕而被销毁 setRetainInstance(true); } @Override @Nullable public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { // TODO Auto-generated method stub View view = inflater.inflate(R.layout.fragment_hello_moon, container, false); mPlayButton = (Button) view.findViewById(R.id.hellomoon_playButton); mPlayButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub mAudioPlayer.play(getActivity()); } }); mStopButton = (Button) view.findViewById(R.id.hellomoon_stopButton); mStopButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub mAudioPlayer.stop(); } }); return view; } // HelloMoonFragment被销毁后,MediaPlayer仍可不停播放,因为MediaPlayer运行在不同的线程上,故需手动停止 @Override public void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); mAudioPlayer.stop(); } }
代码为两个按钮绑定了监听器,用于控制音频的播放和停止,同时还创建了一个AudioPlayer对象,该对象封装了用于控制音频、视频播放的类MediaPlayer。下面是AudioPlayer的逻辑实现:
public class AudioPlayer { private MediaPlayer mPlayer; public void stop() { if (mPlayer != null) { mPlayer.release(); mPlayer = null; } } public void play(Context c) { // 避免多次单击Play按钮创建多个MediaPlayer实例的情况发生 stop(); mPlayer = MediaPlayer.create(c, R.raw.one_small_step); // 监听MediaPlayer播放情况,当播放完成后,立即释放MediaPlayer实例占用的资源 mPlayer.setOnCompletionListener(new OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { // TODO Auto-generated method stub stop(); } }); mPlayer.start(); } }
MediaPlayer是独占性的:被MediaPlayer绑定了的资源将无法被其他类使用,所以,在不需要MediaPlayer的时候,要及时释放其绑定的资源;
使用MediaPlayer.setOnCompletionListener(OnCompletionListener listener),为MediaPlayer绑定监听器,当资源播放完成后,OnCompletionListener 接口中的方法onCompletion(MediaPlayer mp)被回调,可在该方法中处理MediaPlayer的善后工作。
当屏幕配置发生改变时(如旋转屏幕,系统语言改变等),fragment中播放的音频(MediaPlayer)将随fragment的实例被一并销毁,为了保证音频不被重置,应在Fragment.onCreate()方法中调用方法setRetainInstance(true),该方法(默认情况下参数为false)可保证当fragment的宿主activity被销毁并重建时,其托管的fragment实例不被销毁,而只是将该fragment的视图随activity销毁。在activity销毁并重建实例的时间很短,fragment没有被任何activity托管,因此setRetainInstance(true)的适用条件仅是当设备的配置发生改变时,托管的activity正在被销毁的情形。另外,如需持久地保存数据,应使用回调方法onSaveInstanceState方法,该方法用于保存并回复应用的UI状态,有关onSaveInstanceState的用法,请参见我的博文《GeoQuiz的个人见解》。onSaveInstanceState与setRetainInstance方法的主要区别是数据可以保存多久,若只是短暂的保留数据,能应对设备配置的改变,使用后者就行了,因为fragment保留数据的时间就是该fragment的生命周期的时间;若需要持久地保存数据,就得用前者:当用户离开应用后,如系统因回收内存需要销毁activity,则保留的fragment也会被随之销毁。
配置修饰符的使用
不同国家和地区有自己的语言和APP使用习惯;不同设备的屏幕大小、分辨率需要使用的图片大小不同(屏幕显示密度);不同版本的设备需要为其兼容版本的主题(API级别),等等。通过配置修饰符,可以实现上面的要求,下面是具有配置修饰符的设备特征:移动国家代码(通常附有移动网络码)
语言代码(通常附有地区代码)
布局方向
最小宽度
可用宽度
可用高度
屏幕尺寸
屏幕纵横比
屏幕方位
UI模式
夜间模式
屏幕显示目睹
触摸屏类型
键盘可用性
首选输入法
导航键可用性
非文本导航方法
API级别
下面是HelloMoon应用中,res目录下使用配置修饰符的情形:
res/raw、res/layout、res/drawable、res/values目录表示在缺省情况下的配置修饰符,例如,当系统语言切换至中文、而res目录中并没有提供values-zh目录时,应用将使用res/values中的内容;再比如,除非设备本版本等于或高于API 11,否则应用将使用默认的res/values中的资源;
可以在同一资源目录上使用读个配置修饰符,各配置修饰符必须按照上述优先级的顺序排列,如res/values-zh-land;但res/values-land-zh无效;
配置修饰符的匹配规则如下:
–1、首先排除不兼容的目录——比如,在竖屏浏览应用时,res/values-land和values-zh-land目录中的内容将被忽略。特别注意的是,Android对屏幕显示密度采用了不同的处理方式,该兼容匹配规则不适用于它,Android会选择其认为最合适的资源来匹配设备配置,也就是说,系统最终选择的匹配方案可能与开发者适配的方案有出入。
–2、按优先级筛选不兼容目录:系统会按优先级从高到低的顺序匹配修饰符,若没有为该优先级设置修饰符,则将该优先级忽略,继续按次优先级的修饰符进行筛选:当系统语言设置为中文,并将屏幕旋转至横屏时,系统会这样匹配修饰符:首先系统会寻找res目录下是否有被优先级最高的MCC修饰的目录(移动国家代码 MCC:Mobile Country Code),显然在本应用中没有,故比较次优先级的“语言代码”,由于系统语言是中文,故筛选出res/values-zh、res/values-zh-land目录,接着筛选下一个优先级“布局方向”,由于屏幕为横屏,最终筛选的结果是使用res/values-zh-land目录。当没有任何可匹配的结果被筛选出来时,系统将使用缺省的资源目录,也就是说,缺省的资源目录必须设置,否则当系统没有匹配的资源时,程序将崩溃。
相关文章推荐
- MySQL自带数据库test编码问题
- 解决 e.g. x.new A() where x is an instance of xxx.class
- 重置mysql主从同步(MySQL Reset Master-Slave Replication)
- jQuery插件ImageDrawer.js实现动态绘制图片动画(附源码下载)
- nw.js node-webkit系列(5)Native UI API Window的使用
- Ubuntu中安装Python h5py
- 租凭项目记录
- 一年之计在于春
- 我的MVC框架————GothaMVC(4)GothamDispatcher的实现和Model层的封装
- MySQL/phpmyadmin问题解决手记:#2002 – 服务器没有响应 (或者本地 MySQL 服务器的套接字没有正确配置)
- Powershell 工作流实战(1)
- android WIFI
- python实战第一天-环境的安装
- UIView的一些常用属性和方法
- MySQL创建用户与授权
- 推荐一款好用的笔记管理软件(Evernote)
- 当外部程序正在“Opened”上接收时导致访问冲突,是否终止该应用程序, LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏
- MySQL 索引优化
- 解决Git push时重复输入用户名密码的
- 某个字符之前的值