Android 音乐播放器的实现(二)界面的实现

写程序的过程中,想法总会不断地变,有时候会很纠结,到底做哪种效果好,怎么做好呢?

就比如这个音乐播放器,我原来的想法是把列表页面跟歌词页面放在同一个Activity中的两个Fragment,然后通过左右滑动来进行页面的切换。

但是看了酷狗的播放器,它是在启动页面点击了左下角的按钮,就会把歌词页面从右下角斜切上来,我觉得也挺帅的呀,又想做这个效果了。

不管怎么样,先做出一个来再说吧。

下面先看一下效果动态图,是用4.4的AVD来播放的,因为那个斜切上来的动画,会用到一些属性是3.0才支持的,所以2.3的机器做不出来。机器有点差,开个模拟器真是慢到死。

做得比较匆忙,其实还是有不少Bug的,还有一些功能,比如歌词也还没实现,请各位多包涵。

效果图:

Android 音乐播放器的实现(二)界面的实现

下面我们分几步来讲:

界面:

目前只有两个界面,列表界面MainActivity和歌词DetailActivity。
列表界面也分两部分,上面是一个ListView,下面是一个RelativeLayout,里面放了一个按钮(去歌词界面),一个进度条,一个TextView去显示歌名,两个控制按钮,播放和前进(酷狗就是只有这两个按钮,有点好奇设计的理念是什么,为什么不加前一首?),这个看布局的xml就一目了然了。
\res\layout\activity_main.xml
<LinearLayout 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"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <ListView
        android:id="@+id/lvSongs"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="8"
        android:cacheColorHint="#00000000" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:background="#BBBBBB"
        android:layout_height="0dp"
        android:layout_weight="1" >

        <Button
            android:id="@+id/btnDetail"
            android:layout_width="48dip"
            android:layout_height="48dip"
            android:layout_margin="0dip"
            android:background="@drawable/music_app"
            android:layout_alignParentBottom="true"
            android:layout_alignParentTop="true"/>

        <SeekBar
            android:id="@+id/pbDuration"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="wrap_content"
            android:layout_marginTop="3dip"
            android:layout_height="10dip"
            android:layout_marginBottom="3dip"
            android:layout_alignParentRight="true"
            android:layout_alignParentTop="true"
            android:layout_toRightOf="@+id/btnDetail"                       
            android:maxHeight="1dip"
            android:minHeight="1dip"
            android:progressDrawable="@drawable/progress_style"
            android:thumbOffset="0dip"            
            android:thumb="@drawable/seekbar_thumb"/>

        <TextView
            android:id="@+id/tvCurrentMusic"
            android:layout_width="190dp"
            android:layout_height="32dip"
            android:layout_alignBaseline="@+id/btnNext"
            android:layout_toRightOf="@+id/btnDetail"
            android:gravity="left|center_vertical"
            android:paddingLeft="5dip"
            android:paddingRight="5dip" />

        <Button
            android:id="@+id/btnStartStop"
            android:layout_width="32dip"
            android:layout_height="32dip"
            android:layout_alignBaseline="@+id/btnNext"
            android:layout_below="@+id/pbDuration"
            android:layout_toLeftOf="@+id/btnNext"
            android:background="@drawable/play"
            android:layout_marginRight="5dip"
          />

        <Button
            android:id="@+id/btnNext"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:layout_alignParentRight="true"
            android:layout_below="@+id/pbDuration"
            android:layout_marginRight="20dip"
            android:background="@drawable/forward" />

    </RelativeLayout>

