`
siyanglu
  • 浏览: 14982 次
  • 性别: Icon_minigender_2
社区版块
存档分类
最新评论

Android手机音乐播放器的初步实现

 
阅读更多



    习惯于在做事情的时候听一些熟悉的歌,你也许会说,这是一心二用,但有的时候却反而更能让人投入自己的工作中。至少对于我来说是这样,当然他人也许无法理解。或许是为了给自己找个正当的理由听音乐,所以做了这个安卓手机的播放器。在此之前由于搭档有意向的是另一个方面,于是在商量之后,我们决定将二者联合起来。当然,这里写的也许不算严格意义上的通信项目,播放器只是项目的一部分而已。下面是播放器的实现过程。

   这里利用Android jdk中的MediaPlayer这个类来实现。

   先看布局文件:

<?xml version="1.0" encoding="utf-8"?>
	<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
	<TextView
	android:layout_width="fill_parent"
    android:layout_height="wrap_content"
	android:text="@string/title"
	/>
	<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >
	<ImageView
	android:layout_x="7dip"
	android:layout_y="17dip"
	android:layout_width="198dip"
    android:layout_height="198dip"
    android:background="#ffffff"
	/>
	<Button
	android:layout_x="230dip"
	android:layout_y="125dip"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="playlist"
	android:id="@+id/playlist"    
    />
    <Button
	android:layout_x="230dip"
	android:layout_y="70dip"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="choose"
	android:id="@+id/choose"    
    />
	<LinearLayout
	android:layout_x="3dip"
	android:layout_y="230dip"
	android:layout_width="fill_parent"
	android:layout_height="65dip"
	android:orientation="vertical">
	<TextView
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	android:text="@string/songname"
	android:id="@+id/songname"	
	/>
	<TextView
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	android:text="@string/record"
	android:id="@+id/record"	
	/>
	<TextView
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	android:text="@string/artist"
	android:id="@+id/artist"	
	/>	
	</LinearLayout>	
	</AbsoluteLayout>
	
	<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:gravity="center_horizontal"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    >
    <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="start"
	android:id="@+id/start_pause"    
    />
    <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="stop"
	android:id="@+id/stop"    
    />
    <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="next"
	android:id="@+id/next"    
    />
    </LinearLayout>
 	<SeekBar android:id="@+id/seekbar"
 	android:layout_width="300dip"
	android:layout_height="20px" 
	android:layout_gravity="center"	
	 />
	 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:gravity="clip_horizontal"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    >
	 <TextView
	 android:layout_width="wrap_content"
	 android:layout_height="wrap_content" 		 
	 android:layout_gravity="left"
	 android:id="@+id/goTime"
	 android:text="@string/original_time"
	 />
	<TextView
	 android:layout_width="wrap_content"
	 android:layout_height="wrap_content" 		 
	android:paddingLeft="244dip"
	 android:id="@+id/totalTime"
	 android:text="@string/original_time"
	 />
	</LinearLayout>
</LinearLayout>

   在这边简单介绍一下。界面没有什么特别的,正是最为normal的一个播放器界面。上左是一个图片框,右面三个按钮。下方三排TextView用于显示歌曲信息,再下面是控制播放的按钮和进度条。

(这里说明一下,图片暂用白色背景代替,音乐播放总时间未获得到是由于本人系统重装,模拟器里的sd卡中无内容导致,与下面的方法无关。)

 

   下面是主界面的activity:

package lsy.mp20120211;

import android.app.Activity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;

public class MainPanel extends Activity implements Runnable {

	private MediaPlayer mp1 = new MediaPlayer();// 新建的一個播放器對象
	private Button start_pause, stop, next;
	private TextView goTime, totalTime, song, album, artist;
	private SeekBar seek;// 進度條
	private String[] total_tim = new String[2];// 這兩項和時間顯示有關
	private String[] current_tim = new String[2];
	private int t, i = 0;
	private android.os.Handler handler = new android.os.Handler();// 控制線程的Handler
	private boolean userChangeSeek;
	private boolean isStop = true;
	private mainhandler mhandler = new mainhandler();
	private final int CHANGE_SONG = 1;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		/** 得到各組件 */
		seek = (SeekBar) this.findViewById(R.id.seekbar);
		goTime = (TextView) this.findViewById(R.id.goTime);
		totalTime = (TextView) this.findViewById(R.id.totalTime);
		song = (TextView) this.findViewById(R.id.songname);
		album = (TextView) this.findViewById(R.id.record);
		artist = (TextView) this.findViewById(R.id.artist);
		start_pause = (Button) this.findViewById(R.id.start_pause);
		stop = (Button) this.findViewById(R.id.stop);
		next = (Button) this.findViewById(R.id.next);

		// 加载音乐队列
		Tools.addSongs();
		System.out.println("队列里的音乐个数为:" + Tools.songlist.size());

		// 啟動新建音樂的線程
		Thread tt = new Thread(this);
		tt.start();

		// 給按鈕添加監聽器
		start_pause.setOnClickListener(oc1);
		stop.setOnClickListener(oc2);
		next.setOnClickListener(oc3);
		// 進度條拖動的監聽器
		seek.setOnSeekBarChangeListener(osl);
	}


/** 程序運行的run方法 */
	public void run() {
		while (i < Tools.songlist.size()) {
			if (isStop) {
				System.out.println("停止播放" + isStop);
				mhandler.sendEmptyMessage(CHANGE_SONG);
				newPlayer(Tools.songlist.get(i).getUrl());
				System.out.println("完成一遍" + i);
			}
		}
	}



/**
* 新建一個播放器的操作
 */
public void newPlayer(String url) {
	// 新建一個播放器
	isStop = false;
	try {
		//mp1.reset();
		mp1.setDataSource(url);
		mp1.prepare();
		// 得到播放總时间(单位:毫秒)
		t = mp1.getDuration();
		System.out.println("t=" + t);
		// 轉成時間格式
		total_tim = changeInt2Time(t);
		// 顯示總時間
		String total_time = total_tim[0] + ":" +total_tim[1];
		totalTime.setText(total_time);

		// 如果直接下一首则不用点击直接播放
		if (start_pause.getText().equals("pause")) {
			// mp1.prepare();
			mp1.start();
			System.out.println("音乐播放中..." + mp1.isPlaying());
		}

		mp1.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
			public void onCompletion(MediaPlayer arg0) {
				seek.setProgress(0);
				if (Tools.songlist.size() == 0) {
					Toast.makeText(MainPanel.this, "结束", 1000).show();
					mp1.release();
				} else {
					isStop = true;
				}
			}
		});
	} catch (Exception e) {
		System.out.println("找不到文件!!!");
		e.printStackTrace();
	}
}

   这上面只是一部分。在这里要说的是,用 MediaPlayer.setDataSource(url)的方法来创建播放歌曲时,需执行MediaPlayer.prepare()方法,才能使MediaPlayer.getDuration()方法得到正确的音乐播放时间(未prepare时,该方法也能得到一个long型的整数,但是相对来说很大,这里不是很清楚得到的是什么值);而通过MediaPlayer.create()方法创建则不需要。

   下面要说的是控制播放按钮的监听器:

/** 開始和暫停的监听 */
	OnClickListener oc1 = new OnClickListener() {
		public void onClick(View v) {
			try {
				if (start_pause.getText().equals("start")) {
					if (mp1 != null) {
						mp1.stop();
					}
					// 計時
					seek.setMax(t);
					handler.post(time_thread);
					// 播放
					mp1.prepare();
					mp1.start();
					start_pause.setText("pause");
					System.out.println("音乐播放中..." + mp1.isPlaying());

				} else if (start_pause.getText().equals("pause")) {
					// 暫停
					mp1.stop();
					start_pause.setText("start");
					handler.removeCallbacks(time_thread);
					System.out.println("暂停" + mp1.isLooping());
				}
			} catch (Exception e) {
				System.out.println("播放发生异常...");
				e.printStackTrace();
			}
		}
	};
	/** 停止按鈕的監聽 */
	OnClickListener oc2 = new OnClickListener() {
		public void onClick(View v) {
			mp1.stop();
			handler.removeCallbacks(time_thread);
			seek.setProgress(0);
			isStop = true;
			goTime.setText("00:00");
			start_pause.setText("start");
		}
	};
	/** 下一首按鈕的監聽 */
	OnClickListener oc3 = new OnClickListener() {
		public void onClick(View v) {
			mp1.stop();
			seek.setProgress(0);
			goTime.setText("00:00");
			i = 1;
			isStop = true;
		}
	};

  

   播放过程中需要另一个线程来同步控制进度条的移动,这里用安卓特有的Handler来控制。

/** 時間進度條的線程 */
	private Runnable time_thread = new Runnable() {
		public void run() {
			// 設置進度
			int current = seek.getProgress() + 1000;
			seek.setProgress(current);
			// 設置目前播放時間
			int cur_time = mp1.getCurrentPosition();
			// 轉換格式使之顯示在介面上
			current_tim = changeInt2Time(cur_time);
			goTime.setText(current_tim[0] + ":" + current_tim[1]);
			// 控制線程
			handler.postDelayed(time_thread, 1000);
		}
	};

 

  此外,当玩家拖动进度条时,相应的歌曲进度也将随之匹配变化。

/** 拖動進度條時對進度條的監聽 */
	OnSeekBarChangeListener osl = new OnSeekBarChangeListener() {
		// 進度改變時的動作
		public void onProgressChanged(SeekBar seekBar, int progress,
				boolean fromUser) {
			fromUser = userChangeSeek;
			// 設置音樂播放與進度匹配
			mp1.seekTo(progress);
			current_tim = changeInt2Time(progress);
			goTime.setText(current_tim[0] + ":" + current_tim[1]);
		}

		// 觸摸進度條時的監聽
		public void onStartTrackingTouch(SeekBar seekBar) {
		}

		// 停止觸摸時的監聽
		public void onStopTrackingTouch(SeekBar seekBar) {
			userChangeSeek = true;
		}
	};

 最后,是一个刷新主线程UI的Handler。Android与普通java工程不同之处的在于,对于一个activity,它自带一个管理主界面的runnable,因此不允许其他的子线程随意刷新主界面。如果你尝试在自定义的线程中刷新界面,则系统会给你报出CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch its views这个错误。于是我们重新写了handleMessage的方法。


 

/** 新建一个主线程中的handler */
	private class mainhandler extends android.os.Handler {
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
			int what = msg.what;
			switch (what) {
			case CHANGE_SONG: {
				// 切換下一首歌曲時,用來刷新主界面UI
				song.setText("   歌曲:  " + Tools.songlist.get(i).getName());
				album.setText("   专辑:  " + Tools.songlist.get(i).getAlbum());
				artist.setText("艺术家:  " + Tools.songlist.get(i).getArtist());
			}
			}
		}
	}

 

   这里还有一个无关紧要的方法,也就是把时间的毫秒形式转换成我们习惯的格式。

/**
	 * 把時間的毫秒形式改成xx:xx
	 * 
	 * @param t
	 */
	public String[] changeInt2Time(int t) {
		int s, min = 0;
		String[] time = new String[2];
		String minutes, seconds;
		s = t / 1000;
		if (s >= 60) {
			min = s / 60;
			s = s % 60;
		}
		// 將分秒轉為字符串
		if (s < 10) {
			seconds = "0" + s;
		} else {
			seconds = s + "";
		}
		if (min < 10) {
			minutes = "0" + min;
		} else {
			minutes = min + "";
		}
		time[0] = minutes;
		time[1] = seconds;
		return time;
	}

 到了这里,一个简单的播放器就完成了。这里仅仅是整个项目的一部分,此外还有播放列表和歌曲的选择以及与电脑通信中歌曲的下载等等,这里暂时不写了。

 

  • 大小: 31 KB
分享到:
评论
3 楼 JuliaAilse 2012-03-11  
厉害啊!2楼正解啊,最好是用自己的照片当专辑封面,假装下是“我的专辑”。哈哈哈!
2 楼 云初静 2012-03-10  
我也想写个播放器了,哈哈。用自己写的播放器听歌,多爽呀,嘿嘿!之前老没有自己想做的东西。
1 楼 yangzhizhen 2012-03-10  
牛。。。。。

相关推荐

Global site tag (gtag.js) - Google Analytics