您的位置:首页 > 其它

HelloMoon应用介绍

2016-02-25 11:17 190 查看
HelloMoon是《Android权威编程指南》一书中的一个应用,通过这个应用,可以学习到如下知识点:

通过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目录。当没有任何可匹配的结果被筛选出来时,系统将使用缺省的资源目录,也就是说,缺省的资源目录必须设置,否则当系统没有匹配的资源时,程序将崩溃。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息