安卓开发笔记(四)——AppWidget 使用

版权声明: https://blog.csdn.net/dickdick111/article/details/83378056

中山大学数据科学与计算机学院本科生实验报告

(2018年秋季学期)


一、实验题目

个人项目二: 中山大学智慧健康服务平台应用开发
实验代码:传送门:https://github.com/dick20/Android


二、实现内容

在第七周任务的基础上,实现静态广播、动态广播两种改变widget内容的方法。

要求

  • widget初始情况如下:
    preview
  • 点击widget可以启动应用,并在widget随机推荐一个食品。
    preview
  • 点击widget跳转到所推荐食品的详情界面。
    preview
  • 点击收藏图标,widget相应更新。
    preview
  • 点击widget跳转到收藏列表。
    preview
  • 实现方式要求:启动时的widget更新通过静态广播实现,点击收藏图标时的widget更新通过动态广播实现。

验收内容

  • 布局显示是否正常。
  • 静态广播:启动应用Widget是否有随机推荐食品。
  • 动态广播:点击收藏图标后,Widget是否提示食品已加入收藏列表。
  • 点击widget是否正确跳到对应的界面。

三、实验结果

(1)实验截图

下图为Widget初始情况:

1

下图为点击widget可以启动应用,并在widget随机推荐一个食品:

2

下图为点击widget跳转到所推荐食品的详情界面:

3

点击收藏图标,widget相应更新:

4

下图为点击widget跳转到收藏列表:
5

扫描二维码关注公众号,回复: 3802311 查看本文章

(2)实验步骤以及关键代码

1.创建Widget类,更改widget的布局文件

根据实验要求来修改new_app_widget.xml,添加一个ImageView与一个TextView,利用Realative布局控制好边距排版。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="@dimen/widget_margin">

    <ImageView
        android:id="@+id/appwidget_star"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:src="@mipmap/full_star"
        android:scaleType="fitXY"
        />

    <TextView
        android:id="@+id/appwidget_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="35dp"
        android:layout_alignBottom="@id/appwidget_star"
        android:contentDescription="当前没有任何信息"
        android:text="当前没有任何信息"
        android:textColor="#ffffff"
        android:textSize="15sp"
        android:textStyle="bold|italic" />

</RelativeLayout>

修改new_app_widget_info.xml文件,这是对于widget的布局文件,调整它的最小宽度与最小高度,设置图片为full_star,初始布局为之前设置的new_app_widget.

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialKeyguardLayout="@layout/new_app_widget"
    android:initialLayout="@layout/new_app_widget"
    android:minHeight="50dp"
    android:minWidth="300dp"
    android:previewImage="@mipmap/full_star"
    android:resizeMode="horizontal|vertical"
    android:updatePeriodMillis="86400000"
    android:widgetCategory="home_screen|keyguard"></appwidget-provider>

2.修改NewAppWidget.java 类

重写 onUpdate 方法,为 Widget 添加事件,使得点击能够启动应用。这里用到的是上周使用过的PeddingIntent,该intent不马上执行,而是等待事件后执行。

除此之外,对于widget的更新需要用到RemoteView,为其设置监听事件,当点击后会执行peddingIntent。而appWidgetManager调用updateAppWidget来通知更新widget。

@Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        RemoteViews updateView = new RemoteViews(context.getPackageName(), R.layout.new_app_widget);//实例化RemoteView,其对应相应的Widget布局
        Intent i = new Intent(context, FoodList.class);
        PendingIntent pi = PendingIntent.getActivity(context, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
        updateView.setOnClickPendingIntent(R.id.appwidget_star, pi); //设置点击事件
        ComponentName me = new ComponentName(context, NewAppWidget.class);
        appWidgetManager.updateAppWidget(me, updateView);
    }

3.发送静态广播

与上周作业一样需要先在 AndroidMainfest.xml 注册,而接收器不再写在一个新的接收器类,而是写在NewAppWidget的重构函数onRecieve中

接收器中要对于发送广播的action做判断,是否接受该广播。然后像上周通知一样更新RemoteViews即可,将数据传入到详情页面,包括食物的名字等等。只不过,这里是用appWidgetManager通知更新,而通知是用NotificationManager来更新

还要为remoteviews添加点击的监听事件,当点击widget时候跳转。

