手把手教你做音乐播放器(八)桌面小工具(上)
来源:互联网 发布:科比两次得分王数据 编辑:程序博客网 时间:2024/05/17 08:58
第8节 桌面小工具
桌面小工具是可以放置在主界面的、快速控制应用的小助手。例如我们的音乐小工具,它可以帮助用户在桌面上就完成音乐的暂停、播放、切换等操作,而不需要启动应用本身。
在安卓系统中,我们也常常叫它App widget
。
实现一个App widget
要经过以下几个步骤,
- 创建一个
App widget
类,让它继承自AppWidgetProvider
,例如AnddleMusicAppWidget
类; - 放在
res\layout
目录下,为App widget
的界面定义一个布局,例如anddle_music_app_widget.xml
; - 在
res\xml
目录下,定义一个App widget
的说明文件,例如anddle_music_app_widget_info.xml
- 在
AndroidManifest.xml
文件当中,声明App widget
;
8.1 小工具框架的创建
对于使用Android Studio的用户来说,完成上面四个步骤的方法非常简单。因为Android Studio提供了简单的可视化向导来帮助我们。
在项目工程上点击右键,选择
new->Wideget->App Widget
就为我们创建好了开发用的模版,非常的简单方便。在弹出的创建向导中,填上创建的参数,
- 给
App widget
取个名字-AnddleMusicAppWidget
; - 允许将
App widget
放到主界面上; - 不允许
App widget
的显示区域动态调整; App widget
占用3格宽1格高;
这些参数最后将反应在
res/xml/anddle_music_app_widget.info.xml
这个配置文件当中,<?xml version="1.0" encoding="utf-8"?><appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:initialKeyguardLayout="@layout/anddle_music_app_widget" android:initialLayout="@layout/anddle_music_app_widget" android:minHeight="40dp" android:minWidth="180dp" android:previewImage="@mipmap/widget_thumb_sample" android:resizeMode="none" android:updatePeriodMillis="86400000" android:widgetCategory="home_screen"></appwidget-provider>
- 给
运行起来,就可以开始将App widget
添加到桌面了。
8.2 界面设计
现在,我们开始修改布局文件anddle_music_app_widget.xml
,将App widget
设计成如下的效果,
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/colorContract" android:padding="@dimen/widget_margin" android:orientation="horizontal" android:gravity="center_vertical"> <ImageView android:id="@+id/image_thumb" android:layout_width="60dp" android:layout_height="60dp" android:layout_margin="8dp" android:src="@mipmap/default_cover" android:scaleType="fitCenter" android:textColor="#ffffff" android:textSize="24sp" /> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/music_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFF" android:gravity="center" android:singleLine="true" android:layout_margin="5dp" android:text="@string/no_song"/> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:padding="5dp"> <Button android:id="@+id/play_btn2" android:layout_width="40dp" android:layout_height="40dp" android:layout_centerInParent="true" android:text="" android:background="@mipmap/ic_play" /> <Button android:id="@+id/pre_btn2" android:layout_width="40dp" android:layout_height="40dp" android:layout_marginRight="10dp" android:layout_toLeftOf="@id/play_btn2" android:layout_centerVertical="true" android:text="" android:background="@mipmap/ic_pre" /> <Button android:id="@+id/next_btn2" android:layout_width="40dp" android:layout_height="40dp" android:layout_marginLeft="10dp" android:layout_toRightOf="@id/play_btn2" android:layout_centerVertical="true" android:text="" android:background="@mipmap/ic_next" /> </RelativeLayout> </LinearLayout></LinearLayout>
然后修改anddle_music_app_widget_info.xml
的android:previewImage
属性,让它指向的图片变成预览的效果图,
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" ...... android:previewImage="@mipmap/widget_thumb_sample" ......></appwidget-provider>
8.3 AnddleMusicAppWidget的重建
AnddleMusicAppWidget
类负责App widget
的逻辑响应。
8.3.1 AppWidget原理
继承AppWidgetProvider
之后,开发者可以选择的实现onUpdate()
onAppWidgetOptionsChanged()
onDeleted()
onEnabled()
onDisabled()
和onRestored()
等多个函数,
public class AnddleMusicAppWidget extends AppWidgetProvider { @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { } @Override public void onEnabled(Context context) { } @Override public void onDisabled(Context context) { } @Override public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) { } @Override public void onDeleted(Context context, int[] appWidgetIds) { } @Override public void onRestored(Context context, int[] oldWidgetIds, int[] newWidgetIds) { }}
桌面上可以同时添加多个相同的App widget
,系统会为每个App widget
分配一个id
,以便将来更新它们或者对它们进行别的操作时,能够正确的区分它们。
假设现在桌面上没有我们音乐播放器对应的App widget
,此时添加一个的话,会依次触发,
onEnabled()onUpdate()
如果此时继续添加,会接着触发,
onUpdate()
如果系统要更新App widget
的显示,也会触发,
onUpdate()
此时删除一个,会触发,
onDeleted()
此时再删除最后一个,会触发,
onDeleted()onDisabled()
当设备开机,会触发,
onEnabled()onUpdate()
在App widget
的设计框架中,每个显示出来的小工具界面,叫做RemoteViews
,它是由如下方式创建的,
//第二个参数就是桌面小工具的布局文件RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.anddle_music_app_widget);
然后由RemoteViews
提供的设置方法,来设置界面上每个元素的样子,比如某个TextView
该显示什么文字,某个ImageView
该显示什么图片,某个Button
被点击后要触发什么操作等等。
然后,通过调用系统提供的AppWidgetManager.updateAppWidget()
,将这个界面更新到App widget
上面去,
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);//appWidgetId就是主界面上某个特定App widget的Id值,它会由onUpdate()函数传入appWidgetManager.updateAppWidget(appWidgetId, views);
所以AnddleMusicAppWidget
要提供一个对外调用的接口,让其它组件可以在需要的时候更新小工具界面。
8.3.2 App widget的界面元素
前面提到了RemoteViews
的创建方法。现在来详细介绍下界面上每个元素的设置方式,比如某个TextView
该显示什么文字,某个ImageView
该显示什么图片,某个Button
被点击后要触发什么操作等等。
8.3.2.1 设置TextView的文字
如果创建RemoteViews
的布局中有TextView
控件,并需要设置内容,
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.anddle_music_app_widget);//设置id为R.id.music_name的TextView,显示musicName代表的字符串views.setTextViewText(R.id.music_name, musicName);
8.3.2.2 设置ImageView的背景图片
如果创建RemoteViews
的布局中有ImageView
控件,并需要设置图片内容,
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.anddle_music_app_widget);//设置id为R.id.image_thumb的ImageView,显示thumb代表的bitmap图片views.setImageViewBitmap(R.id.image_thumb, thumb);
8.3.2.3 控件的其它属性
除了上面提到的控件和属性,控件的很多属性并专门的设置函数。不过RemoteViews
为我们引入了更加灵活的设置方式。例如要设置Button
的background
属性,
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.anddle_music_app_widget);//设置id为R.id.image_thumb的Button,调用它的setBackgroundResource()函数,//传入R.mipmap.ic_pause参数,相当于调用了Button的setBackgroundResource(R.mipmap.ic_pause)views.setInt(R.id.play_btn2, "setBackgroundResource", R.mipmap.ic_pause);
8.3.2.4 Button的响应
如果创建RemoteViews
的布局中有Button
控件,为它添加点击响应,
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.anddle_music_app_widget);//用隐士调用的方法-指定Action-创建一个IntentIntent preIntent = new Intent(MusicService.ACTION_PLAY_MUSIC_PRE);//设置响应的组件名称preIntent.setComponent(serviceName);//将Intent转化成PendingIntentPendingIntent prePendingIntent = PendingIntent.getService(context, 0, preIntent, 0);//设置id为R.id.pre_btn2的Button,设计点击后要触发的PendingIntentviews.setOnClickPendingIntent(R.id.pre_btn2, prePendingIntent);
RemoteViews
不能直接设置按钮点击后的操作,必须通过设置PendingIntent
来实现。所以要在响应的模块中添加响应的逻辑。
8.3.3 AnddleMusicAppWidget实现
继承
AppWidgetProvider
,准备实现onUpdate()
函数,并提供performUpdates()
的静态函数作为其它组件刷新界面的接口,搭建的代码结构如下,public class AnddleMusicAppWidget extends AppWidgetProvider { //保存各个小工具的id private static int [] sAppWidgetIds; @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { //保存各个小工具的id,以备将来其它组件调用performUpdates()接口时能用到 sAppWidgetIds = appWidgetIds; //更新小工具的界面 performUpdates(context, context.getString(R.string.no_song), false, null); } //对外提供的更新所有小工具的界面接口,需要传入音乐的名称、当前是否播放、音乐封面等参数 public static void performUpdates(Context context, String musicName, boolean isPlaying, Bitmap thumb) { //如果没有小工具的id,就没法更新界面 if(sAppWidgetIds == null || sAppWidgetIds.length == 0) { return; } AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); //遍历每个桌面上的小工具,根据id逐个更新界面 for (int appWidgetId : sAppWidgetIds) { updateAppWidget(context, appWidgetManager, appWidgetId, musicName, isPlaying, thumb); } } //更新指定id的小工具界面 private static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId, String musicName, boolean isPlaying, Bitmap thumb) { //创建RemoteViews RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.anddle_music_app_widget); //添加界面元素的逻辑控制代码,例如按钮、文字、图片等等 ...... //通过appWidgetId,为指定的小工具界面更新 appWidgetManager.updateAppWidget(appWidgetId, views); } ......}
实现对界面元素的逻辑控制,
“`java
public class AnddleMusicAppWidget extends AppWidgetProvider {......//更新指定id的小工具界面private static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId, String musicName, boolean isPlaying, Bitmap thumb) { //创建RemoteViews RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.anddle_music_app_widget); //添加界面元素的逻辑控制代码,例如按钮、文字、图片等等 //设置音乐的名称 views.setTextViewText(R.id.music_name, musicName); //设置按钮响应的对象,这里是MusicService final ComponentName serviceName = new ComponentName(context, MusicService.class); //设置下一首按钮对应的PendingIntent //通过MusicService.ACTION_PLAY_MUSIC_NEXT定义隐性Intent,唤醒MusicService的响应 Intent nextIntent = new Intent(MusicService.ACTION_PLAY_MUSIC_NEXT); nextIntent.setComponent(serviceName); PendingIntent nextPendingIntent = PendingIntent.getService(context, 0, nextIntent, PendingIntent.FLAG_UPDATE_CURRENT); views.setOnClickPendingIntent(R.id.next_btn2, nextPendingIntent); //设置前一首按钮对应的PendingIntent //通过MusicService.ACTION_PLAY_MUSIC_PRE定义隐性Intent,唤醒MusicService的响应 Intent preIntent = new Intent(MusicService.ACTION_PLAY_MUSIC_PRE); preIntent.setComponent(serviceName); PendingIntent prePendingIntent = PendingIntent.getService(context, 0, preIntent, 0); views.setOnClickPendingIntent(R.id.pre_btn2, prePendingIntent); //设置播放暂停按钮对应的PendingIntent //通过MusicService.ACTION_PLAY_MUSIC_TOGGLE定义隐性Intent,唤醒MusicService的响应 Intent toggleIntent = new Intent(MusicService.ACTION_PLAY_MUSIC_TOGGLE); toggleIntent.setComponent(serviceName); PendingIntent togglePendingIntent = PendingIntent.getService(context, 0, toggleIntent, 0); views.setOnClickPendingIntent(R.id.play_btn2, togglePendingIntent); //设置播放暂停按钮的图标 views.setInt(R.id.play_btn2, "setBackgroundResource", isPlaying ? R.mipmap.ic_pause : R.mipmap.ic_play); //设置音乐的封面 if(thumb != null) { views.setImageViewBitmap(R.id.image_thumb, thumb); } else { views.setImageViewResource(R.id.image_thumb, R.mipmap.default_cover); } //通过appWidgetId,为指定的小工具界面更新 appWidgetManager.updateAppWidget(appWidgetId, views);}......
}
“`
/*******************************************************************/
* 版权声明
* 本教程只在CSDN和安豆网发布,其他网站出现本教程均属侵权。
*另外,我们还推出了Arduino智能硬件相关的教程,您可以在我们的网店跟我学Arduino编程中购买相关硬件。同时也感谢大家对我们这些码农的支持。
*最后再次感谢各位读者对安豆
的支持,谢谢:)
/*******************************************************************/
- 手把手教你做音乐播放器(八)桌面小工具(上)
- 手把手教你做音乐播放器(八)桌面小工具(下)(完)
- 手把手教你做音乐播放器(七)播放音乐(上)
- 手把手教你做音乐播放器(五)音乐列表的存储(上)
- 手把手教你做音乐播放器(七)播放音乐(下)
- 手把手教你做音乐播放器(五)音乐列表的存储(下)
- 手把手教你做音乐播放器(三)获取音乐信息
- 手把手教你做音乐播放器(六)存储多首音乐
- 手把手教你做音乐播放器(四)播放服务的搭建
- 手把手教你做音乐播放器(一)功能规划
- 手把手教你做音乐播放器(二)技术原理与框架设计
- 手把手教你做视频播放器(六)-竖屏的播放界面
- iOS开发--手把手教你制作音乐播放器
- 手把手教你做视频播放器(一)-设计方案
- 手把手教你做视频播放器(二)-获取视频信息
- 手把手教你做视频播放器(三)-展示视频列表
- 手把手教你做视频播放器(四)-刷新与停止刷新列表
- 手把手教你做视频播放器(五)-视频列表的横屏
- 剑指offer系列--1
- sort() 排序不影响原数组,data.concat([])
- LRU缓存
- 第一篇 设计模式之简单工厂模式
- 打开excel文件
- 手把手教你做音乐播放器(八)桌面小工具(上)
- TOMCAT JVM调优
- POJ 3661 跑步+dp
- [leetcode] 2. Add Two Numbers
- ISO/OSI七层模型和TCP/IP模型的关系
- Unity自带示例Demo项目Standard Assets Example Project研究详解
- synchronized底层
- eclipse neon 启动报错java版本异常
- 自定义View