</LinearLayout>
其中进度条是用自定义的风格的(也是在CSDN上找的,还没去具体了解,到时了解了再讲一下)。
下面是DetailActivity的布局xml.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res/com.example.nature"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="match_parent"
        android:layout_height="0dip"
        android:layout_weight="1"
        android:background="#00EEEE"
        android:gravity="center_vertical"
        android:paddingLeft="5dip"
        android:paddingRight="5dip" />

    <TextView
        android:id="@+id/tvLyric"
        android:layout_width="match_parent"
        android:layout_height="0dip"
        android:layout_weight="9"
        android:gravity="center_vertical"
        android:text="To Continue...." />

    <LinearLayout
        android:id="@+id/llProgress"
        android:layout_width="match_parent"
        android:layout_height="0dip"
        android:layout_weight="1"
        android:gravity="center_horizontal"
        android:orientation="horizontal" >

        <TextView
            android:id="@+id/tvTimeElapsed"
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal|center_vertical"
            android:layout_weight="1"
            android:text="00:00" />

        <SeekBar
            android:id="@+id/pbDuration"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="0dip"
            android:layout_height="10dip"
            android:layout_gravity="center_horizontal|center_vertical"
            android:layout_weight="6"
            android:maxHeight="1dip"
            android:minHeight="1dip"
            android:progressDrawable="@drawable/progress_style"
            android:secondaryProgress="0"
            android:thumb="@drawable/seekbar_thumb"
            android:thumbOffset="0dip" />

        <TextView
            android:id="@+id/tvDuration"
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal|center_vertical"
            android:layout_weight="1"
            android:text="00:00" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dip"
        android:layout_weight="2"
        android:orientation="horizontal" >

        <com.example.nature.CustomAudioIcon
            android:id="@+id/btnMode"
            android:layout_width="0dip"
            android:layout_height="match_parent"
            android:layout_weight="1"
            custom:type="mode"
            custom:color="#66DD22" />

        <com.example.nature.CustomAudioIcon
            android:id="@+id/btnPrevious"
            android:layout_width="0dip"
            android:layout_height="match_parent"
            android:layout_weight="1"
            custom:type="backward"
            custom:color="#66DD22" />

        <com.example.nature.CustomAudioIcon
            android:id="@+id/btnStartStop"
            android:layout_width="0dip"
            android:layout_height="match_parent"
            android:layout_weight="1"
            custom:type="start"
            custom:color="#66DD22" />

        <com.example.nature.CustomAudioIcon
            android:id="@+id/btnNext"
            android:layout_width="0dip"
            android:layout_height="match_parent"
            android:layout_weight="1"
            custom:type="forward"
            custom:color="#66DD22" />

        <com.example.nature.CustomAudioIcon
            android:id="@+id/btnExit"
            android:layout_width="0dip"
            android:layout_height="match_parent"
            android:layout_weight="1"
            custom:type="exit"
            custom:color="#66DD22" />
    </LinearLayout>

