android 界面设计基本知识Ⅳ
上一章讲述了Android界面开发中的Widget,Service,BroadcastReceiver基本知识点,本章以一个实际案例-后台音乐播放器解析各个知识点之间的关系。
1.功能需求
做一个Android音乐播放器
- 用到Service、Broadcast Receiver、Widget
- 使用后台服务播放音乐
- 做一个音乐播放的Widget
- 显示歌曲名
- 显示上一首、下一首、播放、暂停
2.软件实现
图1
图2
图3
简易说明
长按手机界面出现如图2所示信息,弹出小部件选项,点击进入小部件如图1。选择极客班-作业四,根据提示拖放界面到桌面,出现如图3所示音乐播放界面。点击按钮可实现音乐切换播放歌曲功能。
3.实现流程
后台音乐播放器主要是通过Widget的特性,用广播为通道,完成Widget与Service之间的交互。
Widget:更新桌面信息,发送广播信息,接收用户操作事件。
Service:注册服务,接收广播信息,发送广播信息。
4.界面布局
音乐播放器在界面布局和配置文件中主要包括以下方面:
- appwidget-provider提供了桌面widget的初始化显示状态、默认图标、大小等功能,它放在res/xml文件夹下,参考项目中的music_app_widget_info.xml文件。
- android:initialLayout="@layout/music_app_widget":这个就是指定初始化显示时,应该显示的布局,参考项目中的music_app_widget.xml文件。
- 项目应用了服务,广播,须在AndroidManifest.xml中配置相应的标签。具体信息参考项目中的AndroidManifest.xml文件。
5.核心代码分析
(1)Widget函数-onUpdate()
在用户添加Widget时,会调用OnUpdate()函数,在OnUpdate()中要实现绑定RemoteView和更新Widget的操作。在绑定控件时,主要应用了PendingIntent的方式。
PendingIntent 是Intent的封装,在构造PendingIntent前,也要先构造一个Intent,并可以利用Intent 的属性传进去action,Extra等,同样,在接收时,对方依然是接收Intent的,而不是接收PaddingIntent。
PendingIntent.getBroadcast(context, 0,intent, 0);指从系统中获取一个用于可以发送BroadcastReceiver广播的PendingIntent对象。PendingIntent这个类用于 处理即将发生的事情。比如在通知Notification中用于跳转页面,但不是马上跳转。所以可以将它理解成
一个封装成消息的intent的。即这个intent并不是立即start,而是像消息一样被发送出去,等接收方接到以后,再分析里面的内容。项目相关代 码摘抄如下:

1 private PendingIntent getPendingIntent(Context context, int buttonId) { 2 Intent intent = new Intent(); 3 intent.setClass(context, ExampleAppWidgetProvider.class); 4 intent.addCategory(Intent.CATEGORY_ALTERNATIVE); 5 intent.setData(Uri.parse("harvic:" + buttonId)); 6 PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0); 7 return pi; 8 } 9 10 // 更新所有的 widget 11 private void pushUpdate(Context context,AppWidgetManager appWidgetManager,String songName,Boolean play_pause) { 12 13 RemoteViews remoteView = new RemoteViews(context.getPackageName(),R.layout.music_app_widget); 14 //将按钮与点击事件绑定 15 remoteView.setOnClickPendingIntent(R.id.play_pause,getPendingIntent(context, R.id.play_pause)); 16 remoteView.setOnClickPendingIntent(R.id.prev_song, getPendingIntent(context, R.id.prev_song)); 17 remoteView.setOnClickPendingIntent(R.id.next_song, getPendingIntent(context, R.id.next_song)); 18 19 //设置内容 20 if (!songName.equals("")) { 21 remoteView.setTextViewText(R.id.song_name, songName); 22 } 23 //设定按钮图片 24 if (play_pause) { 25 remoteView.setImageViewResource(R.id.play_pause, R.drawable.button_stop); 26 }else { 27 remoteView.setImageViewResource(R.id.play_pause, R.drawable.button_on); 28 } 29 // 相当于获得所有本程序创建的appwidget 30 ComponentName componentName = new ComponentName(context,ExampleAppWidgetProvider.class); 31 appWidgetManager.updateAppWidget(componentName, remoteView); 32 } 33 34 @Override 35 public void onUpdate(Context context, AppWidgetManager appWidgetManager, 36 int[] appWidgetIds) { 37 38 pushUpdate(context,appWidgetManager,"",false); 39 }
(2)Widget函数-onReceive()
接收用户操作,在接收时,首先根据当前用户点击的哪个按钮,然后给MusicManageService发送不同的广播(发送按钮id),让MusicManageService做出不同的响应,接收代码如下 :

