安卓開發筆記(四)——AppWidget 使用
中山大學資料科學與計算機學院本科生實驗報告
(2018年秋季學期)
一、實驗題目
二、實現內容
在第七週任務的基礎上,實現靜態廣播、動態廣播兩種改變widget內容的方法。
要求
- widget初始情況如下:
- 點選widget可以啟動應用,並在widget隨機推薦一個食品。
- 點選widget跳轉到所推薦食品的詳情介面。
- 點選收藏圖示,widget相應更新。
- 點選widget跳轉到收藏列表。
- 實現方式要求:啟動時的widget更新通過靜態廣播實現,點選收藏圖示時的widget更新通過動態廣播實現。
驗收內容
- 佈局顯示是否正常。
- 靜態廣播:啟動應用Widget是否有隨機推薦食品。
- 動態廣播:點選收藏圖示後,Widget是否提示食品已加入收藏列表。
- 點選widget是否正確跳到對應的介面。
三、實驗結果
(1)實驗截圖
下圖為Widget初始情況:
下圖為點選widget可以啟動應用,並在widget隨機推薦一個食品:
下圖為點選widget跳轉到所推薦食品的詳情介面:
點選收藏圖示,widget相應更新:
下圖為點選widget跳轉到收藏列表:
(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的內容更加完善。