</LinearLayout>
这里面有三部分:
上端的是一个TextView,用来显示歌曲名的。
中间的是一个歌词显示,目前还没实现,所以显示了“To Continue...”。
最下面是五个自定义按钮(上网找图标真是很麻烦的,自定义按钮的实现,请看前一篇博文

控制逻辑

说完界面,下面就是来说我们控制逻辑。
下面是Activity的代码,先贴上来,我再仔细说一下实现。
public class MainActivity extends Activity implements OnClickListener{

	public static final String TAG = "com.example.nature.MAIN_ACTIVITY";
	
	private ListView lvSongs;	
	private SeekBar pbDuration;
	private TextView tvCurrentMusic;
	private List<MusicInfo> musicList;
	private int currentMusic; // The music that is playing.
	private int currentPosition; //The position of the music is playing.
	private int currentMax;
		
	private Button btnStartStop;
	private Button btnNext;
	private Button btnDetail;
	
	private ProgressReceiver progressReceiver;	
	private NatureBinder natureBinder;	
	
	private ServiceConnection serviceConnection = new ServiceConnection() {
		
		@Override
		public void onServiceDisconnected(ComponentName name) {
			
		}
		
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			natureBinder = (NatureBinder) service;			
		}
	};
	
	private void connectToNatureService(){		
		Intent intent = new Intent(MainActivity.this, NatureService.class);				
		bindService(intent, serviceConnection, BIND_AUTO_CREATE);				
	}
	

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		Log.v(TAG, "OnCreate");
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);		
		MusicLoader musicLoader = MusicLoader.instance(getContentResolver());		
		musicList = musicLoader.getMusicList();
		connectToNatureService();
		initComponents();		
	}
	
	public void onResume(){
		Log.v(TAG, "OnResume register Progress Receiver");
		super.onResume();										
		registerReceiver();
		if(natureBinder != null){
			if(natureBinder.isPlaying()){
				btnStartStop.setBackgroundResource(R.drawable.pause);
			}else{
				btnStartStop.setBackgroundResource(R.drawable.play);
			}
			natureBinder.notifyActivity();
		}
	}
	
	public void onPause(){
		Log.v(TAG, "OnPause unregister Progress Receiver");
		super.onPause();
		unregisterReceiver(progressReceiver);
	}
	
	public void onStop(){
		Log.v(TAG, "OnStop");
		super.onStop();				
	}
	
	public void onDestroy(){
		Log.v(TAG, "OnDestroy");
		super.onDestroy();
		if(natureBinder != null){
			unbindService(serviceConnection);
		}
	}
		
	private void initComponents(){		
		pbDuration = (SeekBar) findViewById(R.id.pbDuration);	
		pbDuration.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
			
			@Override
			public void onStopTrackingTouch(SeekBar seekBar) {}
			
			@Override
			public void onStartTrackingTouch(SeekBar seekBar) {}
			
			@Override
			public void onProgressChanged(SeekBar seekBar, int progress,
					boolean fromUser) {
				if(fromUser){
					natureBinder.changeProgress(progress);
				}
			}
		});
		
		tvCurrentMusic = (TextView) findViewById(R.id.tvCurrentMusic);				
		
		btnStartStop = (Button)findViewById(R.id.btnStartStop);
		btnStartStop.setOnClickListener(this);
		
		btnNext = (Button)findViewById(R.id.btnNext);
		btnNext.setOnClickListener(this);
		
		btnDetail = (Button) findViewById(R.id.btnDetail);
		btnDetail.setOnClickListener(this);
		
		MusicAdapter adapter = new MusicAdapter();
		lvSongs = (ListView) findViewById(R.id.lvSongs);		
		lvSongs.setAdapter(adapter);
		lvSongs.setOnItemClickListener(new OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				currentMusic = position;
				natureBinder.startPlay(currentMusic,0);
				if(natureBinder.isPlaying()){					
					btnStartStop.setBackgroundResource(R.drawable.pause);		
				}
			}
		});
	}
	
	private void registerReceiver(){
		progressReceiver = new ProgressReceiver();	
		IntentFilter intentFilter = new IntentFilter();
		intentFilter.addAction(NatureService.ACTION_UPDATE_PROGRESS);
		intentFilter.addAction(NatureService.ACTION_UPDATE_DURATION);
		intentFilter.addAction(NatureService.ACTION_UPDATE_CURRENT_MUSIC);
		registerReceiver(progressReceiver, intentFilter);
	}
	
	class MusicAdapter extends BaseAdapter{

		@Override 
		public int getCount() {
			return musicList.size();
		}

		@Override
		public Object getItem(int position) {
			return musicList.get(position);
		}

		@Override
		public long getItemId(int position) {
			return musicList.get(position).getId();
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			ViewHolder viewHolder; 
			if(convertView == null){
				convertView = LayoutInflater.from(MainActivity.this).inflate(R.layout.music_item, null);
				ImageView pImageView = (ImageView) convertView.findViewById(R.id.albumPhoto);
				TextView pTitle = (TextView) convertView.findViewById(R.id.title);
				TextView pDuration = (TextView) convertView.findViewById(R.id.duration);
				TextView pArtist = (TextView) convertView.findViewById(R.id.artist);
				viewHolder = new ViewHolder(pImageView, pTitle, pDuration, pArtist);
				convertView.setTag(viewHolder);
			}else{
				viewHolder = (ViewHolder) convertView.getTag();
			}
			
			viewHolder.imageView.setImageResource(R.drawable.audio);
			viewHolder.title.setText(musicList.get(position).getTitle());
			viewHolder.duration.setText(FormatHelper.formatDuration(musicList.get(position).getDuration()));
			viewHolder.artist.setText(musicList.get(position).getArtist());
			
			return convertView;
		}		
	}
	
	class ViewHolder{
		public ViewHolder(ImageView pImageView, TextView pTitle, TextView pDuration, TextView pArtist){
			imageView = pImageView;
			title = pTitle;
			duration = pDuration;
			artist = pArtist;
		}
		
		ImageView imageView;
		TextView title;
		TextView duration;
		TextView artist;
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	public void onClick(View v) {		
		switch (v.getId()) {		
		case R.id.btnStartStop:		
			play(currentMusic,R.id.btnStartStop);
			break;		
		case R.id.btnNext:
			natureBinder.toNext();
			break;		
		case R.id.btnDetail:						
			Intent intent = new Intent(MainActivity.this,DetailActivity.class);
			intent.putExtra(DetailActivity.MUSIC_LENGTH, currentMax);
			intent.putExtra(DetailActivity.CURRENT_MUSIC, currentMusic);
			intent.putExtra(DetailActivity.CURRENT_POSITION, currentPosition);			
			startActivity(intent);			
			break;
		}		
	}
	
	private void play(int position, int resId){		
		if(natureBinder.isPlaying()){
			natureBinder.stopPlay();
			btnStartStop.setBackgroundResource(R.drawable.play);
		}else{
			natureBinder.startPlay(position,currentPosition);
			btnStartStop.setBackgroundResource(R.drawable.pause);
		}
	}

	

	class ProgressReceiver extends BroadcastReceiver{

		@Override
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();
			if(NatureService.ACTION_UPDATE_PROGRESS.equals(action)){
				int progress = intent.getIntExtra(NatureService.ACTION_UPDATE_PROGRESS, 0);
				if(progress > 0){
					currentPosition = progress; // Remember the current position
					pbDuration.setProgress(progress / 1000);
				}
			}else if(NatureService.ACTION_UPDATE_CURRENT_MUSIC.equals(action)){
				//Retrive the current music and get the title to show on top of the screen.
				currentMusic = intent.getIntExtra(NatureService.ACTION_UPDATE_CURRENT_MUSIC, 0);				
				tvCurrentMusic.setText(musicList.get(currentMusic).getTitle());
			}else if(NatureService.ACTION_UPDATE_DURATION.equals(action)){
				//Receive the duration and show under the progress bar
				//Why do this ? because from the ContentResolver, the duration is zero.
				currentMax = intent.getIntExtra(NatureService.ACTION_UPDATE_DURATION, 0);				
				int max = currentMax / 1000;
				Log.v(TAG, "[Main ProgressReciver] Receive duration : " + max);
				pbDuration.setMax(currentMax / 1000);						
			}
		}
		
	}
	
}

