手把手教你做音乐播放器(八)桌面小工具(上)

第8节 桌面小工具

桌面小工具是可以放置在主界面的、快速控制应用的小助手。例如我们的音乐小工具,它可以帮助用户在桌面上就完成音乐的暂停、播放、切换等操作,而不需要启动应用本身。

在安卓系统中,我们也常常叫它App widget

实现一个App widget要经过以下几个步骤,

  1. 创建一个App widget类,让它继承自AppWidgetProvider,例如AnddleMusicAppWidget类;
  2. 放在res\layout目录下,为App widget的界面定义一个布局,例如anddle_music_app_widget.xml
  3. res\xml目录下,定义一个App widget的说明文件,例如anddle_music_app_widget_info.xml
  4. AndroidManifest.xml文件当中,声明App widget

8.1 小工具框架的创建

对于使用Android Studio的用户来说,完成上面四个步骤的方法非常简单。因为Android Studio提供了简单的可视化向导来帮助我们。

  1. 在项目工程上点击右键,选择new->Wideget->App Widget就为我们创建好了开发用的模版,非常的简单方便。

  2. 在弹出的创建向导中,填上创建的参数,

    1. App widget取个名字-AnddleMusicAppWidget;
    2. 允许将App widget放到主界面上;
    3. 不允许App widget的显示区域动态调整;
    4. 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.xmlandroid: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为我们引入了更加灵活的设置方式。例如要设置Buttonbackground属性,

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-创建一个Intent
Intent preIntent = new Intent(MusicService.ACTION_PLAY_MUSIC_PRE);
//设置响应的组件名称
preIntent.setComponent(serviceName);
//将Intent转化成PendingIntent
PendingIntent prePendingIntent = PendingIntent.getService(context,
                0, preIntent, 0);
//设置id为R.id.pre_btn2的Button,设计点击后要触发的PendingIntent
views.setOnClickPendingIntent(R.id.pre_btn2, prePendingIntent);

RemoteViews不能直接设置按钮点击后的操作,必须通过设置PendingIntent来实现。所以要在响应的模块中添加响应的逻辑。

8.3.3 AnddleMusicAppWidget实现

  1. 继承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);
        }
        ......
    }
  2. 实现对界面元素的逻辑控制,

    “`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编程中购买相关硬件。同时也感谢大家对我们这些码农的支持。

*最后再次感谢各位读者对安豆的支持,谢谢:)

/*******************************************************************/

时间: 2024-10-06 15:50:25

手把手教你做音乐播放器(八)桌面小工具(上)的相关文章

手把手教你做音乐播放器(八)桌面小工具(下)(完)

8.4 MusicService的改造 8.4.1 App widget触发MusicService 当App widget的按钮被点击后,会触发隐式定义的Intent发送给MusicService.例如当下一首按钮被点击后,携带action-MusicService.ACTION_PLAY_MUSIC_NEXT的Intent将触发MusicService的onStartCommand()函数. 我们可以在onStartCommand()函数当中接收到App widget要求的操作命令,进行相应

手把手教你做音乐播放器(五)音乐列表的存储(上)

第5节 播放列表的存取 关于播放列表的存取需要三个组件的协同配合, MusicListActivity:让用户选择多首或一首音乐,将用户的选择项,传递给MusicService: MusicService:接收到添加列表的请求后,把数据交给PlayListContentProvider,进行存储: PlayListContentProvider:将播放列表存储到SQLite数据库中: 5.1 PlayListContentProvider的实现 自定义的ContentProvider与系统自带的

手把手教你做音乐播放器(六)存储多首音乐

第6节 存储多首音乐 我们已经能够存储单首音乐了,接下来我们开始添加一次存储多首音乐的界面设计. 当用户长按某首音乐项的时候,将进入ListView的多选modal状态,我们叫它模式框. 6.1 ListView的选择模式 在理解这个模式框之前,我们要先进一步了解一下ListView. ListView本身带有单选和多选功能,也就是说在单选模式下,它能够记住当前选中的唯一的列表项:在多选模式下,它能够记住目前选取的所有的列表项. 这种选择模式共有4种CHOICE_MODE_NONE CHOICE

自定义audio样式做音乐播放器

最近工作需求需要播放预览一些音乐资源,所以自己写了个控制audio的音乐播放器. 实现的原理主要是通过js调整audio的对象属性及对象方法来进行控制: 1.通过play().pause()来控制音乐的播放与暂停 2.通过duration.currentTime获取音乐长度及实时播放进度 3.通过volume.muted对音量大小进行调节 4.通过ended.error检测音乐的播放状态 首先来看一下实现效果:当播放点击音乐时该音乐展示播放进度.进度条.音乐条 本例子实际效果(博客园不允许添加a

Android开发本地及网络Mp3音乐播放器(八)状态存取与界面滑动

实现功能: 退出应用时,保存歌曲位置(也就是当前是第几首歌曲) 退出应用时,保存播放模式(也就是用户设置的是顺序播放/随机播放/单曲循环) 进入应用时,读取歌曲位置 进入应用时,读取播放模式 实现PlayActivity(独立音乐播放界面)的专辑图片layout滑动,滑动后展示歌词layout (目前源码,只实现了专辑图片layout与歌词layout,歌词layout的信息填充后续会进行补充) 截止到目前的源码下载: http://download.csdn.net/detail/iwangh

android 教你制作音乐播放器

呵呵,写之前我本人先笑了,欣赏一下我们清爽的界面吧~ 如果是只用activity来制作这样的东西简直是太小儿科了,此处我们当然用的是service 首先我们先上service的代码: 1.如果我们要访问service的属性和方法,那么在activity肯定是以bindservice的方法实现的,而在service中的onbind方法也是必须要实现的,onbind返回的Ibinder对象在activity的serviceconnection中得到使用. 2.activity获取到Ibinder对象

Xamarin.Android开发音乐播放器

最近.Net开源着实让C#火了一把,好久就听说Mono for Android一直没静下心来看,上周末找来看看,确实不错,前台界面axml编写跟Java安卓开发毫无区别,后台用C#其实很多windows下的方法都可以用,做一个安卓音乐播放器,主要实现音乐播放,上一曲下一曲,音乐播放列表,随机播放,扫描SD卡内所有音乐. 先show一组界面啊,话说有图有真相啊: 项目结构如图: Lrc是歌词处理方法 MusicService是安卓服务播放音乐,服务Broadcast,以及界面接收Receiver

android音乐播放器的竞品分析

迄今为止最长的一篇博客,各位看官笑纳~~ 本次分析基于android平台,选取了几款我体验过的播放器进行比较分析.主要分为两类,一类是大而全的,功能全面,可满足用户管理歌曲.导入导出歌单等多方面需求,不仅仅是听歌,另一类是小而简的,操作简单,让用户专注听歌. 1. QQ音乐          版本:5.2.1.15 特点:这款产品在PC端就有庞大的用户基础,有一定的用户粘性,手机端是web端的延伸.其性能和体验都很不错,1. 曲库强大,可选不同播放音效和音质:2. 独具Qplay功能,可将手机里

【源码分享】mui实现简单的手机音乐播放器

mui实现简单的手机音乐播放器 最近先来无事,我用mui写了一个可以跨页面控制的音乐播放器.主要功能有上一曲,下一曲,播放,暂停,感兴趣的可以继续看下去. 说的总是不实在,直接上源码,有兴趣的可以读下注释. 1首页代码 ①首页的html代码 1 <a> 2 <i id="bofang" class="iconfont icon-play-o"></i> <!--就是一个播放按钮没啥特殊的--> 3 </a>