由于工作需求,上周剛開始接觸widget。經過一周的開發和了解,對widget的一些基礎點做一下整理。
什么是widget:
widget也叫小部件,在launcher中點擊menu鍵,會出現小部件選項,進入后會顯示所以手機上已經安裝的widget。長按或者單擊(launcher決定)后,可添加到手機桌面上。
從功能上,widget功能比較簡單,專注于某一件事情。比如時鐘、天氣、音樂等等。
widget也可以和響應的普通應用聯系起來,點擊之后進入應用界面。這時候widget更像是一個應用的快捷入口。
下面來具體介紹一下widget的開發步驟:
在manifest中,注冊一個receiver,用來接收系統發來的widget的廣播。
<!-- receiver的 android:name指向的是widget的請求處理器或者說請求接收者 --> <receiver android:label="@string/app_name" android:name="com.cooee.phenix.widget.WidgetPRovider"> <intent-filter> <!-- 點擊事件對應的廣播字符串 --> <action android:name="com.cooee.phenix.widget.click.clock_layout"/> <action android:name="com.cooee.phenix.widget.click.weather_layout"/> <action android:name="com.cooee.phenix.widget.click.date_layout"/> <!-- widget默認的事件action --> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter> <!-- widget元數據,name是寫死的,resource指的是widget的配置文件 --> <meta-data android:name="android.appwidget.provider" android:resource="@layout/widgetinfo" /> </receiver>2、WidgetProvider處理器
WidgetProvider是我們自己創建的類,繼承AppWidgetProvider,AppWidgetProvider extends BroadcastReceiver,用來處理widget的各種請求。
widget中的事件都是通過廣播進行傳遞的:
widget的添加、刪除、更新等操作,都會收到相應的廣播。在AppWidgetProvider的public void onReceive(方法中可以看到
AppWidgetProvider類中:
public void onUpdate:widget更新時回調
public void onDeleted:一個widget從桌面刪除時回調
public void onEnabled:第一個widget被放到桌面時回調
public void onDisabled:最后一個widget從桌面刪除時回調
public void onReceive:用來接收廣播,處理widget的消息。
如果我們需要在這些時刻做一下自己的邏輯處理,WidgetProvider可以重寫這幾個方法。
3、widgetinfo和widget布局
widget的界面和widget寬高等信息在哪里定義呢?
在上面代碼的注釋中可以看到,widgetinfo是關聯在receiver里的mete-data中。(把這部分代碼單獨貼出來方便大家看)<!-- widget元數據,name是寫死的,resource指的是widget的配置文件 --> <meta-data android:name="android.appwidget.provider" android:resource="@layout/widgetinfo" />那么,widgetinfo中究竟定義了什么東西,我們來具體看一下這個xml文件:<?xml version="1.0" encoding="utf-8"?><!-- appwidget-provider Widget的配置文件 --><!-- android:minWidth 最小寬度 --><!-- android:minHeight 最小高度 --><!-- android:updatePeriodMillis 組件更新頻率(毫秒) --><!-- android:initialLayout 用來關聯widget的布局文件 --><!-- android:configure Widget設置用Activity ??? --><appwidget-provider android:layout_width="wrap_content" android:layout_height="wrap_content" xmlns:android="http://schemas.android.com/apk/res/android" android:initialLayout="@layout/widget_layout" android:minWidth="@dimen/widget_width" android:minHeight="@dimen/widget_height" android:updatePeriodMillis="86400000" />關于android:configure屬性,這次并沒有用到,具體我也不懂這是干嘛用的。在這里,我最關心的是布局文件,畢竟,它是要顯示的東西。由于布局文件比較長這里就不貼代碼了。(布局文件就像我們app里的一樣布局就ok啦)
4、widget點擊事件(action的意義)
說道點擊事件,我們得先來說一下widget中的view。
remoteview:
上面講到widget關聯布局的方式,和我們寫app有點不同。那么 我們想對顯示的布局做一些動態的改變(改變文字或者背景之類),該怎么辦呢?
按照寫app的思考方式,我們需要獲取到一個view,它里面填充的整個布局,然后最好有個findviewbyid獲取到布局里每一個控件,然后,嘎嘎嘎,我們就能為所欲為了。
設置點擊監聽、改變文字、設置圖片背景神馬的還不是灑灑水???!??!
想法,是很美好的。但是百度了一番之后我才發現并沒有想象中辣么簡單。。。。。。
下面來介紹widget如何獲取我們想要的view:
rv = new RemoteViews( mContext.getPackageName() , R.layout.widget_layout );得到view之后,悲劇的事情發生了,它居然沒有findviewbyid方法?。。。。∫簿褪钦f我得不到布局里每一個具體的view一番網上搜索之后,設置點擊事件監聽和改變文字之類的找到了對應的方法,不過,動畫好像就與我們絕緣了。。。
點擊事件:
//日期Intent intentDateClick = new Intent( WidgetProvider.CLICK_DATE_LAYOUT );//intent的action用來區分具體點擊的是哪一個viewPendingIntent pendingDateIntent = PendingIntent.getBroadcast( mContext , 0 , intentDateClick , 0 );rv.setOnClickPendingIntent( R.id.date_textview , pendingDateIntent );//設置點擊事件監聽//時鐘if( showClockVeiw ){ Intent intentClockClick = new Intent( WidgetProvider.CLICK_CLOCK_LAYOUT ); PendingIntent pendingClockIntent = PendingIntent.getBroadcast( mContext , 0 , intentClockClick , 0 ); rv.setOnClickPendingIntent( R.id.clock_layout , pendingClockIntent );}visibility = showClockVeiw ? View.VISIBLE : View.GONE;rv.setViewVisibility( R.id.clock_layout , visibility );//天氣if( showWeatherVeiw ){ Intent intentWeatherClick = new Intent( WidgetProvider.CLICK_WEATHER_LAYOUT ); PendingIntent pendingWeatherIntent = PendingIntent.getBroadcast( mContext , 0 , intentWeatherClick , 0 ); rv.setOnClickPendingIntent( R.id.city_textview , pendingWeatherIntent ); rv.setOnClickPendingIntent( R.id.weather_city , pendingWeatherIntent ); rv.setOnClickPendingIntent( R.id.temperature_current , pendingWeatherIntent ); rv.setOnClickPendingIntent( R.id.temperature_range , pendingWeatherIntent );}visibility = showWeatherVeiw ? View.VISIBLE : View.GONE;setWeatherVisibility( visibility );上面代碼可以看到。我們的關鍵點在于:rv.setOnClickPendingIntent( R.id.date_textview , pendingDateIntent );就是這個方法設置了點擊事件的監聽。第一個參數是rv這個view中,布局里面的某一個view的id,第二個參數是PendingIntent是具體的廣播意圖。里面包含了如果點擊這個view,WidgetProvider將受到的廣播。
widget中,點擊事件是通過廣播傳遞的。intent的action就是我們在manifest里看到的三個action。所以我們用三個action來區分三種view的點擊事件。
下面貼出來三個action的變量值,方便大家邏輯跟蹤。這三個值都是WidgetProvider中的:
public static final String CLICK_CLOCK_LAYOUT = "com.cooee.phenix.widget.click.clock_layout";public static final String CLICK_WEATHER_LAYOUT = "com.cooee.phenix.widget.click.weather_layout";public static final String CLICK_DATE_LAYOUT = "com.cooee.phenix.widget.click.date_layout";通過點擊事件的啟發,我發現雖然我們不能具體拿到布局里的每一個view,但是我們可以通過remoteview來改變它。
比如這句:rv.setViewVisibility( R.id.clock_layout , visibility );//設置view是否可見,我似乎找到了某種規律。至此,我們已經設置好了widget中view的點擊事件。
當我們點擊view時,WidgetProvider就會收到廣播,我們通過區分intent的action來處理不同view的點擊:
public void onClick( String action ){ //點擊時鐘布局 if( action.equals( WidgetProvider.CLICK_CLOCK_LAYOUT ) ) { ClockCalendarManager.getInstance( mContext ).onClickClock(); } //點擊日期 else if( action.equals( WidgetProvider.CLICK_DATE_LAYOUT ) ) { ClockCalendarManager.getInstance( mContext ).onclickDate(); } //點擊天氣 else if( action.equals( WidgetProvider.CLICK_WEATHER_LAYOUT ) ) { WeatherManager.getInstance( mContext ).onClick(); } else { Log.d( TAG , "其他" ); }}5、widget修改后更新顯示
以上我們講了widget的manifest注冊、widgetinfo、widget布局、如何獲取view、如何設置點擊事件、如何響應點擊事件。
下面我們來看看如何更新一個view顯示的內容、如何更新我們的widget。
以我們的時鐘為例,當我們需要更新view時:
TwinkleClockwidgetManager instance = TwinkleClockwidgetManager.getInstance( mContext );RemoteViews rv = instance.getRv();if( instance.showClockVeiw ){ rv.setImageViewResource( R.id.clock_minute_tens , timeNumbers[mCurrentMinute / 10] ); rv.setImageViewResource( R.id.clock_minute_ones , timeNumbers[mCurrentMinute % 10] );}哈哈,果然跟想的一樣,都是通過remoteview來設置,第一個參數是要改變的view的id,第二個是要設置的值。改變完view之后,我們需要更新一下widget,就能改變桌面上widget的顯示了:
//更新插件AppWidgetManager appWidgetManger = AppWidgetManager.getInstance( mContext );//第一步,通過context拿到appwidgetManagerint[] appIds = appWidgetManger.getAppWidgetIds( new ComponentName( mContext , WidgetProvider.class ) );//第二步,通過widgetManager和包類名拿到widgetIdappWidgetManger.updateAppWidget( appIds , rv );//第三步,更新widget的顯示總結:
以上就是這一周多時間對widget的了解,貼一張widget的圖展示小成果:嘿嘿嘿
其中還有些坑無法解惑:
1、widget如何做動畫?(好像在哪看到過別的桌面的widget,日歷時鐘,過一天,日歷更新時是帶一個旋轉的動畫的)
2、widget如何可拉伸?(launcher上顯示widget時,有的widget長按松手之后可以改變widget在桌面上顯示的大小,目前還不確定這部分是桌面做?還是需要widget這邊協助。。。)
以上僅代表個人觀點,如有理解不到位的地方,還請指正。歡迎留言,歡迎私信。
新聞熱點
疑難解答