接下来说说主要的实现逻辑:
1)加载歌曲,写了一个MusicLoader来读取本地sdcard中的歌曲,这一方面的请参考:
2)将歌曲放到ListView中,写了一个继承于BaseAdapter的MusicAdapter,一般实现BaseAdapter,都会用到下面两种优化方式:
2.1)重复利用ConvertView,保证同一个时刻在列表的View不会太多
2.3)利用ViewHolder,来存放加载过的View,不用每次都要重新用findViewById去实例化View,节省成本
3)绑定Service,音乐播放器主要的操作,包括播放,暂停,前进,后退等,都是在Service中实现的,我们在Service里面实现了一个Binder,在里面定义了几种操作,然后传给Activity,让Activity可以跟Service进行互动。
4)继承BroadcastReceiver 实现一个自定义的Receiver,接收从Service传过来的广播,比如当前播放的位置,播放的前/后一首是什么,播放的当前模式是什么,这些都要从Service传回到Activity中,从而让Activity可以根据这些状态去更新按钮的状态。
基本的功能就是这样。
在这里面有一点要注意的就是,注册广播的时候,最好是在OnResume中,因为在OnPause的时候我们会将这个广播给卸掉,这样从歌词界面切回来的时候,系统只会调用OnResume函数,从而就会重新将广播给注册上,而不需要让广播一直监听信息(即使Activity都没显示了)。
基本的逻辑大概就是这样了,但是这里面还是会有很多小细节需要完善的。
比如在Detail界面中一首歌播完了,开始播下一首,那么Service会传个广播回到这个Activity,告诉当前界面要更新歌曲名,歌曲长度,但是如果这个时候我们切回Main界面的话,
Service是不会知道要发送广播回Main界面告诉它要更新的(因为只有在一首歌结束的时候,进入下一首歌的时候才发广播), 那么此时就要在MainActivity恢复(OnResume)的时候,让Service发送广播回来,所以可以在MainActivity中看到如下的代码:
	public void onResume(){
		Log.v(TAG, "OnResume register Progress Receiver");
		super.onResume();										
		registerReceiver();
		if(natureBinder != null){
			...
			natureBinder.notifyActivity();
		}
	}
	
