一。主要 Activity分析
先分析三个主要 的 Activity 。
(1) 一个是打开程序时的 Tab 界面 --- PlayerActivity
(2) Tab 中第一个 Spec 界面 --------MusicListActivity
(3) 点击 MusicListActivity 的一个 Item 后,进入 播放界面 -------OnPlayActivity
下面,我们分析三个主要界面 的实现 方法
二。打开程序时的 Tab 界面 --- PlayerActivity
这个 界面我们继承自 TabActivity,里面的 几个 Tab 都 是intent 类型的。
这个 界面 并无消息传递的功能 ,只是一个显示 的功能 ,我们也没有给其加入菜单,而是将菜单加在其 Tab 中。
所以我们注意的有 其 xml 文件的实现 。文件结构 如下图
如果 Tab 是用 Intent ,则 tabcontent 下面可以 没有tab1 什么的。
使用 intent 的主要 java 代码 如下:
Resources res= getResources();
TabHost tabHost =getTabHost();
TabSpec spec;
Intent intent;
intent= new Intent().setClass(getBaseContext(), MusicListActivity.class);
spec =tabHost.newTabSpec("音乐").setIndicator("音乐",
res.getDrawable(R.drawable.media_music)).setContent(intent);
tabHost.addTab(spec);
注意,这里不用 setcontentView()。而是直接 添加 到 tabHost 中。
上面 的intent 初始化也可以 用 new Intent (getBaseContext() ,XXX.class);
三 。Tab 中第一个 Spec 界面 --------MusicListActivity
这个界面 是TabHost 的第一个 Tab。我 们在这里将加入菜单的功能 。
界面主要功能 有:
(1)显示音乐 List
这里我们要定义 一个 MusicAdpater ,为了保持 Adapter 不变(也就是后面的按下去了后的效果),我们用static 类的Adpater 。且在后面我们
将其加入 到一个 BaseActivity 中。(先不管)
(2)点击后,进入 OnPlayAcvitiy 界面 ,并同时传递所选择的 position 到 OnPlayActivity 中。
细节上的功能 还有,在传递 position 的过程 中,保存下 selected_id ,以让 选择的 Item 背景为GRAY。这就要用到
musicAdapter.notifyDataSetInvalidated();、
这里的处理比较复杂 ,会涉及到生命周期等
下面我们来分析上面的两个功能
(1) 为了将 Music 显示到list上,我们要定义一个 Music 类,一个 MusicAdapter 适配器。其中, Music 类的定义采用了 Bean 方法。
Music中主要 的变量如下:
private String title;
private String singer;
private String album;
private String url;
private long size;
private long time;
private String name;
在我们返回 name 的时候 ,可能要变换 下,这里先不管 ,不太重要。
定义好了 Music 类后,我们就可以很容易得到 Adapter 要的数据 类型--- List<Music> listMusic .
我们定义 了MusicList 类,里面的 getMusicData(Context )用于返回 List<Music > 给 MusicAdapter 。
MusicList 的功能 就是查询手机中的音乐 ,并封装成 Music。查询用到了 ContentResolver。细节上还有要选 出后缀 为 mp3 的音乐 。
同时 ,要补充 的知识 还有音乐URL 的查询方法,这里不细讲。
最后j MusicAdapter 的定义 。
主要功能 是将音乐 的数据显示到 List 上面,除了时间的的显示 上要注意下,别的还好。但是,
为了标注下已经选择的 Item, 我们在里面加一个变量 selected_id ,且用 get and set 。以区别。这样 ,里面的变量有:
Context context;
private List<Music> listMusic;
private LayoutInflater inflater;
private int selectedItem =-1;
当遇到 已经选择的 item,我们进行下面的处理:
if (getSelectedItem()==position){
convertView.setBackgroundColor(Color.GRAY); //是被点击 的音乐
}else { //没有点击 的音乐
convertView.setBackgroundColor(Color.TRANSPARENT);
convertView.getBackground().setAlpha(120);
}
return convertView;
(2) 在上面 的基础上,我们开始主要 分析 MusicListActivity
主要功能 是将 MusicList 数据 用 MusicAdpater 显示 在ListView 上。由于 MusicAdpater 可能 以后 还会用到,可以考虑将其设定为 static 的。
同时 ,考虑到每个 Tab 的菜单都相同 ,我们可以写一个 BaseActivity .具体如下。
public class BaseActivity extends Activity{
View convertView ;
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// TODO Auto-generated method stub
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
return super.onOptionsItemSelected(item);
}
}
这样 ,我们可以在 BaseActivity中完成菜单的功能 ,同时 ,由于 我们定义 了convertView .以后 Activity 中使用的方法为
super.onCreate(savedInstanceState);
convertView = LayoutInflater.from(this ).inflate(R.layout.music_list, null);
convertView.setBackgroundDrawable(null); //此处可以 加入背景图片
setContentView(convertView);
为了将 选择的音乐 的 id 传递给 OnPlayActivity (OnPlayActivity 再传递给 PalyService )。在监听器中有:
OnItemClickListener ItemClick=new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View v, int position,
long id ) {
// TODO Auto-generated method stub
musicAdapter.setSelectedItem(position);
musicAdapter.notifyDataSetInvalidated();
//使Adapter 在之后 不再改变 ,只能保证在Activity没有退出时的,如果 退出 了,
//则会再次运行 onStart() ;
Intent i = new Intent(getBaseContext(), OnPlayActivity.class);
i.putExtra("id", position);
startActivity(i);
}
};
如果 这个 Activity 退出 了,怎么还保持 选 的 Item 背景色不同呢, 由Activity 的生命周期,我们应该在 onStart()中完成 这个 功能 ,如下
@Override
protected void onStart() {
// TODO Auto-generated method stub
musicAdapter.setSelectedItem(PlayService.current_id);
musicAdapter.notifyDataSetInvalidated();
super.onStart();
}
要注意的是,此时 musicAdapter 设定的 selected_id 是我们从 PlayService 中读取的。
(selected_id 的最后销毁在 Menu 方法 closeApp()里)
(PlayService 中的变量 current_id 类型为
public static volatile int currentId = -1;
原因有:a. 用public修饰的static成员变量和成员方法本质是全局变量和全局方法,当声明它类的对象时,不生成static变量的副本,而是类的所有实例共享同一个static变量。
b. 一个变量声明为volatile,就意味着这个变量是随时会被其他线程修改的,因此不能将它cache在线程memory中。以下例子展现了volatile的作用:用volatile修饰的
变量,线程在每次使用变量的时候,都会读取变量修改后的最的值.)
四。音乐播放界面 --OnPlayActivity
OnPlayActivity 从前面的 MusicListActivity 中得到消息只有一个---所选择的音乐。
这里,我们要完成 的主要 功能 有
(1) 完成音乐 的显示 ,先只显示歌名与歌手 。这里我们要先得到 musicList ,再得到 传递进来 的 id 以得到 所选择的 music ,我们在 initView ()方法中完成 。
private void initView() {
// TODO Auto-generated method stub
music_name= (TextView) findViewById(R.id.music_name);
music_singer= (TextView) findViewById(R.id.music_singer);
musicList = MusicList.getMusicData(getApplicationContext());
selected_id = getIntent().getIntExtra("id", 1);
music= musicList.get(selected_id);
music_name.setText(music.getName());
music_singer.setText(music.getSinger());
}
(2)音乐播放,也就是 PlayerService 的启动,只用传递消息 selected_id 就可以了,这里我们用 方法 sendServiceID() 来完成 。
private void sendServiceID() {
// TODO Auto-generated method stub
Intent i= new Intent(this ,PlayService.class);
i.putExtra("selected_id", selected_id);
startService(i);
}
(3)歌曲进度,声音调节,歌词 ,前一首歌,后一首歌曲 等先不考虑。
五。PlaySerivce 的实现
PlayerService 会接收来自 OnPlayActivity 的消息 selected_id .以播放音乐 。