找遍weex及相应第三方框架,没发现有支持音乐播放的组件标签,则自己动手实现这一功能
效果如图(具体代码及样式在这里不提供)
通知栏
条件:
1.使用基于weex的第三方框架eros
2.会module扩展开发
3.有java及android开发基础
正文开始:
一.开发service
public class MusicService extends Service {
public final IBinder binder = new MyBinder();
public class MyBinder extends Binder{
public MusicService getService() {
return MusicService.this;
}
}
public static MediaPlayer mp = new MediaPlayer();
public MusicService() {
}
public void setVideoUrl(Context context, String videoUrl){
try {
YhTools.Log(videoUrl);
mp.reset();
Uri uri = Uri.parse(videoUrl);
mp.setDataSource(context,uri);
mp.prepare();
} catch (Exception e) {
YhTools.Log("can't get to the song");
e.printStackTrace();
}
}
public void start() {
mp.start();
}
public boolean isPlaying() {
return mp.isPlaying();
}
public void playOrPause() {
if(mp.isPlaying()){
mp.pause();
} else {
mp.start();
}
}
public void stop() {
if(mp != null) {
mp.stop();
try {
mp.prepare();
mp.seekTo(0);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void onDestroy() {
mp.stop();
mp.release();
super.onDestroy();
}
/**
* onBind 是 Service 的虚方法,因此我们不得不实现它。
* 返回 null,表示客服端不能建立到此服务的连接。
*/
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}
二.在Application中添加全局静态变量
public class App extends BMWXApplication {
private static Context appContext;
private static MusicService musicService = null;
private static LessonInfo lessonInfo = null;
.....此省略非重要代码
@Override
public void onCreate() {
super.onCreate();
try {
WXSDKEngine.registerModule("MusicModule", MusicModule.class);
} catch (WXException e) {
e.printStackTrace();
}
.....此省略非重要代码
appContext = getApplicationContext();
}
.....此省略非重要代码
public static Context getContext() {
return appContext;
}
public static void setMusicService(MusicService service){
musicService = service;
}
public static MusicService getMusicService(){
return musicService;
}
public static void setLessonInfo(LessonInfo lesson){
lessonInfo = lesson;
}
public static LessonInfo getLessonInfo(){
return lessonInfo;
}
}
三.module模块开发
public class MusicModule extends WXModule {
private static final String TAG = "ProgressNotification";
public final static String INTENT_BUTTONID_TAG = "ButtonId";
private static RemoteViews mRemoteViews = null;
private static NotificationManager notificationManager = null;
private static Notification notification = null;
private static NotificationCompat.Builder builder = null;
/**
* 通知栏按钮点击事件对应的ACTION(标识广播)
*/
public final static String ACTION_BUTTON = "com.notification.intent.action.ButtonClick";
/**
* 通知栏按钮广播
*/
public static ButtonBroadcastReceiver receiver;
/**
* 播放/暂停 按钮点击 ID
*/
public final static int BUTTON_PALY_ID = 1;
public final static int BUTTON_NEXT_ID = 2;
public final static int BUTTON_CLOSE_ID = 3;
private final int NOTIFICATION_ID = 0xa01;
private final int REQUEST_CODE = 0xb01;
private Context getContext(){
return App.getContext();
//return mWXSDKInstance.getContext();
//注意这里,不用mWXSDKInstance.getContext(),用是用App.getContext(),当weex页面切换时,操作还是同一个对像
}
private ServiceConnection sc = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
App.setMusicService(((MusicService.MyBinder) iBinder).getService());
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
App.setMusicService(null);
}
};
private void bindServiceConnection() {
Intent intent = new Intent(getContext(), MusicService.class);
getContext().startService(intent);
getContext().bindService(intent, sc, Activity.BIND_AUTO_CREATE);
}
private boolean isPlaying() {
boolean isPlaying = false;
if (App.getMusicService() != null) {
isPlaying = App.getMusicService().isPlaying();
}
return isPlaying;
}
private void setNogification() {
final LessonInfo lesson = App.getLessonInfo();
if (lesson != null) {
//导步获取通知栏中的图片
new AsyncTask<String, Void, Bitmap>() {
@Override
protected Bitmap doInBackground(String... params) {
try {
URL url = new URL(params[0]);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(6000);//设置超时
conn.setDoInput(true);
conn.setUseCaches(false);//不缓存
conn.connect();
int code = conn.getResponseCode();
Bitmap bitmap = null;
if (code == 200) {
InputStream is = conn.getInputStream();//获得图片的数据流
bitmap = BitmapFactory.decodeStream(is);
}
return bitmap;
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
@Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
if (result != null) {
showNotification(lesson, result);
}
}
}.execute(lesson.getThumb());
}
}
private void showNotification(LessonInfo lesson, Bitmap bitmap) {
int playIndex = lesson.getIndex();
if(notificationManager == null || builder==null || mRemoteViews==null) {
notificationManager = (NotificationManager) getContext().getSystemService(NOTIFICATION_SERVICE);
builder = new NotificationCompat.Builder(getContext());
mRemoteViews = new RemoteViews(getContext().getPackageName(), R.layout.notification_music);
}
//以上设置防止多次注册事件
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
if (receiver == null){
mRemoteViews = new RemoteViews(getContext().getPackageName(), R.layout.notification_music);
//注册广播
receiver = new ButtonBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_BUTTON);
getContext().registerReceiver(receiver, intentFilter);
//设置点击的事件
Intent buttonIntent = new Intent(ACTION_BUTTON);
/* 播放/暂停 按钮 */
buttonIntent.putExtra(INTENT_BUTTONID_TAG, BUTTON_PALY_ID);
PendingIntent intent_paly = PendingIntent.getBroadcast(getContext(), BUTTON_PALY_ID, buttonIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mRemoteViews.setOnClickPendingIntent(R.id.iv_play_pause, intent_paly);
/* 下一个 按钮 */
buttonIntent.putExtra(INTENT_BUTTONID_TAG, BUTTON_NEXT_ID);
PendingIntent intent_next = PendingIntent.getBroadcast(getContext(), BUTTON_NEXT_ID, buttonIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mRemoteViews.setOnClickPendingIntent(R.id.iv_next, intent_next);
/* 关闭 按钮 */
buttonIntent.putExtra(INTENT_BUTTONID_TAG, BUTTON_CLOSE_ID);
PendingIntent intent_close = PendingIntent.getBroadcast(getContext(), BUTTON_CLOSE_ID, buttonIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mRemoteViews.setOnClickPendingIntent(R.id.iv_close, intent_close);
}
}else{
//如果版本号低于(3.0),那么不显示按钮
mRemoteViews.setViewVisibility(R.id.iv_play_pause, View.GONE);
mRemoteViews.setViewVisibility(R.id.iv_next, View.GONE);
mRemoteViews.setViewVisibility(R.id.iv_close, View.GONE);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
if (isPlaying()) {
mRemoteViews.setImageViewResource(R.id.iv_play_pause, R.mipmap.voice_play);
} else {
mRemoteViews.setImageViewResource(R.id.iv_play_pause, R.mipmap.voice_pause);
}
mRemoteViews.setTextViewText(R.id.tv_title, lesson.getTitleList().get(playIndex));
mRemoteViews.setTextViewText(R.id.tv_name, lesson.getTeacher_name());
mRemoteViews.setImageViewBitmap(R.id.iv_thumb, bitmap);
}
Intent intent = new Intent(getContext(), WelcomeActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(getContext(), 1, intent, PendingIntent.FLAG_ONE_SHOT);
builder.setContent(mRemoteViews)
.setContentIntent(pendingIntent)
.setWhen(System.currentTimeMillis())// 通知产生的时间,会在通知信息里显示
.setTicker("正在播放")
.setPriority(Notification.PRIORITY_DEFAULT)// 设置该通知优先级
.setOngoing(true)
.setSmallIcon(R.drawable.logo);
notification = builder.build();
notification.flags = Notification.FLAG_ONGOING_EVENT;
notificationManager.notify(NOTIFICATION_ID, notification);
}
/**
* (通知栏中的点击事件是通过广播来通知的,所以在需要处理点击事件的地方注册广播即可)
* 广播监听按钮点击事件
*/
public class ButtonBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(ACTION_BUTTON)) {
//通过传递过来的ID判断按钮点击属性或者通过getResultCode()获得相应点击事件
int buttonId = intent.getIntExtra(INTENT_BUTTONID_TAG, 0);
switch (buttonId) {
case BUTTON_PALY_ID:
onPlayOrPauseBtnClick();
break;
case BUTTON_NEXT_ID:
onNextBtnClick(true,1);
break;
case BUTTON_CLOSE_ID:
onCloseBtnClick();
break;
default:
break;
}
}
}
}
/**
* 开启或暂停
*/
private void onPlayOrPauseBtnClick() {
App.getMusicService().playOrPause();
boolean isPlaying = isPlaying();
if (isPlaying) {
mRemoteViews.setImageViewResource(R.id.iv_play_pause, R.mipmap.voice_play);
} else {
mRemoteViews.setImageViewResource(R.id.iv_play_pause, R.mipmap.voice_pause);
}
notificationManager.notify(NOTIFICATION_ID, notification);
//通知前端变更状态
setPlayOrPauseGlobalEvent(isPlaying);
}
/**
* 下一课
* @param isStep
* @param num
*/
private void onNextBtnClick(boolean isStep,int num) {
LessonInfo lesson = App.getLessonInfo();
int playIndex = lesson.getIndex();
if (isStep){
playIndex += num;
}else{
playIndex = num;
}
if (playIndex < (lesson.getSectionidList().size())) {
lesson.setIndex(playIndex);
App.getMusicService().setVideoUrl(getContext(), lesson.getVideoUrlList().get(playIndex));
App.getMusicService().start();
setNogification();
//通知前端变更状态
setPreOrNextGlobalEvent(playIndex);
}
}
/**
* 关闭音频
*/
public void onCloseBtnClick() {
App.getMusicService().stop();
App.setLessonInfo(null);
/**
* 关闭通知
*/
if (receiver != null) {
getContext().unregisterReceiver(receiver);
receiver = null;
}
if (notificationManager != null) {
notificationManager.cancel(NOTIFICATION_ID);
}
//通知前端变更状态
setCloseVoiceGlobalEvent();
}
public void initService() {
if (App.getMusicService() == null) {
App.setMusicService(new MusicService());
bindServiceConnection();
}
}
/**
* 前台-开始播放
*
* @param
*/
@JSMethod(uiThread = true)
public void play(Map data) {
if (data != null && data.size() > 0) {
int id = Integer.valueOf(data.get("id").toString());
String thumb = data.get("thumb").toString();
String teacherName = data.get("teacher_name").toString();
int playIndex = Integer.valueOf(data.get("index").toString());
String[] titleArr = data.get("titlestr").toString().split(",");
String[] sectionidArr = data.get("sectionidstr").toString().split(",");
String[] videourlArr = data.get("videourlstr").toString().split(",");
String videoUrl = videourlArr[playIndex];
if (!TextUtils.isEmpty(videoUrl)) {
initService();
App.getMusicService().setVideoUrl(getContext(), videoUrl);
App.getMusicService().start();
LessonInfo lessonInfo = new LessonInfo();
lessonInfo.setId(id);
lessonInfo.setThumb(thumb);
lessonInfo.setTeacher_name(teacherName);
lessonInfo.setIndex(playIndex);
lessonInfo.setSectionidList(Arrays.asList(sectionidArr));
lessonInfo.setTitleList(Arrays.asList(titleArr));
lessonInfo.setVideoUrlList(Arrays.asList(videourlArr));
App.setLessonInfo(lessonInfo);
setNogification();
}
}
}
/**
* 前台-开始/暂停播放
*
* @param
*/
@JSMethod(uiThread = true)
public void playOrPause() {
onPlayOrPauseBtnClick();
}
/**
* 前台-上一课或下一课
*
* @param
*/
@JSMethod(uiThread = true)
public void preOrNext(int index) {
if (index>=0) {
onNextBtnClick(false,index);
}
}
/**
* 停止播放
*
* @param
*/
@JSMethod(uiThread = true)
public void stop() {
onCloseBtnClick();
}
/**
* 判断是否正在播放,及最新播放的课程信息
*
* @param callback
*/
@JSMethod(uiThread = false)
public void checkLesson(JSCallback callback) {
boolean isPlaying = isPlaying();
Map<String, Object> map = new HashMap<>();
map.put("isPlaying", isPlaying);
map.put("lessonInfo", App.getLessonInfo());
//int flag = isPlaying?1:0;
callback.invokeAndKeepAlive(map);
}
//添加监听事件通知前端
public void setPlayOrPauseGlobalEvent(boolean isPlaying){
Map<String,Object> params=new HashMap<>();
params.put("playStaus",isPlaying?"1":"0");
List<WXSDKInstance> instances = WXSDKManager.getInstance().getWXRenderManager().getAllInstances();
for (WXSDKInstance instance : instances) {
instance.fireGlobalEventCallback("playOrPauseGlobalEvent",params);
}
}
public void setPreOrNextGlobalEvent(int playIndex){
Map<String,Object> params=new HashMap<>();
params.put("playIndex",playIndex);
//在weex端切换了页面后,直接使用wXSDKInstance是无法操作回调事件的,用以下方法解决
List<WXSDKInstance> instances = WXSDKManager.getInstance().getWXRenderManager().getAllInstances();
for (WXSDKInstance instance : instances) {
instance.fireGlobalEventCallback("preOrNextGlobalEvent",params);
}
}
public void setCloseVoiceGlobalEvent(){
List<WXSDKInstance> instances = WXSDKManager.getInstance().getWXRenderManager().getAllInstances();
for (WXSDKInstance instance : instances) {
instance.fireGlobalEventCallback("CloseVoiceGlobalEvent",null);
}
}
}
通知栏自定义样式
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="58dp"
>
<ImageView
android:id="@+id/iv_thumb"
android:layout_width="64dp"
android:layout_height="58dp"
android:src="@drawable/logo"/>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginLeft="8dp"
android:layout_weight="1">
<TextView
android:id="@+id/tv_title"
android:layout_marginTop="8dp"
android:text="著名情感专家绛妖精:10节婚姻必修课,跟上幸福的脚步"
android:lines="1"
android:textSize="14sp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_name"
android:text="某某专家"
android:textSize="12sp"
android:lines="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="8dp"/>
</RelativeLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="18dp">
<ImageView
android:id="@+id/iv_play_pause"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/voice_play"
android:layout_marginRight="20dp"/>
<ImageView
android:id="@+id/iv_next"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/voice_next"
android:layout_marginRight="20dp"/>
<ImageView
android:id="@+id/iv_close"
android:layout_width="18dp"
android:layout_height="18dp"
android:src="@mipmap/voice_close"
android:layout_marginTop="4dp"/>
</LinearLayout>
</LinearLayout>
四.AndroidManifest.xml添加service
<service android:name=".service.MusicService" android:exported="true"></service>
五.weex前端核心代码
1.在create中添加监听事件
//监听事件
var globalEvent = weex.requireModule('globalEvent');
//播放或暂停,当切换了页面,以下监听失去了效果
globalEvent.addEventListener("playOrPauseGlobalEvent", function (e) {
console.log("playOrPauseGlobalEvent:",e)
self.playing = e.playStaus == "1";
});
//上一课或下一课
globalEvent.addEventListener("preOrNextGlobalEvent", function (e) {
console.log("preOrNextGlobalEvent:",e)
if(e.playIndex>=0){
self.playing = true;
self.currentSectionIndex = e.playIndex;
}
});
//关闭
globalEvent.addEventListener("CloseVoiceGlobalEvent", function (e) {
console.log("CloseVoiceGlobalEvent:",e)
self.playing = false;
self.isClose = true;
});
2.播放功能
startLearn(){
var lessonInfo = {
id:this.id,
teacher_name:this.lessonInfo.teacher,
thumb:this.lessonInfo.images,
index:this.currentSectionIndex,
titlestr:this.titleArr.join(','),
sectionidstr:this.sectionidArr.join(','),
videourlstr:this.videourlArr.join(','),
}
console.log(lessonInfo)
this.playing = true;
weex.requireModule('MusicModule').play(lessonInfo);
}
3.暂停与开始
playOrPause(){
if(this.id!=0){
//this.playing = !this.playing;
if(this.isClose){
if(this.lessonInfo.section_list>0){
this.checkIsPlaying();
}
}else{
weex.requireModule('MusicModule').playOrPause();
}
}
}
4.上/下一章节
preOrNext(step){
if(!this.isClose) {
let self = this;
let index = this.currentSectionIndex + step;
var len = this.lessonInfo.section_list.length;
console.log("index", index)
if (index >= 0 && index < len) {
self.playing = false;
weex.requireModule('MusicModule').preOrNext(index);
} else {
this.$notice.toast({message: '没有更多课程了'});
}
}
}
5.当从其他页面跳转播放页面,页面加载完成时调用
checkIsPlaying(){
var _this = this;
weex.requireModule('MusicModule').checkLesson(function (ret) {
console.log('checkIsPlaying--',ret)
console.log('currentSectionIndex->0--',_this.currentSectionIndex)
if(!ret.isPlaying){
_this.startLearn();
}else{
if(typeof(ret.lessonInfo) != "undefined"){
//如果正在播放的与现在的不是同一课程,则切换到现在这个
if(ret.lessonInfo.id!=_this.id){
_this.startLearn();
}else{
//同个课程不同章节
if(parseInt(_this.currentSectionIndex) != parseInt(ret.lessonInfo.index)){
_this.playing = false;
weex.requireModule('MusicModule').preOrNext(_this.currentSectionIndex);
}
}
console.log('currentSectionIndex->1--',_this.currentSectionIndex)
_this.playing = true;
}
}
});
}
待完成功能
1.播放时间及进度条
2.封装为component组件或插件形式方便调用