在Service中的Binder也提供了这样一个接口,来让Activity告诉Service,我想知道当前在播哪一首歌。。。
所以在Service中的Binder类,其实我们要实现比较多的接口来跟Activity中交互的,先列举一下,下一篇文章会详细说一下Service
class NatureBinder extends Binder{
		
		public void startPlay(int currentMusic, int currentPosition){
			play(currentMusic,currentPosition);
		}
		
		public void stopPlay(){
			stop();
		}
		
		public void toNext(){
			playNext();
		}
		
		public void toPrevious(){
			playPrevious();
		}
		
		/**
		 * MODE_ONE_LOOP = 1;
		 * MODE_ALL_LOOP = 2;
		 * MODE_RANDOM = 3;
		 * MODE_SEQUENCE = 4; 
		 */		
		public void changeMode(){			
			currentMode = (currentMode + 1) % 4;
			Log.v(TAG, "[NatureBinder] changeMode : " + currentMode);
			Toast.makeText(NatureService.this, MODE_DESC[currentMode], Toast.LENGTH_SHORT).show();
		}
		
		/**
		 * return the current mode
		 * MODE_ONE_LOOP = 1;
		 * MODE_ALL_LOOP = 2;
		 * MODE_RANDOM = 3;
		 * MODE_SEQUENCE = 4; 
		 * @return
		 */
		public int getCurrentMode(){
			return currentMode; 
		}
		
		/**
		 * The service is playing the music
		 * @return
		 */
		public boolean isPlaying(){
			return isPlaying;
		}
		
		/**
		 * Notify Activities to update the current music and duration when current activity changes.
		 */
		public void notifyActivity(){
			toUpdateCurrentMusic();
			toUpdateDuration();			
		}
		
		/**
		 * Seekbar changes
		 * @param progress
		 */
		public void changeProgress(int progress){
			...
		}

下面是DetailActivity中的代码:
public class DetailActivity extends Activity implements OnClickListener{
	
	private static final String TAG = "com.example.natrue.DetailActivity";
	
	public static final String MUSIC_LENGTH = "com.example.nature.DetailActivity.MUSIC_LENGTH";
	public static final String CURRENT_POSITION = "com.example.nature.DetailActivity.CURRENT_POSITION";
	public static final String CURRENT_MUSIC = "com.example.nature.DetailActivity.CURRENT_MUSIC";

	private SeekBar pbDuration;
	private TextView tvTitle,tvTimeElapsed, tvDuration;
	private List<MusicInfo> musicList;
	private int currentMusic;
	
	private int currentPosition;
	
	private ProgressReceiver progressReceiver;	
	
	private NatureBinder natureBinder;
	
	private int[] btnResIds = new int[] {
			R.id.btnMode,
			R.id.btnPrevious, 
			R.id.btnStartStop, 			
			R.id.btnNext,
			R.id.btnExit 
	};
	
	private ServiceConnection serviceConnection = new ServiceConnection() {
		
		@Override
		public void onServiceDisconnected(ComponentName name) {
			
		}
		
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			natureBinder = (NatureBinder) service;	
			if(natureBinder.isPlaying()){
				CustomAudioIcon btnStartStop = (CustomAudioIcon)findViewById(R.id.btnStartStop);
				btnStartStop.setFlagStart(false);		
			}
			CustomAudioIcon btnMode = (CustomAudioIcon)findViewById(R.id.btnMode);
			btnMode.setCurrentMode(natureBinder.getCurrentMode());
		}
	};
	
