編輯:關於Android編程
這個系列僅僅包含控制部分 , 不包含音頻操作代碼 , 如 pauseAudio(); 我不會說這個方法裡是怎麼操作的 , 大家需要結合自己的音頻播放處理來實現.
一個完整的多媒體播放器應該有的基礎功能:
通過耳機按鈕來控制歌曲 播放/暫停 上/下一首歌曲 當有線耳機/藍牙耳機 斷開連接和重新連接的時候 我們應該對應做出 暫停音頻 恢復音頻 使用系統提供的Notification.MediaStyle來控制歌曲 , 可以順利兼容 android 4.x ~ android 7.x 現在的系統樣式
> [[廣告]] https://github.com/ocwvar/DarkPurple > > 需要代碼樣例的同學可以在我的這個項目中查看 這個項目是個完整的音頻播放器 這裡的代碼均是從裡面提取 , 同時還有均衡器調節 頻譜動畫顯示等.. 目前正在不斷完善中 , 但由於上班 , 代碼更新可能不及時.
###1. 創建 MediaSessionCompat 對象
MediaSessionCompat sessionCompat = new MediaSessionCompat();
在這個構造方法中 , 我們使用這個:
MediaSessionCompat(Context context, String tag, ComponentName mbrComponent, PendingIntent mbrIntent) //第一個參數 context: 這個沒有什麼好講的,大家都懂的 //第二個參數 tag: 這個是用於調試用的,隨便填寫即可 //第三個參數 mbrComponent: 這個是用於API21以下的時候傳遞耳機按鈕事件用的MediaSessionCompat. //第四個參數 mbrIntent: 這個是給API21以下傳遞的時候攜帶的,一般設為 NULL即可 //例如: //其中的HeadsetButtonReceiver是我們的API21以下實用的監聽器 , 我們稍候再講 ComponentName cn = new ComponentName(this.context.getApplicationContext().getPackageName(), HeadsetButtonReceiver.class.getName()) sessionCompat = new MediaSessionCompat(this.context.getApplicationContext(), "test", cn, null);
sessionCompat = new MediaSessionCompat(...); //設置MediaSession回調監聽,主要用於設置API21+的耳機按鈕監聽 sessionCompat.setCallback(new MediaSessionCallback()); //設置FLAG,FLAG的用途一看名字就知道了 sessionCompat.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS); //設置MediaSession啟動 (很重要,不啟動則無法接受到數據) sessionCompat.setActive(true);
//創建完成後用MediaSessionCompat.setCallback設置上即可使用 private class MediaSessionCallback extends MediaSessionCompat.Callback { @Override public boolean onMediaButtonEvent(Intent mediaButtonEvent) { //接收到監聽事件 } }
//在代碼中創建一個單獨的類文件,而不能作為一個內部類 public class HeadsetButtonReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { //接收到監聽事件 } } //然後在注冊文件中注冊這個接收器//最後在創建MediaSessionCompat對象的時候使用即可
MediaStyle是什麼樣子的Notification呢?
在**Android 4.x**的是這樣的 ![這裡寫圖片描述](http://img.blog.csdn.net/20161107152026146)
在**Android 5.x ~ 6.x** 的是這樣的 ![這裡寫圖片描述](http://img.blog.csdn.net/20161107152048581)
在**Android 7.0** 的是這樣的 ![這裡寫圖片描述](http://img.blog.csdn.net/20161107152312833) MediaStyle使用的都是系統資源 , 除了布局之外 , 可以自定義的有: > 封面圖片 標題 副標題 按鈕樣式 背景顏色 使用MediaStyle的優點是不用擔心因系統變化而導致布局的變化 , 以及可以使用MediaSession作為我們的控制中介 , 雖然這部分不能自定義但個人認為還是挺值得的 .
對應的一個還有一個是Style是 **DecoratedMediaCustomViewStyle** 這個是可以提供部分自定義 , 但是修改部分僅是標題文字那部分的布局而已 , 並沒有什麼太大的作用 .
####**Notification.MediaStyle 創建代碼** PS:我們分開一段段講 ( 代碼太長了不好排版…. )
####**Part.1 基礎部分**
//創建 Notification 構建器 final NotificationCompat.Builder builder = new NotificationCompat.Builder(context); //創建 MediaStyle 對象 final NotificationCompat.MediaStyle mediaStyle = new NotificationCompat.MediaStyle(builder); //在折疊的視圖中顯示的按鈕序號 根據下面設置的ACTION順序相關 mediaStyle.setShowActionsInCompactView(0,1); //設置上面創建的MediaSession mediaStyle.setMediaSession(sessionCompat.getSessionToken()); //設置 MediaStyle 風格 builder.setStyle(mediaStyle); //不顯示默認的通知開始時間 builder.setShowWhen(false); //僅通知一次 builder.setOnlyAlertOnce(true); //設置為當前正在運行狀態,不能清除 builder.setOngoing(true); //設置點擊時不隱藏Notification builder.setAutoCancel(false); //設置狀態欄上面顯示的小圖標 builder.setSmallIcon(R.drawable.ic_action_small_icon); //設置鎖屏是否顯示 在 Android5.0+ 的鎖屏界面可以隱藏Notification內容 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { builder.setVisibility(Notification.VISIBILITY_PUBLIC); } //設置通知優先度,讓我們的 Notification 顯示在最上面 builder.setPriority(Notification.PRIORITY_MAX); //設置通知類別為服務 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { builder.setCategory(Notification.CATEGORY_SERVICE); } //點擊通知操作 使得用戶點擊Notification空白區域的時候打開指定的Activity Intent intent = new Intent(context, SelectMusicActivity.class); intent.setAction(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0); builder.setContentIntent(pendingIntent);
/** * 生成按鈕Action * * @param icon 圖標資源 * @param intentAction 按鈕產生的廣播Action * @return 按鈕Action */ private NotificationCompat.Action generateAction(int icon, String intentAction ) { return new NotificationCompat.Action( icon, intentAction, PendingIntent.getBroadcast(context, 0, new Intent(intentAction), PendingIntent.FLAG_CANCEL_CURRENT) ); } //根據播放狀態不同 , 設置不同主按鈕樣式 , 這裡的設置順序影響到折疊界面顯示的順序 //上一首 按鈕ACTION builder.addAction(generateAction(android.R.drawable.ic_media_previous,MediaNotificationReceiver.BUTTON_PREV)); switch (audioStatus) { case Paused: //播放 按鈕ACTION builder.addAction(generateAction(android.R.drawable.ic_media_play,MediaNotificationReceiver.BUTTON_PLAY)); break; case Playing: //暫停 按鈕ACTION builder.addAction(generateAction(android.R.drawable.ic_media_pause,MediaNotificationReceiver.BUTTON_PAUSE)); break; } //下一首 按鈕ACTION builder.addAction(generateAction(android.R.drawable.ic_media_next,MediaNotificationReceiver.BUTTON_NEXT));
####**Part.3 設置顯示歌曲信息**
//更新標題 builder.setContentTitle(songItem.getTitle()); //更新作者 builder.setContentText(songItem.getArtist()); //更新封面 builder.setLargeIcon( 歌曲封面的Bitmap對象 );
最後就 builder.build(); 得到最終的成品 Notification
我們需要在耳機拔出的時候 暫停音樂 在耳機重新插入的時候 恢復音樂
/** * 耳機插入廣播接收器 */ public class HeadsetPlugInReceiver extends BroadcastReceiver { final IntentFilter filter; public HeadsetPlugInReceiver() { filter = new IntentFilter(); if (Build.VERSION.SDK_INT >= 21) { filter.addAction(AudioManager.ACTION_HEADSET_PLUG); } else { filter.addAction(Intent.ACTION_HEADSET_PLUG); } } @Override public void onReceive(Context context, Intent intent) { if (intent != null && intent.hasExtra("state") && AppConfigs.isResumeAudioWhenPlugin) { //通過判斷 "state" 來知道狀態 final boolean isPlugIn = intent.getExtras().getInt("state") == 1; } } }
####耳機拔出/斷開連接 廣播接收器
/** * 耳機拔出廣播接收器 */ private class HeadsetReceiver extends BroadcastReceiver { final IntentFilter filter; final BluetoothAdapter bluetoothAdapter; public HeadsetReceiver() { filter = new IntentFilter(); filter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY); //有線耳機拔出變化 filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); //藍牙耳機連接變化 bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); } @Override public void onReceive(Context context, Intent intent) { if (isRunningForeground) { //當前是正在運行的時候才能通過媒體按鍵來操作音頻 switch (intent.getAction()) { case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED: if ( bluetoothAdapter != null && BluetoothProfile.STATE_DISCONNECTED == bluetoothAdapter.getProfileConnectionState(BluetoothProfile.HEADSET) && core.getCurrectStatus() == AudioCore.AudioStatus.Playing ) { //藍牙耳機斷開連接 同時當前音樂正在播放 則將其暫停 pause(); } break; case AudioManager.ACTION_AUDIO_BECOMING_NOISY: if (core.getCurrectStatus() == AudioCore.AudioStatus.Playing) { //有線耳機斷開連接 同時當前音樂正在播放 則將其暫停 pause(); } break; } } } }
最後用 registerReceiver() 來注冊廣播監聽器即可
我們需要在有電話來的時候和撥通電話的時候 暫停音頻 , 在通話結束之後 恢復音頻
要使得能接受到通話狀態 , 我們需要注冊一個權限 android.permission.READ_PHONE_STATE 這個權限在Android 6.0+上是需要用戶授予的
/** *電話狀態廣播接收器 */ public class PhoneStatusReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent != null && intent.getExtras() != null){ final String status = intent.getStringExtra(TelephonyManager.EXTRA_STATE); switch (status){ // "IDLE" 則代表通話結束或振鈴結束 case "IDLE": resume(); break; //其他狀態包括了撥通電話和新來電振鈴 default: pause(); break; } } } }
在代碼端寫完之後 , 我們還需要在注冊清單中注冊一下:
先看看效果圖:分析: 根據敵機類型區分 敵機 運動邏輯 以及繪制/** * 敵機 * * @author liuml * @time 2016-5-31 下午4:14:
實現功能:已存在歌曲歌詞下載後續將博文,將實現已下載音樂掃描功能。因為,沒有自己的服務器,所以網絡音樂所有相關功能(包含搜索音樂、下載音樂、下載歌詞)均無法保證時效性,建
一、Activity與Fragment之間通信1、Activity向Fragment傳值在Activity中使用setArguments封裝所需傳遞的值,在Fragmen
0x0 引言 我們知道,在Android上的Intent-based攻擊很普遍,這種攻擊輕則導致應用程序崩潰,重則可能演變提權漏洞。當然,通過靜態特征匹配,Intent