音乐播放之进度条
[前提]
* android 自身也提供了该接口 似乎是:MediaController 但看过截图 发现极丑 所以今天就自己写了一个 现于诸位分享分享
[要求]
1. 进度条控件打算使用系统提供的SeekBar
2. SeekBar 要支持拖拉功能 即:定点播放
3. SeekBar 要反映播放位置 即:播放到哪 SeekBar 就在哪
[原理]
1. 音乐定点播放:MediaPlayer.seekTo(int msecond) //单位:毫秒
2. 音乐文件播放时间:MediaPlayer.getDuration()
3. SeekBar 获取位置:SeekBar.getProgress()
4. SeekBar 最大值: SeekBar.getMax()
[代码 步骤]
1. 定义界面:main.xml
1 * Button : 播放控制 如:暂停 继续 1 * TextView : 显示播放百分比 1 * SeekBar : 进度条 1 * RadioGroup : 显示所有sdcard 音乐文件
<?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" > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" > <Button android:id="@+id/cmd" android:text="Loading..." android:layout_width="90dip" android:layout_height="wrap_content" android:singleLine="true" /> <TextView android:id="@+id/progress" android:text="Progress.." android:layout_width="50dip" android:layout_height="fill_parent" android:gravity="center" android:singleLine="true" /> </LinearLayout> <SeekBar android:id="@+id/seekb" android:max="100" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout>
2. View 初始化
public void initialize(){
sBar = (SeekBar)findViewById(R.id.seekb);
rGroup = (RadioGroup)findViewById(R.id.radio);
cmdButton = (Button)findViewById(R.id.cmd);
mPlayer = new MediaPlayer();
}
3. 拖动SeekBar 且播放指定位置的音乐
sBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener(){
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// TODO Auto-generated method stub
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
int dest = seekBar.getProgress();
int mMax = mPlayer.getDuration();
int sMax = sBar.getMax();
mPlayer.seekTo(mMax*dest/sMax);
}
});
4. 刷新播放位置 且使其实时变化
//因为MediaPlayer没有播放进度的回调函数 所以只能:开辟一个Thread 定时通知其刷新
public void startProgressUpdate(){
//开辟Thread 用于定期刷新SeekBar
DelayThread dThread = new DelayThread(100);
dThread.start();
}
而该Thread 具体实现为:
private Handler mHandle = new Handler(){
@Override
public void handleMessage(Message msg){
int position = mPlayer.getCurrentPosition();
int mMax = mPlayer.getDuration();
int sMax = sBar.getMax();
sBar.setProgress(position*sMax/mMax);
}
};
public class DelayThread extends Thread {
int milliseconds;
public DelayThread(int i){
milliseconds = i;
}
public void run() {
while(true){
try {
sleep(milliseconds);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mHandle.sendEmptyMessage(0);
}
}
}
5. emulator 运行截图:
但是发现拖动实现定点播放都没有问题
就是没有实时更新SEEKBAR
不知道是我添加错哪里了~
但是发现拖动实现定点播放都没有问题
就是没有实时更新SEEKBAR
不知道是我添加错哪里了~
可能是因为你没有启动DelayThread所致吧 你在Activity::onCreate() 最后加上如下代码:
startProgressUpdate()
无限感谢~~自己做的player又完善了一点~~
看到你們好牛。。奮鬥啊!
把音乐的总时间设置成进度条的最大值
musicProgressBar.setMax(mp.getDuration());
这样你在进度条上拖动的位置直接就可以定位到毫秒了
public void onStopTrackingTouch(SeekBar seekBar) {
mp.seekTo(musicProgressBar.getProgress());
}
更新进度条也一样 直接把当前播放的时间定位到进度条就可以了
musicProgressBar.setProgress(mp.getCurrentPosition());
public class SettingsDialog extends Dialog implements android.view.View.OnClickListener {
public SettingsDialog(XMPPClient xmppClient) {
super(xmppClient);
}
protected void onStart() {
super.onStart();
setContentView(R.layout.settings);
getWindow().setFlags(4, 4);
setTitle("XMPP Settings");
Button ok = (Button) findViewById(R.id.ok);
ok.setOnClickListener(this);
}
public void onClick(View v) {
String host = getText(R.id.host);
String port = getText(R.id.port);
String service = getText(R.id.service);
String username = getText(R.id.userid);
String password = getText(R.id.password);
dismiss();
}
private String getText(int id) {
EditText widget = (EditText) this.findViewById(id);
return widget.getText().toString();
}
}
使用
setup.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
mHandler.post(new Runnable() {
public void run() {
mDialog.show();
}
});
}
});
以下代码用于实现PIM Event的增加、删除,修改,它的增加、删除功能我的P990上工作得很好,但修改功能会导致异常退出,而在k750上无论增加、修改、删除都没有效果(虽然也会引发安全性提问)。
这段代码我花精力最多的是重复部分,主要是没有注意它的常量与Calandar的常量表示并不相同。
至于修改的异常问题和k750上没效果,哪位仁兄可以给分析一下呢?
// 更新事务提醒
public boolean updatePIMEvent(){
EventList events = null;
try {
Resource.appendLog("Start updatePIMEvent", Resource.MESSAGE_LOG);
// 打开事务列表
events = (EventList)PIM.getInstance().openPIMList(PIM.EVENT_LIST, PIM.READ_WRITE);
// 根据情况进行不同处理
if(rpUID == null){
if(repeatID == -2){
Resource.appendLog("pre call appendEvent", Resource.MESSAGE_LOG);
return appendEvent(events); // 增加
}
}else {
if(repeatID == -2){
Resource.appendLog("pre call updateEvent", Resource.MESSAGE_LOG);
return updateEvent(events); // 更新
}else{
Resource.appendLog("pre call removeEvent", Resource.MESSAGE_LOG);
return removeEvent(events); // 移除
}
}
Resource.appendLog("error updatePIMEvent", Resource.ERROR_LOG);
return false;
} catch (Exception e){
//e.printStackTrace();
return false;
}finally{
if(events != null){
try {
events.close();
} catch (Exception e){
//e.printStackTrace();
}
}
}
}
// 创建事务提醒
private boolean appendEvent(EventList events) throws PIMException {
Event event = events.createEvent();
// 开始日期+响铃时间
if(events.isSupportedField(Event.START)){
event.addDate(Event.START, PIMItem.ATTR_NONE, rpStartDate + DateUtil.rawOffset);
}
// 结束日期
if(events.isSupportedField(Event.END)){
event.addDate(Event.END, PIMItem.ATTR_NONE, rpStartDate);
}
// 提醒摘要
if(events.isSupportedField(Event.SUMMARY)){
event.addString(Event.SUMMARY, PIMItem.ATTR_NONE, rpNotes == null ? "" : rpNotes);
}
// 提醒备注
if(events.isSupportedField(Event.NOTE)){
event.addString(Event.NOTE, PIMItem.ATTR_NONE, summarize == null ? "" : summarize);
}
// 提前响铃时间
if (events.isSupportedField(Event.ALARM)) {
event.addInt(Event.ALARM, PIMItem.ATTR_NONE, 300);
}
//event.setRepeat(getRepeatRule());
setRepeatRule(event);
// 提交事务提醒
event.commit();
// 保存事务提醒ID
rpUID = event.getString(Event.UID, 0);
Resource.appendLog("Append ok UID: " + rpUID, Resource.MESSAGE_LOG);
return true;
}
// 更新事务提醒
private boolean updateEvent(EventList events) throws PIMException {
Event event = seekEvent(events);
if(event != null){ // 执行更新
// 开始日期+响铃时间
if(events.isSupportedField(Event.START)){
event.setDate(Event.START, 0, PIMItem.ATTR_NONE, rpStartDate + DateUtil.rawOffset);
}
// 结束日期
if(events.isSupportedField(Event.END)){
event.setDate(Event.END, 0, PIMItem.ATTR_NONE, rpStartDate);
}
// 提醒摘要
if(events.isSupportedField(Event.SUMMARY)){
event.setString(Event.SUMMARY, 0, PIMItem.ATTR_NONE, rpNotes == null ? "" : rpNotes + " - old");
}
// 提醒备注
if(events.isSupportedField(Event.NOTE)){
event.setString(Event.NOTE, 0, PIMItem.ATTR_NONE, summarize == null ? "" : summarize);
}
// 提前响铃时间
if (events.isSupportedField(Event.ALARM)) {
event.setInt(Event.ALARM, 0, PIMItem.ATTR_NONE, 300);
}
//event.setRepeat(null);
//event.setRepeat(getRepeatRule());
setRepeatRule(event);
// 提交事务提醒
event.commit();
Resource.appendLog("Update ok UID: " + rpUID, Resource.MESSAGE_LOG);
}else{ // 找不到旧记录则增加
return appendEvent(events);
}
return false;
}
// 移除事务提醒
private boolean removeEvent(EventList events) throws PIMException {
Event event = seekEvent(events);
if(event != null){ // 执行删除
event.setRepeat(null);
event.commit();
events.removeEvent(event);
Resource.appendLog("Remove ok UID:" + rpUID, Resource.MESSAGE_LOG);
rpUID = null;
return true;
}
return false;
}
// 查找事务提醒
private Event seekEvent(EventList events) throws PIMException {
// 枚举指定开始时间的事务提醒
//Enumeration items = events.items(EventList.STARTING, rpStartDate, 0, true);
Resource.appendLog("start seeking items", Resource.MESSAGE_LOG);
Enumeration items = events.items();
if(items != null && items.hasMoreElements()){
Resource.appendLog("items read ok", Resource.MESSAGE_LOG);
}
// 从中查找对应标识的事务提醒并返回
Event event = null;
while(items.hasMoreElements()){
event = (Event)items.nextElement();
if(rpUID.equals(event.getString(Event.UID, 0))){
Resource.appendLog("retrieve event ok", Resource.MESSAGE_LOG);
return event;
}
}
Resource.appendLog("retrieve event fail", Resource.MESSAGE_LOG);
return null;
}
// private RepeatRule getRepeatRule(){
// // 重复规则
// RepeatRule rpRule = new RepeatRule();
//
// Calendar calendar = Calendar.getInstance();
// calendar.setTime(new Date(rpStartDate));
// // 重复类型
// rpRule.setInt(RepeatRule.FREQUENCY, RepeatRule.DAILY + rpType);
// switch(rpType){
// case 0: // 每日提醒
// break;
// case 1: // 每周某天提醒
// rpRule.setInt(RepeatRule.DAY_IN_WEEK, 0x20000 >> calendar.get(Calendar.DAY_OF_WEEK));
// break;
// case 2: // 每月某天提醒
// rpRule.setInt(RepeatRule.DAY_IN_MONTH, calendar.get(Calendar.DAY_OF_MONTH));
// break;
// case 3: // 每年某天提醒
// rpRule.setInt(RepeatRule.MONTH_IN_YEAR, 0x10000 << calendar.get(Calendar.MONTH));
// rpRule.setInt(RepeatRule.DAY_IN_MONTH, calendar.get(Calendar.DAY_OF_MONTH));
// break;
// }
//
// // 重复频率
// rpRule.setInt(RepeatRule.INTERVAL, rpInterval);
// // 结束日期
// rpRule.setDate(RepeatRule.END, DateUtil.dayToDate(rpEndDay).getTime());
//
// return rpRule;
// }
private void setRepeatRule(Event event){
// 重复规则
RepeatRule rpRule = event.getRepeat();
if(rpRule == null){
rpRule = new RepeatRule();
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date(rpStartDate));
// 重复类型
rpRule.setInt(RepeatRule.FREQUENCY, RepeatRule.DAILY + rpType);
switch(rpType){
case 0: // 每日提醒
break;
case 1: // 每周某天提醒
rpRule.setInt(RepeatRule.DAY_IN_WEEK, 0x20000 >> calendar.get(Calendar.DAY_OF_WEEK));
break;
case 2: // 每月某天提醒
rpRule.setInt(RepeatRule.DAY_IN_MONTH, calendar.get(Calendar.DAY_OF_MONTH));
break;
case 3: // 每年某天提醒
rpRule.setInt(RepeatRule.MONTH_IN_YEAR, 0x10000 << calendar.get(Calendar.MONTH));
rpRule.setInt(RepeatRule.DAY_IN_MONTH, calendar.get(Calendar.DAY_OF_MONTH));
break;
}
// 重复频率
rpRule.setInt(RepeatRule.INTERVAL, rpInterval);
// 结束日期
rpRule.setDate(RepeatRule.END, DateUtil.dayToDate(rpEndDay).getTime());
event.setRepeat(rpRule);
}