音乐播放之进度条
[前提]
* 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); }