1 // 接收广播的回调函数 2 @Override 3 public void onReceive(Context context, Intent intent) { 4 String action = intent.getAction(); 5 Log.d("harvic", "action:"+action); 6 if (intent.hasCategory(Intent.CATEGORY_ALTERNATIVE)) { 7 Uri data = intent.getData(); 8 int buttonId = Integer.parseInt(data.getSchemeSpecificPart()); 9 switch (buttonId) { 10 case R.id.play_pause: 11 pushAction(context,MusicManageService.ACTION_PLAY_PAUSE); 12 if(mStop){ 13 Intent startIntent = new Intent(context,MusicManageService.class); 14 context.startService(startIntent); 15 mStop = false; 16 } 17 break; 18 case R.id.prev_song: 19 pushAction(context, MusicManageService.ACTION_PRE); 20 break; 21 case R.id.next_song: 22 pushAction(context, MusicManageService.ACTION_NEXT); 23 break; 24 } 25 }else if (MAIN_UPDATE_UI.equals(action)){ 26 int play_pause = intent.getIntExtra(KEY_MAIN_ACTIVITY_UI_BTN, -1); 27 String songid = intent.getStringExtra(KEY_MAIN_ACTIVITY_UI_TEXT); 28 if(songid==null) songid="歌曲"; 29 switch (play_pause) { 30 case VAL_UPDATE_UI_PLAY: 31 pushUpdate(context, AppWidgetManager.getInstance(context), songid,true); 32 break; 33 case VAL_UPDATE_UI_PAUSE: 34 pushUpdate(context, AppWidgetManager.getInstance(context), songid,false); 35 break; 36 default: 37 break; 38 } 39 40 } 41 42 super.onReceive(context, intent); 43 }
(3)Widget发送广播
//发送按键广播 private void pushAction(Context context, int ACTION) { //发送目的服务 Intent actionIntent = new Intent(MusicManageService.ACTION); //发送参数 actionIntent.putExtra(MusicManageService.KEY_USR_ACTION, ACTION); context.sendBroadcast(actionIntent); }
(4)Service注册广播
服 务要与按钮相交互,在Service中一般是通过BroadcastReceiver来实现,所以在 MusicManageService的OnCreate函数中(Service起来的时候调用OnCreate)应该包括下面几个步骤:注册 Receiver。对应的项目代码如下:
1 private BroadcastReceiver receiver = new BroadcastReceiver() { 2 @Override 3 public void onReceive(Context context, Intent intent) { 4 String action = intent.getAction(); 5 if (ACTION.equals(action)) { 6 int widget_action = intent.getIntExtra(KEY_USR_ACTION, -1); 7 switch (widget_action){ 8 case ACTION_PRE: 9 playPrev(context);Log.d("harvic","action_prev");break; 10 case ACTION_PLAY_PAUSE: 11 if (mPlayState) { 12 pause(context);Log.d("harvic","action_pause"); 13 }else{ 14 play(context);Log.d("harvic","action_play"); 15 } 16 break; 17 case ACTION_NEXT:playNext(context);Log.d("harvic","action_next");break; 18 default:break; 19 }} 20 } 21 }; 22 @Override 23 public void onCreate() { 24 // TODO Auto-generated method stub 25 super.onCreate(); 26 IntentFilter intentFilter = new IntentFilter(); 27 intentFilter.addAction(ACTION); 28 //注册服务 29 registerReceiver(receiver, intentFilter); 30 //加载歌曲 31 initList(); 32 //播放默认歌曲 33 mediaPlayerStart(); 34 }
(5)歌曲定义
歌曲为本地歌曲文件,放在项目的raw目录下:
1 //歌曲播放 2 private void mediaPlayerStart(){ 3 mPlayer = new MediaPlayer(); 4 mPlayer = MediaPlayer.create(getApplicationContext(), mArrayList[mIndex]); 5 mPlayer.start(); 6 mPlayState = true; 7 //通过广播发送当前播放状态到Widget 8 postState(getApplicationContext(), ExampleAppWidgetProvider.VAL_UPDATE_UI_PLAY,mIndex); 9 } 10 //歌曲初始化 11 private void initList() { 12 //歌曲路径 13 mArrayList[0] = R.raw.night_two; 14 mArrayList[1] = R.raw.night_four; 15 mArrayList[2] = R.raw.night_five; 16 mArrayList[3] = R.raw.night_six; 17 //歌曲名称 18 mArrayListName[0] ="石进-夜的钢琴曲二"; 19 mArrayListName[1] = "石进-夜的钢琴曲四"; 20 mArrayListName[2] ="石进-夜的钢琴曲五"; 21 mArrayListName[3] = "石进-夜的钢琴曲六"; 22 }
(6)Service发送广播
歌曲通过Widget不同按钮切换发送广播到Service,Service通过播放功能函数(参考项目源代码)做出相应的播放功能切换处理以后,就又一次发送一个广播信息回馈给Widget,Widget接收到广播信息后进行更新处理,项目代码如下:
1 //回馈广播信息给Widget 2 private void postState(Context context, int state,int songid) { 3 //定义发送广播接收者 4 Intent actionIntent = new Intent(ExampleAppWidgetProvider.MAIN_UPDATE_UI); 5 //定义歌曲播放状态(播放/暂停) 6 actionIntent.putExtra(ExampleAppWidgetProvider.KEY_MAIN_ACTIVITY_UI_BTN,state); 7 //定义发送信息(歌曲名称) 8 actionIntent.putExtra(ExampleAppWidgetProvider.KEY_MAIN_ACTIVITY_UI_TEXT, mArrayListName[songid]); 9 context.sendBroadcast(actionIntent); 10 }
6.项目源代码下载
本项目源代码在360云盘上,开发环境为 Android Studio 2.0 beta 7。
https://yunpan.cn/cYamkZG3sq9jf 访问密码 f3d5
喜欢请赞赏一下啦^_^
微信赞赏
支付宝赞赏
作者:@wuya11
本文为作者原创,转载请注明出处:https://www.cnblogs.com/wlandwl/p/android_4.html
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
限于本人有限的知识水平,文中可能存在误解或错误(轻喷~),欢迎指出。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?