@Override
    public void onReceive(Context context, Intent intent ){
        super.onReceive(context, intent);
        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
        Log.i("receive","widget1");
        if(intent.getAction().equals("android.appwidget.action.APPWIDGET_UPDATE")){
            Bundle bundle = intent.getExtras();
            RemoteViews remoteViews=new RemoteViews(context.getPackageName(),R.layout.new_app_widget);
            if (bundle.getSerializable("collect") != null) {
                Log.i("receive",((MyCollection)bundle.getSerializable("collect")).getName());
                remoteViews.setTextViewText(R.id.appwidget_text, "今日推荐 " + ((MyCollection) bundle.getSerializable("collect")).getName());
            }
            //跳回主页面
            Intent intent2 = new Intent(context,Details.class);
            Bundle bundle2 = new Bundle();
            String s[] = new String [5];
            if (bundle.getSerializable("collect") != null) {
                s[0] = ((MyCollection) bundle.getSerializable("collect")).getName();
                s[1] = ((MyCollection) bundle.getSerializable("collect")).getMaterial();
                s[2] = ((MyCollection) bundle.getSerializable("collect")).getType();
                s[3] = ((MyCollection) bundle.getSerializable("collect")).getContent();
                s[4] = ((MyCollection) bundle.getSerializable("collect")).getIs_star() ? "yes" : "no";
            }
            bundle2.putStringArray("msg",s);
            intent2.putExtras(bundle2);

            PendingIntent pendingIntent=PendingIntent.getActivity(context,0,intent2,PendingIntent.FLAG_UPDATE_CURRENT);
            remoteViews.setOnClickPendingIntent(R.id.appwidget_text,pendingIntent);
            ComponentName me=new ComponentName(context,NewAppWidget.class);
            appWidgetManager.updateAppWidget(me, remoteViews);
        }
    }

而发送广播部分,与上次发送一样,只需要改变action的内容即可。
其中bundles的内容是随机生成的食物对象。

		Intent widgetBroadcast=new Intent("android.appwidget.action.APPWIDGET_UPDATE");//widget广播
        widgetBroadcast.putExtras(bundles);
        sendBroadcast(widgetBroadcast);

4.发送动态广播

动态广播的接受者与上次一样,写在DynamicReceiver类中,对不同的action做一下判断。

remoteViews.setTextViewText函数来修改对应id的widget文字内容。而跳回收藏列表,则也是用PeddingIntent来操作,注意加入点击事件的监听器。最后appWidgetManager更新widget。

else if(intent.getAction().equals("com.example.asus.health.WidgetDynamicFilter")){
            Bundle bundle = intent.getExtras();
            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
            RemoteViews remoteViews=new RemoteViews(context.getPackageName(),R.layout.new_app_widget);

            if (bundle.getSerializable("collect") != null) {
                remoteViews.setTextViewText(R.id.appwidget_text, "已收藏 " + ((MyCollection) bundle.getSerializable("collect")).getName());
            }
            //跳回收藏夹
            Intent intent2 = new Intent(context,FoodList.class);
            Bundle bundle2 = new Bundle();
            bundle2.putString("tag","collect");
            intent2.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
            intent2.putExtras(bundle2);

            PendingIntent pendingIntent=PendingIntent.getActivity(context,0,intent2,PendingIntent.FLAG_UPDATE_CURRENT);
            remoteViews.setOnClickPendingIntent(R.id.appwidget_text,pendingIntent);
            ComponentName me=new ComponentName(context,NewAppWidget.class);
            appWidgetManager.updateAppWidget(me, remoteViews);
        }

发送广播也是在点击收藏按钮后进行发送,记得注册广播,也要在onDestroy中删除。这里不再详述,与上周的发送注册删除一致。

(3)实验遇到的困难以及解决思路

1.关于收不到静态广播的问题

明明是一样的写法,为什么我新在widget类中的onRecieve函数中收不到呢?于是我在这个函数中设置了两个log消息,发现根本没有进入到这个函数。于是,我开始怀疑注册过程的错误。发现注册的action与发送的action不一样,所以收不到广播。

<receiver android:name=".NewAppWidget">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
                <action android:name="com.example.asus.health.WidgetDynamicFilter" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/new_app_widget_info" />
        </receiver>

这里要注意注册的action要与我发送广播的action一致,否则收不到广播。

Intent widgetBroadcast=new Intent("android.appwidget.action.APPWIDGET_UPDATE");//widget广播
        widgetBroadcast.putExtras(bundles);
        sendBroadcast(widgetBroadcast);

四、实验思考及感想

本次实验的内容是相对比较简单的,因为在上周已经解决了所有动态广播与静态广播的bug,只需注意widget的一些函数使用即可。接受器中的逻辑不需要改动,只是改变widget使用了PeddingIntent。

而关于widget的部分,AppWidgetProvider 继承自 BroadcastReceiver,它能接收 widget 相关的广播,例如 widget 的更新、删除、开启和禁用等。所以,我的NewAppWidget类继承AppWidgetProvider ,在里面可以重构多个函数来控制widget的一些操作,这次实验我只使用了onRecieve()以及onUpdate()。显然我们还可以对于它的删除开启来设置一些交互事件,完善widget的功能。例如,在创建widget的时候弹出使用的简要说明,在删除widget的时候弹出消息框询问用户是否确定删除,等等。

通过实验了解到了关于广播的第二个作用,可以用于生成app的widget。而动态更新widget则需要remoteviews这一部件的参与,使得app的内容更加完善。RemoteViews就是远程的View,也就是可以运行在其他进程中的View。RemoteViews常用在通知和桌面小组件中。我们可以利用它来更新不同活动中的view内容,不仅仅局限于widget。


猜你喜欢

转载自blog.csdn.net/dickdick111/article/details/83378056