	private void connectToNatureService(){		
		Intent intent = new Intent(DetailActivity.this, NatureService.class);				
		bindService(intent, serviceConnection, BIND_AUTO_CREATE);				
	}
		
	
	@Override	
	public void onCreate(Bundle savedInstanceState){
		Log.v(TAG, "OnCreate");
		super.onCreate(savedInstanceState);
		overridePendingTransition(R.anim.push_right_in,R.anim.hold);
		MusicLoader musicLoader = MusicLoader.instance(getContentResolver());		
		musicList = musicLoader.getMusicList();	
		setContentView(R.layout.detail_layout);			
		connectToNatureService();
		initComponents();	
	}
	
	public void onResume(){
		Log.v(TAG, "OnResume");
		super.onResume();				
		initReceiver();		
	}
	
	public void onPause(){
		Log.v(TAG, "OnPause unregister progress receiver");
		super.onPause();		
		unregisterReceiver(progressReceiver);
		overridePendingTransition(R.anim.hold, R.anim.push_right_out);
	}
		
	public void onStop(){
		Log.v(TAG, "OnStop");
		super.onStop();		
	}
	
	public void onDestroy(){
		Log.v(TAG, "Destroy");
		super.onDestroy();
		if(natureBinder != null){
			unbindService(serviceConnection);
		}
	}	

	private void initComponents(){		
		tvTitle = (TextView) findViewById(R.id.tvTitle);
		currentMusic = getIntent().getIntExtra(CURRENT_MUSIC,0);
		tvTitle.setText(musicList.get(currentMusic).getTitle());	
		
		tvDuration = (TextView) findViewById(R.id.tvDuration);
		int max = getIntent().getIntExtra(MUSIC_LENGTH, 0);
		tvDuration.setText(FormatHelper.formatDuration(max));
		
		pbDuration = (SeekBar) findViewById(R.id.pbDuration);
		pbDuration.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
			
			@Override
			public void onStopTrackingTouch(SeekBar seekBar) {}
			
			@Override
			public void onStartTrackingTouch(SeekBar seekBar) {}
			
			@Override
			public void onProgressChanged(SeekBar seekBar, int progress,
					boolean fromUser) {		
				if(fromUser){
					natureBinder.changeProgress(progress);
				}
			}
		});
		pbDuration.setMax(max/1000);
					
		currentPosition = getIntent().getIntExtra(CURRENT_POSITION,0);
		pbDuration.setProgress(currentPosition / 1000);
		
		tvTimeElapsed = (TextView) findViewById(R.id.tvTimeElapsed);
		tvTimeElapsed.setText(FormatHelper.formatDuration(currentPosition));
				
		for(int resId : btnResIds){
			CustomAudioIcon icon = (CustomAudioIcon)findViewById(resId);
			icon.setOnClickListener(this);
		}				
	}	
	
	private void initReceiver(){
		progressReceiver = new ProgressReceiver();	
		IntentFilter intentFilter = new IntentFilter();
		intentFilter.addAction(NatureService.ACTION_UPDATE_PROGRESS);
		intentFilter.addAction(NatureService.ACTION_UPDATE_DURATION);
		intentFilter.addAction(NatureService.ACTION_UPDATE_CURRENT_MUSIC);
		registerReceiver(progressReceiver, intentFilter);
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {		
		case R.id.btnStartStop:		
			play(currentMusic,R.id.btnStartStop);
			break;		
		case R.id.btnNext:
			natureBinder.toNext();
			break;
		case R.id.btnPrevious:
			natureBinder.toPrevious();
			break;
		case R.id.btnExit:	
			finish();
			break;
		case R.id.btnMode:						
			natureBinder.changeMode();
			break;
		default:
			break;
		}			
	}
	
	private void play(int currentMusic, int resId){
		CustomAudioIcon btnStartStop = (CustomAudioIcon) findViewById(resId);
		if(btnStartStop.isStartStatus()){
			natureBinder.stopPlay();
		}else{
			natureBinder.startPlay(currentMusic,currentPosition);
		}
	}

	
	class ProgressReceiver extends BroadcastReceiver{

		@Override
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();
			if(NatureService.ACTION_UPDATE_PROGRESS.equals(action)){
				int progress = intent.getIntExtra(NatureService.ACTION_UPDATE_PROGRESS, currentPosition);				
				if(progress > 0){
					currentPosition = progress; // Remember the current position
					tvTimeElapsed.setText(FormatHelper.formatDuration(progress));
					pbDuration.setProgress(progress / 1000);
				}
			}else if(NatureService.ACTION_UPDATE_CURRENT_MUSIC.equals(action)){
				//Retrieve the current music and get the title to show on top of the screen.
				currentMusic = intent.getIntExtra(NatureService.ACTION_UPDATE_CURRENT_MUSIC, 0);					
				tvTitle.setText(musicList.get(currentMusic).getTitle());
			}else if(NatureService.ACTION_UPDATE_DURATION.equals(action)){
				//Receive the duration and show under the progress bar
				//Why do this ? because from the ContentResolver, the duration is zero.
				int duration = intent.getIntExtra(NatureService.ACTION_UPDATE_DURATION, 0);
				tvDuration.setText(FormatHelper.formatDuration(duration));
				pbDuration.setMax(duration / 1000);						
			}
		}
		
	}
