Android UI組件AppWidget控件入門(mén)詳解
Widget引入
我們可以把Widget理解成放置在桌面上的小組件(掛件),有了Widget,我們可以很方便地直接在桌面上進(jìn)行各種操作,例如播放音樂(lè)。
當(dāng)我們長(zhǎng)按桌面時(shí),可以看到Widget選項(xiàng),如下圖所示:
點(diǎn)擊上圖中箭頭處的widgets圖標(biāo),會(huì)出現(xiàn)如下界面:(都是widget)
長(zhǎng)按上圖中的任意一個(gè)widget,就可以將其放到桌面上。
Widget的使用
Widget的實(shí)現(xiàn)思路
(1)在AndroidManifest中聲明AppWidget;
(2)在xml目錄中定義AppWidget的配置文件;
(3)在layout目錄中定義Widget的布局文件;
(4)新建一個(gè)類(lèi),繼承AppWidgetProvider類(lèi),實(shí)現(xiàn)具體的widget業(yè)務(wù)邏輯。
我們需要新建一個(gè)類(lèi),繼承AppWidgetProvider。點(diǎn)開(kāi)AppWidgetProvider,發(fā)現(xiàn)AppWidgetProvider竟然是繼承自BroadcastReceiver。
為什么Widget是一個(gè)廣播接收器呢?我們知道,BroadcastReceiver類(lèi)中有一個(gè)onReceive方法,用來(lái)接收廣播。當(dāng)我們?cè)谧烂鎾旒先プ霾僮鲿r(shí),必然引起應(yīng)用的改變,這就涉及到掛件和應(yīng)用之間的通信,此時(shí)用廣播來(lái)通信是再好不過(guò)了。
Widget的具體使用步驟
(1)新建一個(gè)類(lèi)TestWidget.java,繼承AppWidgetProvider:
TestWidget.java:
import android.appwidget.AppWidgetProvider; import android.content.Context; import android.content.Intent; /** * Created by smyhvae on 2016/9/7. */ public class TestWidget extends AppWidgetProvider{ @Override public void onReceive(Context context, Intent intent) { super.onReceive(context, intent); } }
(2)因?yàn)閃idget是一個(gè)廣播接收器,所以我們需要在清單文件中注冊(cè):
<!-- 聲明widget對(duì)應(yīng)的AppWidgetProvider --> <receiver android:name=".TestWidget"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@layout/widget_setting"/> </receiver>
04行:action是過(guò)濾條件,用來(lái)過(guò)濾行為,監(jiān)測(cè)widget的更新。
08行:android:resource指定了widget的配置。我們知道,屬性在清單文件中是用來(lái)存儲(chǔ)數(shù)據(jù)的。
(3)layout文件夾中新建文件widget_setting.xml:(widget的配置文件)
setting_widget.xml:
<?xml version="1.0" encoding="utf-8"?> <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:initialLayout="@layout/layout_widget" android:minHeight="140dp" android:minWidth="140dp" android:previewImage="@mipmap/ic_launcher" android:updatePeriodMillis="20000" android:widgetCategory="home_screen" > </appwidget-provider>
08行: android:initialLayout 指定了widget的布局。
09行:android:updatePeriodMillis 指定更新的時(shí)間周期
10行: android:widgetCategory="home_screen" 將widget顯示在主屏幕上(也可以顯示在鎖屏上)
(4)layout文件夾中新建文件layout_widget.xml:(widget的布局)
layout_widget.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="Large Text" android:textAppearance="?android:attr/textAppearanceLarge"/> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="New Button"/> </LinearLayout>
到此,程序就可以跑起來(lái)了。運(yùn)行程序之后,長(zhǎng)按桌面,點(diǎn)開(kāi)"Widget"按鈕,可以看到我們剛剛設(shè)計(jì)出的widget:
長(zhǎng)按上圖中的箭頭處,就可以將我們?cè)O(shè)計(jì)出的widget拖放到桌面上了:
Widget的點(diǎn)擊和更新【重要】
我們知道,TestWidget.java繼承自AppWidgetProvider,而AppWidgetProvider在繼承BroadcastReceiver之后,重寫(xiě)onReceive方法,然后還自定義了很多方法:
上圖中,包含了被刪除時(shí)、被禁用時(shí)、被啟用時(shí)、被更新時(shí)等各種方法。尤其重要的是onReceive()方法和onUpDate()方法。
當(dāng)小部件被改變時(shí)(比如被安裝到桌面),系統(tǒng)會(huì)發(fā)送一個(gè)更新的廣播(上圖紅框部分所示)。我們?cè)趕etting_widget.xml中設(shè)置了widget的更新頻率,這個(gè)也會(huì)調(diào)用更新。
有人可能會(huì)問(wèn),我開(kāi)機(jī)之后,天氣等widget為何不更新了?這是因?yàn)檫M(jìn)程被殺死了,那我們只能把這個(gè)控件先移除,然后再裝上,此時(shí)應(yīng)用會(huì)發(fā)update更新的廣播。
當(dāng)需要做widget的點(diǎn)擊和更新時(shí),我們需要在需要重寫(xiě)onUpdate()方法,用來(lái)發(fā)送廣播。當(dāng)程序初始化的時(shí)候,系統(tǒng)就會(huì)調(diào)用onUpdate()方法。
onUpdate()方法中的代碼如下:
@Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { super.onUpdate(context, appWidgetManager, appWidgetIds); RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.layout_widget);//需要構(gòu)造一個(gè)RemoteViews Intent intent = new Intent(); intent.setClass(context, TestWidget.class); //通過(guò)intent把廣播發(fā)給TestWidget本身,TestWidget接受到廣播之后,會(huì)調(diào)用。。進(jìn)而刷新借鑒 // 。 intent.setAction(WIDGET_BTN_ACTION); PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0); remoteViews.setOnClickPendingIntent(R.id.widget_btn, pendingIntent);//控件btn_widget的點(diǎn)擊事件:點(diǎn)擊按鈕時(shí),會(huì)發(fā)一個(gè)帶action的廣播。 appWidgetManager.updateAppWidget(appWidgetIds, remoteViews); //點(diǎn)擊完了之后,記得更新一下。 }
代碼解釋?zhuān)?br />
首先需要new一個(gè)RemoteViews,構(gòu)造方法里需要傳遞兩個(gè)參數(shù),一個(gè)是包名(context.getPacakgeName),一個(gè)是布局文件(layout_widget)。
然后通過(guò)remoteViews.setOnClickPendingIntent()設(shè)置按鈕的點(diǎn)擊事件。setOnClickPendingIntent()中需要傳遞兩個(gè)參數(shù):一個(gè)是id(比如需要被點(diǎn)擊的button),一個(gè)是PendingIntent。PendingIntent是未來(lái)的意圖。
于是我們需要事先構(gòu)造一個(gè)PendingIntent,這個(gè)需要通過(guò) PendingIntent.getBroadcast()來(lái)構(gòu)造。getBroadcast()方法中需要傳遞四個(gè)參數(shù),其中有一個(gè)是Intent。
于是我們需要構(gòu)造一個(gè)Intent。在intent里發(fā)送廣播,并設(shè)置Action。
按鈕點(diǎn)擊完了之后,記得調(diào)用appWidgetManager.updateAppWidget(int[] appWidgetIds, RemoteViews views)方法更新一下,第一個(gè)參數(shù)就是onUpdate方法中的參數(shù),代表的是所有的控件。
在onUpdate()方法中通過(guò)intent發(fā)送按鈕點(diǎn)擊時(shí)間的廣播之后,我們需要在onReceive()方法中進(jìn)行廣播的接收。
onReceive()方法中的代碼如下:
@Override public void onReceive(Context context, Intent intent) { super.onReceive(context, intent); if (intent != null && TextUtils.equals(intent.getAction(), WIDGET_BTN_ACTION)) { //當(dāng)intent不為空,且action匹配成功時(shí),就接收廣播,然后點(diǎn)擊事件成功 Log.i(WIDGET_BTN_ACTION, "is clicked"); //接下來(lái)開(kāi)始做點(diǎn)擊事件里面的內(nèi)容 RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.layout_widget);//注意:需要【重新】構(gòu)造一個(gè)RemoteViews remoteViews.setTextViewText(R.id.widget_tv, "be clicked"); remoteViews.setTextColor(R.id.widget_tv, Color.RED); AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);// 單例模式 ComponentName componentName = new ComponentName(context, TestWidget.class); appWidgetManager.updateAppWidget(componentName, remoteViews);//setText之后,記得更新一下 } }
代碼解釋?zhuān)?br />
當(dāng)intent的action匹配成功時(shí),開(kāi)始執(zhí)行做點(diǎn)擊時(shí)間之后的setText,不過(guò)這里需要重新new 一個(gè) RemoteViews,而不能共用onUpdate()方法中的RemoteViews(這是一個(gè)很大的坑)。
執(zhí)行完點(diǎn)擊事件之后的setText之后,記得調(diào)用appWidgetManager.updateAppWidget(ComponentName, RemoteViews)方法,第一個(gè)參數(shù)為組件名,需要我們自己new一下,第二個(gè)參數(shù)很好解釋。
綜合來(lái)說(shuō),TestWidget.java的完整版代碼如下:
Testwidget.java:
import android.app.PendingIntent; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.text.TextUtils; import android.util.Log; import android.widget.RemoteViews; /** * Created by smyhvae on 2016/9/7. */ public class TestWidget extends AppWidgetProvider { public static final String WIDGET_BTN_ACTION = "widget_btn_action"; @Override public void onReceive(Context context, Intent intent) { super.onReceive(context, intent); if (intent != null && TextUtils.equals(intent.getAction(), WIDGET_BTN_ACTION)) { //當(dāng)intent不為空,且action匹配成功時(shí),就接收廣播,然后點(diǎn)擊事件成功 Log.i(WIDGET_BTN_ACTION, "is clicked"); //接下來(lái)開(kāi)始做點(diǎn)擊事件里面的內(nèi)容 RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.layout_widget);//注意:需要【重新】構(gòu)造一個(gè)RemoteViews remoteViews.setTextViewText(R.id.widget_tv, "be clicked"); remoteViews.setTextColor(R.id.widget_tv, Color.RED); AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);// 單例模式 ComponentName componentName = new ComponentName(context, TestWidget.class); appWidgetManager.updateAppWidget(componentName, remoteViews);//setText之后,記得更新一下 } } @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { super.onUpdate(context, appWidgetManager, appWidgetIds); RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.layout_widget);//需要構(gòu)造一個(gè)RemoteViews Intent intent = new Intent(); intent.setClass(context, TestWidget.class); //通過(guò)intent把廣播發(fā)給TestWidget本身,TestWidget接受到廣播之后,會(huì)調(diào)用。。進(jìn)而刷新借鑒 // 。 intent.setAction(WIDGET_BTN_ACTION); PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0); remoteViews.setOnClickPendingIntent(R.id.widget_btn, pendingIntent);//控件btn_widget的點(diǎn)擊事件:點(diǎn)擊按鈕時(shí),會(huì)發(fā)一個(gè)帶action的廣播。 appWidgetManager.updateAppWidget(appWidgetIds, remoteViews); //點(diǎn)擊完了之后,記得更新一下。 } }
運(yùn)行之后,把widget拖到桌面上,效果如下:
點(diǎn)擊按鈕后,效果如下:
工程文件:(Android Studio 2.1)
當(dāng)然,widget還有很多其他的用途。比如:
•與Service進(jìn)行通信
•widget控件的交互方法。
•如何做一個(gè)桌面播放器Widget
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android編程實(shí)現(xiàn)懸浮窗獲取并顯示當(dāng)前內(nèi)存使用量的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)懸浮窗獲取并顯示當(dāng)前內(nèi)存使用量的方法,涉及Android針對(duì)窗口及內(nèi)存的相關(guān)操作技巧,需要的朋友可以參考下2017-07-07Android使用HorizontalScrollView實(shí)現(xiàn)水平滾動(dòng)
這篇文章主要為大家詳細(xì)介紹了Android使用HorizontalScrollView實(shí)現(xiàn)水平滾動(dòng),并點(diǎn)擊有相應(yīng)的反應(yīng)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-11-11Android開(kāi)發(fā)中Flutter組件實(shí)用技巧
這篇文章主要為大家介紹了Android開(kāi)發(fā)中Flutter組件實(shí)用技巧,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05利用Kotlin的方式如何處理網(wǎng)絡(luò)異常詳解
這篇文章主要 給大家介紹了關(guān)于利用Kotlin的方式如何處理網(wǎng)絡(luò)異常的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-07-07Android程序打開(kāi)和對(duì)輸入法的操作(打開(kāi)/關(guān)閉)
整理了一下Android下對(duì)輸入法的操作:打開(kāi)輸入法窗口、關(guān)閉出入法窗口、如果輸入法打開(kāi)則關(guān)閉,如果沒(méi)打開(kāi)則打開(kāi)、獲取輸入法打開(kāi)的狀態(tài)2013-05-05android仿即刻點(diǎn)贊文字部分的自定義View的示例代碼
本篇文章主要介紹了android仿即刻點(diǎn)贊文字部分的自定義View的示例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11Android調(diào)用系統(tǒng)的發(fā)郵件功能的小例子
這篇文章介紹了Android調(diào)用系統(tǒng)的發(fā)郵件功能的小例子,有需要的朋友可以參考一下2013-08-08Android開(kāi)發(fā)性能優(yōu)化總結(jié)
這篇文章主要介紹了Android開(kāi)發(fā)性能優(yōu)化總結(jié)的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09