• Android小玩意儿-- 从头开发一个正经的MusicPlayer(三)


    MusicService已经能够接收广播,通过广播接收的内容来做出相应的MediaPlayer对象的处理,包括播放,暂停,停止等,并当MediaPlayer对象的生命周期发生变化的时候,同样通过发送广播,让UI层产生变换。现在后台处理已经写好。下面就来实现前台的Activity。

    1·构建UI布局框架##

    1.先构建一个RelativeLayout布局,指定一个背景。

    2.我的构想是把整个平面分为三部分,第一部分用来调节音量,因为音量调节常用。第二部分是音乐列表。第三部分是音乐控制按钮和音乐进度条。

    3.这三部分一步一步的做出来。先做第一部分调节音量的视图。

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    	    android:id="@+id/relativeLayout1"
    	    android:layout_width="fill_parent"
    	    android:layout_height="fill_parent" 
    	    android:background="@drawable/bg_mountain"
    	    >
    	    <LinearLayout 
    	        android:id="@+id/main_volumeLayout"
    	        android:layout_height="wrap_content"
    	        android:layout_width="fill_parent">
    	        <LinearLayout 
    	            android:layout_height="wrap_content"
    	            android:layout_width="fill_parent"
    	            android:layout_weight="1"></LinearLayout>
    	        <LinearLayout 
    	            android:layout_height="wrap_content"
    	            android:layout_width="fill_parent"
    	            android:layout_weight="1"
    	            android:gravity="center"
    	            android:orientation="horizontal">
    	            <TextView 
    	                android:id="@+id/main_tv_volumeText"
    	                android:layout_width="fill_parent"
    	                android:layout_height="wrap_content"
    	                android:layout_weight="1"
    	                android:text="音量:100%"
    	                android:textColor="#ffffffff"
    	                android:textSize="15dp"/>
    	            <SeekBar 
    	                android:id="@+id/main_sb_volumebar"
    	                android:layout_width="82dp"
    	                android:layout_height="wrap_content"
    	                android:maxHeight="5dip"
    	                android:minHeight="5dip"
    	                android:progressDrawable="@drawable/seekbar_style"
    	                />
    	        </LinearLayout>
    	    </LinearLayout>   
    </RelativeLayout>
    

    这一部分先这样布局,以后如果体验不好再重新修改。

    第二部分是歌曲列表的布局

    	<ListView 
    			android:id="@+id/main_listview"
    			android:layout_width="fill_parent"
    			android:layout_height="wrap_content"
    			android:layout_above="@+id/linearLayout1"
    			android:layout_below="@id/main_volumeLayout"
    			android:fastScrollEnabled="true"
    			android:layout_marginLeft="10dip"
    			android:layout_marginRight="10dip"
    			android:background="@drawable/widget_bg"
    			android:cacheColorHint="#00000000"></ListView>
    

    第三部分是音乐控制的布局。

                   <LinearLayout 
    		    android:id="@+id/linearLayout1"
    		    android:layout_width="fill_parent"
    		    android:layout_height="wrap_content"
    		    android:layout_alignParentBottom="true"
    		    android:layout_marginBottom="10dip"
    	        android:layout_marginLeft="10dip"
    	        android:layout_marginRight="10dip"
    	        android:background="@drawable/widget_bg"
    	        android:orientation="vertical"
    		    >
    		    <LinearLayout 
    		        android:id="@+id/linearLayout2"
    	            android:layout_width="fill_parent"
    	            android:layout_height="wrap_content"
    	            android:gravity="center">
    		        <ImageButton 
    		            android:id="@+id/main_ibtn_pre"
    		            android:layout_width="wrap_content"
    	                android:layout_height="wrap_content"
    	                android:layout_margin="10dip"
    	                android:background="@drawable/button_previous"/>
    		        <ImageButton
    	                android:id="@+id/main_ibtn_play"
    	                android:layout_width="wrap_content"
    	                android:layout_height="wrap_content"
    	                android:layout_margin="10dip"
    	                android:background="@drawable/button_play" />
    	
    	            <ImageButton
    	                android:id="@+id/main_ibtn_stop"
    	                android:layout_width="wrap_content"
    	                android:layout_height="wrap_content"
    	                android:layout_margin="10dip"
    	                android:background="@drawable/button_stop" />
    	
    	            <ImageButton
    	                android:id="@+id/main_ibtn_next"
    	                android:layout_width="wrap_content"
    	                android:layout_height="wrap_content"
    	                android:layout_margin="10dip"
    	                android:background="@drawable/button_next" />
    		    </LinearLayout>
    		    <SeekBar
    	            android:id="@+id/main_seekBar"
    	            android:layout_width="fill_parent"
    	            android:layout_height="wrap_content"
    	            android:paddingLeft="10dip"
    	            android:paddingRight="10dip" />
    		    <RelativeLayout 
    		        android:id="@+id/relativeLayout2"
    		        android:layout_width="fill_parent"
    	            android:layout_height="wrap_content">
    		        <TextView
    	                android:id="@+id/main_tv_curtime"
    	                android:layout_width="wrap_content"
    	                android:layout_height="wrap_content"
    	                android:layout_alignParentLeft="true"
    	                android:text="00:00" />
    		        <TextView
    	                android:id="@+id/main_tv_totaltime"
    	                android:layout_width="wrap_content"
    	                android:layout_height="wrap_content"
    	                android:layout_alignParentRight="true"
    	                android:text="00:00" />
    		    </RelativeLayout>
    		</LinearLayout>  
    

    这样整个UI的布局就完成了。

    2在之前的MainActivity的基础上继续开发##

    先实现最基础的功能,让音乐播放器能够播放,暂停,下一首,上一首,停止。要实现这个功能就要思考,怎么样才能让MusicService能够按照我们UI的状态变化来操纵MediaPlayer对象呢?最直观的一点就是,最起码要让我们的按钮都有响应了,所以要为我们的音乐控制按钮都加上事件监听器,比如,播放按钮,如果触发了播放按钮,就应该让监听器监听到播放按钮被按下,然后我们需要做的就是在用户按下按钮之后,在监听器下做出相应的相应。MusicService里的MediaPlayer对象有绑定了广播接收器。我们可以让按钮按下之后分发相应的广播。通过广播来通知Service。所以,要给各个按钮增加事件监听器,并分发广播。
    MainActivity.java
    package com.zharma.greatlovemusic;

    	import java.util.ArrayList;
    	import java.util.HashMap;
    	import java.util.List;
    	import java.util.Map;
    	import java.util.Timer;
    	import com.zharma.data.Music;
    	import com.zharma.data.MusicList;
    	import android.support.v7.app.ActionBarActivity;
    	import android.database.Cursor;
    	import android.os.Bundle;
    	import android.os.Handler;
    	import android.provider.MediaStore;
    	import android.view.Menu;
    	import android.view.MenuItem;
    	import android.view.View;
    	import android.view.View.OnClickListener;
    	import android.widget.AdapterView;
    	import android.widget.ImageButton;
    	import android.widget.ImageView;
    	import android.widget.ListView;
    	import android.widget.RelativeLayout;
    	import android.widget.SeekBar;
    	import android.widget.SeekBar.OnSeekBarChangeListener;
    	import android.widget.SimpleAdapter;
    	import android.widget.TextView;
    	import android.widget.AdapterView.OnItemClickListener;
    	
    	public class MainActivity extends ActionBarActivity {
    	
    		// 显示组件
    		private TextView tv_current_time;
    		private TextView tv_total_time;
    		private ImageButton imgBtn_Previous;
    		private ImageButton imgBtn_PlayOrPause;
    		private ImageButton imgBtn_Stop;
    		private ImageButton imgBtn_Next;
    		private SeekBar seekBar;
    		private ListView listView;
    		private RelativeLayout root_Layout;
    		
    		// 当前歌曲的持续时间和当前位置,作用于进度条
    		private int total_time;
    		private int curent_time;
    		
    		//当前歌曲的序号,下标从零开始
    		private int number;
    		
    		// 播放状态标志位
    		private int status;
    			
    		//歌曲列表对象
    		private ArrayList<Music> musicArrayList;
    		
    		//音量控制
    		private TextView tv_vol;
    		private SeekBar  seekbar_vol;
    		
    		// 进度条控制常量
    		private static final int PROGRESS_INCREASE = 0;
    		private static final int PROGRESS_PAUSE = 1;
    		private static final int PROGRESS_RESET = 2;
    		
    		// 更新进度条的Handler
    		private Handler seekBarHandler;
    		
    		//睡眠模式相关组件,标识常量
    		private ImageView iv_sleep;
    		private Timer timer_sleep ;
    		private static final boolean NOTSLEEP = false;
    		private static final boolean ISSLEEP = true;
    		
    		@Override
    		protected void onCreate(Bundle savedInstanceState) {
    			super.onCreate(savedInstanceState);
    			setContentView(R.layout.activity_main);
    			
    			findViews();
    			initMusicList();
    			initListView();
                                registerListeners();
                                checkMusicfile();
    
                                startService(new Intent(this, MusicService.class));
                                // 绑定广播接收器,可以接收广播
    	                bindStatusChangedReceiver();
                                sendBroadcastOnCommand(MusicService.COMMAND_CHECK_IS_PLAYING);
                                //初始化进度条的Handler
    	                initSeekBarHandler();
                                status = MusicService.COMMAND_STOP;
    		}
    		
    		void findViews() {
    			listView = (ListView) findViewById(R.id.main_listview);
    			tv_current_time = (TextView) findViewById(R.id.main_tv_curtime);
    			tv_total_time = (TextView) findViewById(R.id.main_tv_totaltime);
    			imgBtn_Previous = (ImageButton) findViewById(R.id.main_ibtn_pre);
    			imgBtn_PlayOrPause = (ImageButton) findViewById(R.id.main_ibtn_play);
    			imgBtn_Previous = (ImageButton) findViewById(R.id.main_ibtn_pre);
    			imgBtn_Next = (ImageButton) findViewById(R.id.main_ibtn_next);
    			imgBtn_Stop = (ImageButton) findViewById(R.id.main_ibtn_stop);
    			seekBar = (SeekBar) findViewById(R.id.main_seekBar);
    			root_Layout = (RelativeLayout) findViewById(R.id.relativeLayout1);
    			
    			tv_vol = (TextView)findViewById(R.id.main_tv_volumeText);
    			seekbar_vol = (SeekBar)findViewById(R.id.main_sb_volumebar);
    			iv_sleep = (ImageView)findViewById(R.id.main_iv_sleep);
    		}
    	
    		/**初始化音乐列表对象*/
    		private void initMusicList() {
    			musicArrayList = MusicList.getMusicList();
    			//避免重复添加音乐
    			if(musicArrayList.isEmpty())
    			{
    				Cursor mMusicCursor = this.getContentResolver().query(
    						MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null,
    						MediaStore.Audio.AudioColumns.TITLE);
    				int indexTitle = mMusicCursor.getColumnIndex(MediaStore.Audio.AudioColumns.TITLE);
    				int indexArtist = mMusicCursor.getColumnIndex(MediaStore.Audio.AudioColumns.ARTIST);
    				int indexTotalTime = mMusicCursor.getColumnIndex(MediaStore.Audio.AudioColumns.DURATION);
    				int indexPath = mMusicCursor.getColumnIndex(MediaStore.Audio.AudioColumns.DATA);
    	
    				/**通过mMusicCursor游标遍历数据库,并将Music类对象加载带ArrayList中*/
    				for (mMusicCursor.moveToFirst(); !mMusicCursor.isAfterLast(); mMusicCursor
    						.moveToNext()) { 
    					String strTitle = mMusicCursor.getString(indexTitle);
    					String strArtist = mMusicCursor.getString(indexArtist);
    					String strTotoalTime = mMusicCursor.getString(indexTotalTime);
    					String strPath = mMusicCursor.getString(indexPath);
    	
    					if (strArtist.equals("<unknown>"))
    						strArtist = "无艺术家";
    					Music music = new Music(strTitle, strArtist, strPath, strTotoalTime);
    					musicArrayList.add(music);
    				}
    			}
    		}
    		/**设置适配器并初始化listView*/
    		private void initListView() {
    			List<Map<String, String>> list_map = new ArrayList<Map<String, String>>();
    			HashMap<String, String> map;
    			SimpleAdapter simpleAdapter;
    			for (Music music : musicArrayList) {
    				map = new HashMap<String, String>();
    				map.put("musicName", music.getMusicName());
    				map.put("musicArtist", music.getMusicArtist());
    				list_map.add(map);
    			} 
    	
    			String[] from = new String[] { "musicName", "musicArtist" };
    			int[] to = { R.id.listview_tv_title_item, R.id.listview_tv_artist_item };
    	
    			simpleAdapter = new SimpleAdapter(this, list_map, R.layout.listview,from, to);
    			listView.setAdapter(simpleAdapter);
    		}
    	
    		private void registerListeners() {
    			imgBtn_Previous.setOnClickListener(new OnClickListener() {
    				@Override
    				public void onClick(View v) {
    					sendBroadcastOnCommand(MusicService.COMMAND_PREVIOUS);
    				}
    			});
    			
    			imgBtn_PlayOrPause.setOnClickListener(new OnClickListener() {
    				@Override
    				public void onClick(View v) {
    					switch (status) {
    					case MusicService.STATUS_PLAYING:
    						sendBroadcastOnCommand(MusicService.COMMAND_PAUSE);
    						break;
    					case MusicService.STATUS_PAUSED:
    						sendBroadcastOnCommand(MusicService.COMMAND_RESUME);
    						break;
    					case MusicService.COMMAND_STOP:
    						sendBroadcastOnCommand(MusicService.COMMAND_PLAY);
    						break;
    					default:
    						break;
    					}
    				}
    			});
    			
    			imgBtn_Stop.setOnClickListener(new OnClickListener() {
    				public void onClick(View view) {
    					sendBroadcastOnCommand(MusicService.COMMAND_STOP);
    				}
    			});
    			
    			imgBtn_Next.setOnClickListener(new OnClickListener() {
    				public void onClick(View view) {
    					sendBroadcastOnCommand(MusicService.COMMAND_NEXT);
    				}
    			});
    			
    			listView.setOnItemClickListener(new OnItemClickListener() {
    	
    				@Override
    				public void onItemClick(AdapterView<?> parent, View view,
    						int position, long id) {
    					number = position;
    					sendBroadcastOnCommand(MusicService.COMMAND_PLAY);
    				}
    			});
    			
    			seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
    				
    				@Override
    				public void onStopTrackingTouch(SeekBar seekBar) {
    					if (status == MusicService.STATUS_PLAYING) {
    						// 发送广播给MusicService,执行跳转 
    						sendBroadcastOnCommand(MusicService.COMMAND_SEEK_TO);
    						// 进度条恢复移动
    						seekBarHandler.sendEmptyMessageDelayed(PROGRESS_INCREASE,
    								1000);
    					}
    				}
    				
    				@Override
    				public void onStartTrackingTouch(SeekBar seekBar) {
    					// 进度条暂停移动
    					seekBarHandler.sendEmptyMessage(PROGRESS_PAUSE);
    				}
    				
    				@Override
    				public void onProgressChanged(SeekBar seekBar, int progress,
    						boolean fromUser) {
    					if (status != MusicService.STATUS_STOPPED) {
    						curent_time = progress;
    						// 更新文本
    						tv_current_time.setText(formatTime(curent_time));
    					}
    				}
    			});
    			
    		}
    	}
    

    现在主体已经写好,后面就是具体的各个实现方法。把发送广播的方法与格式化时间的方法实现如下:

                /** 发送命令,控制音乐播放。参数定义在MusicService类中 */
    private void sendBroadcastOnCommand(int command) {
    	Intent intent = new Intent(MusicService.BROADCAST_MUSICSERVICE_CONTROL);
    	intent.putExtra("command", command);
    	// 根据不同命令,封装不同的数据
    	switch (command) {
    	case MusicService.COMMAND_PLAY:
    		intent.putExtra("number", number);
    		break;
    	case MusicService.COMMAND_SEEK_TO:
    		intent.putExtra("time", curent_time);
    		break;
    	case MusicService.COMMAND_PREVIOUS:
    	case MusicService.COMMAND_NEXT:
    	case MusicService.COMMAND_PAUSE:
    	case MusicService.COMMAND_STOP:
    	case MusicService.COMMAND_RESUME:
    	default:
    		break;
    	}
                sendBroadcast(intent);
    }
    
        /**如果列表没有歌曲,则播放按钮不可用,并提醒用户*/
    private void checkMusicfile()
    {
    	if (musicArrayList.isEmpty()) {
    		imgBtn_Next.setEnabled(false);
    		imgBtn_PlayOrPause.setEnabled(false);
    		imgBtn_Previous.setEnabled(false);
    		imgBtn_Stop.setEnabled(false);
    		Toast.makeText(getApplicationContext(), "当前没有歌曲文件",Toast.LENGTH_SHORT).show();
    	} else {
    		imgBtn_Next.setEnabled(true);
    		imgBtn_PlayOrPause.setEnabled(true);
    		imgBtn_Previous.setEnabled(true);
    		imgBtn_Stop.setEnabled(true);
    	}
    }
    
        /** 绑定广播接收器 */
    private void bindStatusChangedReceiver() {
    	receiver = new StatusChangedReceiver();
    	IntentFilter filter = new IntentFilter(
    			MusicService.BROADCAST_MUSICSERVICE_UPDATE_STATUS);
    	registerReceiver(receiver, filter);
    }
    
        /** 内部类,用于播放器状态更新的接收广播 */
     class StatusChangedReceiver extends BroadcastReceiver {
    	public void onReceive(Context context, Intent intent) {
    		// 获取播放器状态
    		status = intent.getIntExtra("status", -1);
    		switch (status) {
    		case MusicService.STATUS_PLAYING:
    			String musicName = intent.getStringExtra("musicName");
    			String musicArtist = intent.getStringExtra("musicArtist");
    			seekBarHandler.removeMessages(PROGRESS_INCREASE);
    			curent_time = intent.getIntExtra("time", 0);
    			total_time = intent.getIntExtra("duration", 0);
    			number = intent.getIntExtra("number", number);
    			listView.setSelection(number);
    
    			seekBar.setProgress(curent_time);
    			seekBar.setMax(total_time);
    			seekBarHandler.sendEmptyMessageDelayed(PROGRESS_INCREASE, 1000);
    
    			tv_total_time.setText(formatTime(total_time));
    			imgBtn_PlayOrPause.setBackgroundResource(R.drawable.pause);
    		
    			// 设置Activity的标题栏文字,提示正在播放的歌曲
    			MainActivity.this.setTitle("正在播放:" + musicName + " "+ musicArtist);
    			break;
    		case MusicService.STATUS_PAUSED:
    			seekBarHandler.sendEmptyMessage(PROGRESS_PAUSE);
    			String string = MainActivity.this.getTitle().toString().replace("正在播放:", "已暂停:");
    			MainActivity.this.setTitle(string);
    			imgBtn_PlayOrPause.setBackgroundResource(R.drawable.play);
    			break;
    		case MusicService.STATUS_STOPPED:
    			curent_time = 0;
    			total_time = 0;
    			tv_current_time.setText(formatTime(curent_time));
    			tv_total_time.setText(formatTime(total_time));
    			seekBarHandler.sendEmptyMessage(PROGRESS_RESET);
    			MainActivity.this.setTitle("GracePlayer");
    			imgBtn_PlayOrPause.setBackgroundResource(R.drawable.play);
    			break;
    		case MusicService.STATUS_COMPLETED:
    			number = intent.getIntExtra("number", 0);
                   //顺序模式:到达列表末端时发送停止命令,否则播放下一首
    			if(playmode == MainActivity.MODE_LIST_SEQUENCE)
    			{
    				if(number == MusicList.getMusicList().size()-1) 											
    					sendBroadcastOnCommand(MusicService.STATUS_STOPPED);
    				else
    					sendBroadcastOnCommand(MusicService.COMMAND_NEXT);
    				}
                    //单曲循环
    			else if(playmode == MainActivity.MODE_SINGLE_CYCLE)
    				sendBroadcastOnCommand(MusicService.COMMAND_PLAY);
                    //列表循环:到达列表末端时,把要播放的音乐设置为第一首
    			else if(playmode == MainActivity.MODE_LIST_CYCLE)
    			{
    				    //然后发送播放命令。
    				if(number == musicArrayList.size()-1)
    				{
    					number = 0;
    					sendBroadcastOnCommand(MusicService.COMMAND_PLAY);
    				}
    				else sendBroadcastOnCommand(MusicService.COMMAND_NEXT);
    			}
                    //随机播放
               else if (playmode == MainActivity.MODE_LIST_RANDOM)
                {
                   Random random = new Random();
                   int randomnum = random.nextInt(listView.getCount());
                   number = randomnum;
                   sendBroadcastOnCommand(MusicService.COMMAND_PLAY);
                }
    				
       		   seekBarHandler.sendEmptyMessage(PROGRESS_RESET);
    			MainActivity.this.setTitle("GracePlayer");
    			imgBtn_PlayOrPause.setBackgroundResource(R.drawable.play);
    			break;
    		default:
    			break;
    		}
    	}
    }
    
        private void initSeekBarHandler() {
    	seekBarHandler = new Handler() {
    		public void handleMessage(Message msg) {
    			super.handleMessage(msg);
    
    			switch (msg.what) {
    			case PROGRESS_INCREASE:
    				if (seekBar.getProgress() < total_time) {
    					// 进度条前进1秒
    					seekBar.incrementProgressBy(1000);
    					seekBarHandler.sendEmptyMessageDelayed(
    							PROGRESS_INCREASE, 1000);
    					// 修改显示当前进度的文本
    					tv_current_time.setText(formatTime(curent_time));
    					curent_time += 1000;
    				}
    				break;
    			case PROGRESS_PAUSE:
    				seekBarHandler.removeMessages(PROGRESS_INCREASE);
    				break;
    			case PROGRESS_RESET:
    				// 重置进度条界面
    				seekBarHandler.removeMessages(PROGRESS_INCREASE);
    				seekBar.setProgress(0);
    				tv_current_time.setText("00:00");
    				break;
    			}
    		}
    	};
    }
    

    3·AndroidManifest.xml文件配置##

    最后还要把我们的Service加到配置文件里。

       <service
            android:name="com.zharma.greatlovemusic.MusicService"
            android:exported="true" >
            <intent-filter>
                <action android:name="VideoService.START_Video_SERVICE" />
    
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </service>
    

    到目前为止,这个播放器已经实现了基本的音乐播放功能,后面有时间就会再加一些网络歌词获取,放到一个Activity里。搞个华丽的侧滑界面。弄个睡眠模式,播放模式之类的东西,让这个播放器看起来更正儿八经。

  • 相关阅读:
    springBoot(3)---目录结构,文件上传
    springBoot(2)---快速创建项目,初解jackson
    VueJs(14)---理解Vuex
    VueJs(13)---过滤器
    VueJs(12)---vue-router(导航守卫,路由元信息,获取数据)
    php多进程中的阻塞与非阻塞
    php 中的信号处理
    dede中arcurl的解析
    dede5.7 GBK 在php5.4环境下 后台编辑器无法显示文章内容
    php5.3 php-fpm 开启 关闭 重启
  • 原文地址:https://www.cnblogs.com/zharma/p/4576309.html
Copyright © 2020-2023  润新知