实现的功能也是类似的,不过为了实现动画效果斜切入的一个功能,我们可以看到在OnCreate函数和OnPause函数中,分别有如下两行代码:
    @Override	
    public void onCreate(Bundle savedInstanceState){
        ...		
        overridePendingTransition(R.anim.push_right_in,R.anim.hold);
        ...
    }
    @Override
    public void onPause(){ 
        ...
        overridePendingTransition(R.anim.hold, R.anim.push_right_out)
        ;
    }

这就是为了实现动画斜切效果的一个函数,进入当前Activity的效果是要在OnCreate的SetContentView之前,也就是在添加View到窗口展示前要把这个效果设置进去,而离开当前的Activity的效果要在OnPause的时候,也就是OnStop之前,在界面不见之前把效果给设置进去。
这个函数有两个参数,其中第一个参数是进入下一个Activity的动画,后一个参数是离开当前Activity的动画。


 public void overridePendingTransition(int enterAnim, int exitAnim) {
        try {
            ActivityManagerNative.getDefault().overridePendingTransition(
                    mToken, getPackageName(), enterAnim, exitAnim);
        } catch (RemoteException e) {
        }
    }

我们再来看一下动画效果的设置,是利用xml来设置的,
res/anim/push_right_in.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
	<translate android:fromYDelta="100%p" android:toYDelta="0" android:fromXDelta="100%p" 
	    	   android:toXDelta="0" android:duration="500"/>
	<rotate android:fromDegrees="90" android:toDegrees="0" 
	    	  android:pivotX="100%p" android:pivotY="100%p" android:duration="500"/>
	<alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="500" />
</set>

res/anim/push_right_out.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
	<translate android:fromXDelta="0" android:toXDelta="100%p" 
	    android:fromYDelta="0" android:toYDelta="100%p" android:duration="500"/>
	<rotate android:fromDegrees="0" android:toDegrees="90" 
	    android:pivotX="100%p" android:pivotY="100%p" android:duration="500"/>
	<alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="500" />
</set>

res/anim/hold.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
	<translate android:fromXDelta="0" android:toXDelta="0" android:duration="500"/>	
</set>

其中push_right_in 和 push_right_out实现的是相反的效果,一个是从右边斜切进来,一个是斜切出去,而hold是为了保证MainActivity不动的。
而rotate效果中的,pivotX和pivotY是3.0之后才支持的,所以在2.X中的没办法实现这个效果的。

关于界面显示的大概就是这么多了,有一些逻辑是跟Service相关的,下一篇讲Service的实现的时候再提一些。


Android 音乐播放器的实现(二)界面的实现

上一篇:Android 内存相关 onTrimMemory,onLowMemory


下一篇:内存不足时Android 系统如何Kill进程