1. 程式人生 > >安卓開發筆記(四)——AppWidget 使用

安卓開發筆記(四)——AppWidget 使用

中山大學資料科學與計算機學院本科生實驗報告

(2018年秋季學期)

一、實驗題目

二、實現內容

在第七週任務的基礎上,實現靜態廣播、動態廣播兩種改變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

(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。

通過實驗瞭解到了關於廣播的第二個作用,可以用於生成app的widget。而動態更新widget則需要remoteviews這一部件的參與,使得app的內容更加完善。