Android Widget工作原理詳解(一) 最全介紹

     

   轉載請標明出處:http://blog.csdn.net/sk719887916/article/details/46853033 ;

      Widget是安卓的一應用程序組件,學名窗口小部件,它是微型應用程序視圖, 可以嵌入到其他應用程序(如主屏幕)和接收數據定期更新。,可以使其他應用程序的插件被稱爲應用程序部件。用戶可以通過添加窗口小部件來添加自己喜歡的APPwidget ,widget主要用於展現程序快捷入口,下面的屏幕快照展示了音樂應用程序的Widget。


                                    


 本文描述瞭如何使用應用程序部件發佈應用程序提供者。創建您自己的喜歡的AppWidgetHost主機應用程序的小部件。


一 創建AppWidget組件


   1 AppWidgetProviderInfo 


      描述了應用程序的元數據部件,如應用程序部件的佈局,更新頻率,AppWidgetProvider類。這需要在XML中定義。


 2 AppWidgetProvider 


     定義允許的基本方法與應用程序編程接口部件,基於廣播事件。通過它我們將會收到廣播 ,用來更新應用程序的widget,用來進行啓用,關閉,刪除的操作。繼承父類是一個BroadcastReceiver,擁有廣播的一切特性,我們可以這麼理解:AppWidgetProvider 是帶有界面的廣播。


3 視圖佈局 View layout

在創建時間。

怎麼創建widget。

 A :在清單中申明widget部件

  首先,聲明應用程序的AndroidManifest AppWidgetProvider類。xml文件。例如:

<receiver android:name="ExampleAppWidgetProvider" >
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>
    <meta-data android:name="android.appwidget.provider"
               android:resource="@xml/example_appwidget_info" />
</receiver>

<receiver>需要Android:name屬性,它指定了AppWidgetProvider的具體類。

< intent-filter >元素必須包含一個<action>元素與android:name屬性。這個屬性指定AppWidgetProvider接受系統的ACTION_APPWIDGET_UPDATE廣播。這是唯一的廣播,申明,您必須顯式地聲明。代表此類就是一個widget。AppWidgetManager 自動發送所有其他應用程序部件廣播此註冊的廣播才能收到,也就是說我們必須要指定識別爲widget的action,當然你需要這個AppWidgetProvider接收接她action,ni

 <Mata_data>元素 指定AppWidgetProviderInfo 資源和需要以下屬性:

 android:name——指定Mata_data名稱。使用android.appwidgetb必須確定AppWidgetProviderInfo描述符的數據。

 android:resource——指定AppWidgetProviderInfo資源XML。


二 添加 AppWidgetProviderInfo


      AppWidgetProviderInfo定義了應用程序的小部件的基本屬性,如最小尺寸佈局,其最初的佈局資源,多久更新應用程序的小部件,和(可選)配置活動啓動創建時間。定義AppWidgetProviderInfo在XML資源使用一個< appwidget-provider >標籤,並將其保存在項目的res / XML /文件夾下。
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="40dp"
    android:minHeight="40dp"
    android:updatePeriodMillis="86400000"
    android:previewImage="@drawable/preview"
    android:initialLayout="@layout/example_appwidget"
    android:configure="com.example.android.ExampleAppWidgetConfigure" 
    android:resizeMode="horizontal|vertical"
    android:widgetCategory="home_screen|keyguard"
    android:initialKeyguardLayout="@layout/example_keyguard">
</appwidget-provider>

   < appwidget-provider >屬性:介紹
       minWidth和minHeight屬性指定了widget所佔據的寬和高。主屏幕位置默認的應用程序的widget基於網格,其每個item有一個定義的高度和寬度。如果應用程序部件的最小寬度或高度值不匹配的屏幕的單元寬高,,然後widget的尺寸就會自動縮放到最適合的網格大小。

     注意:如果想讓你的widget跨設備、移植widget的最小大小不應超過4 x 4單元。


    minResizeWidth和minResizeHeight屬性指定widget的絕對最小大小。Android 3.1中引入此屬性,用來指定應用程序的小部件的大小低於多少將字跡模糊的或無法使用。此屬性允許用戶自定義調整大小,但此widget可能小於默認小部件 由minWidth和minHeight屬性定義的大小。。


    updatePeriodMillis:用來  設置widget更新時間,也就是會所從AppWidgetProvider通過調用onUpdate()回調方法更新數據的時間週期。實際的更新不能保證準時就能更新,爲了節省電量和保護電池,規格建議一般一小時更新一次。開發者還可以讓用戶調整頻率configuration-some。
   注意:如果設備時熄屏狀態下 ,需要更新(如由updatePeriodMillis定義)的時候 ,設備就會自動喚醒,用來執行更新。如果每小時更新不超過一次,這可能不會導致電池壽命的重大問題。然而,如果您需要更新很頻繁頻繁 ,或者設備在滅屏下不需要了更新,我們可以給更新操作時設置一個警報提醒,,此時在滅屏狀態下也不會執行更新操作,。爲此,設置一個報警意圖 讓AppWidgetProvider接收, 使用alarmmanager。設置報警action 爲ELAPSED_REALTIME或RTC,    這樣到了一定的更新時間,此事件就會被停止。然後設置updatePeriodMillis爲零(“0”)。


initialLayout 屬性:   定義了Widget的XML佈局。
   配置屬性定義了活動推出當用戶添加應用程序部件,爲了他(她)來配置應用程序窗口小部件的屬性。這是無需必須添加的。


previewImage  在Android 3.0中引入的。指定了用戶第一次看見預覽界面的widget是什麼樣子。如果不設置,默認爲APP的啓動圖標。這個字段對應於android:previewImage屬性在AndroidManifest <recevicer>元素。


autoAdvanceViewId 屬性指定應用程序的視圖ID應該auto-advanced部件子視圖部件的主機。這是在Android 3.0中引入的。


resizeMode  在Android 3.1中引入的。指定規則的一個小部件可以調整大小。使用這個屬性使widget 的resizeable-horizontally,垂直,或在兩個軸。用戶touch-hold小部件,以顯示其調整處理,然後拖動水平和/或垂直佈局網格處理改變大小。值resizeMode屬性包括“水平”、“垂直”,“沒有”。設置一個部件resizeable水平方向和垂直方向,
minResizeHeight 屬性指定的最低高度(dps)小部件可以調整大小。大於minHeight 或者如果沒有啓用垂直調整(見resizeMode)。在Android 4.0中引入的。
minResizeWidth屬性指定的最小寬度(dps)小部件可以調整大小。 大於minWidth或 如果沒有啓用水平調整(見resizeMode)。在Android 4.0中引入的。
widgetCategory屬性 決定widget是否可以顯示在主屏幕上, 鎖屏也包括其中。這個屬性的值包括“home_screen”和“power”鍵。顯示一個小部件都需要確保它遵循窗口小部件類的設計指導方針。有關更多信息,請參見啓用應用程序部件千篇一律。默認值是“home_screen”。在Android 4.2中引入的。
initialKeyguardLayout   在Android 4.2中引入, 用來定義鎖屏應用小部件的佈局。同樣android:initialLayout,可指定一個佈局資源 ,可以立即出現,直到應用程序部件初始化,能夠更新佈局。


     你必須爲應用程序在項目的res /佈局/目錄下定義個xml文件 ,因爲widget的佈局需要的RemoteViews的支持。不能隨便定義自定義view,支持的控件有:

支持的佈局:

支持的控件:


三   AppWidgetProvider 

     此類是widget的控制核心,主要控制添加,刪除,更新等。

    onUpdate()   widget更新時觸發

   onDeleted(Context, int[] )  widget被刪除是觸發

  
    onEnabled(Context),widget可用時觸發

   

    onDisabled(Context)  widget不可用時觸發

   onReceive(Context, Intent)  收到指定的廣播時觸發


 指定某個widget創建以及更新可以重寫onUpdate() ,通過遍歷註冊的appwidget的ID,創建一個RemoteViews來加載佈局,最後調用updateAppWidget

來加載界面。  

public class ExampleAppWidgetProvider extends AppWidgetProvider {

    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        final int N = appWidgetIds.length;

        // Perform this loop procedure for each App Widget that belongs to this provider
        for (int i=0; i<N; i++) {
            int appWidgetId = appWidgetIds[i];

            // Create an Intent to launch ExampleActivity
            Intent intent = new Intent(context, ExampleActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

            // Get the layout for the App Widget and attach an on-click listener
            // to the button
            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout);
            views.setOnClickPendingIntent(R.id.button, pendingIntent);

            // Tell the AppWidgetManager to perform an update on the current app widget
            appWidgetManager.updateAppWidget(appWidgetId, views);
        }
    }
}

  用於接收指定意圖,處理相關需求,可以重寫onRecrive(),列如我們收到一個toast的動作時,顯示一條Toast

@Override
    public void onReceive(Context context, Intent intent) {
        AppWidgetManager mgr = AppWidgetManager.getInstance(context);
        if (intent.getAction().equals(TOAST_ACTION)) {
            int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                AppWidgetManager.INVALID_APPWIDGET_ID);
            int viewIndex = intent.getIntExtra(EXTRA_ITEM, 0);
            Toast.makeText(context, "Touched view " + viewIndex, Toast.LENGTH_SHORT).show();
        }
        super.onReceive(context, intent);
    }


     AppWidgetProvider只是一個方便的類。如果你想接收應用程序部件直接廣播,您可以實現自己的BroadcastReceiver或複寫onReceive()的回調方法。意圖需要關心如下:

ACTION_APPWIDGET_UPDATE //處理更新

ACTION_APPWIDGET_DELETED // 處理刪除

ACTION_APPWIDGET_ENABLED //可用

ACTION_APPWIDGET_DISABLED//不可用

ACTION_APPWIDGET_OPTIONS_CHANGED // 配置改變


四 RemoteViewsService



1. RemoteViews


    顧名思義,它是一個遠程視圖。App Widget中的視圖,都是通過RemoteViews轉換表現的。 
    在RemoteViews的構造函數中,通過傳入layout文件的id來獲取 “layout文件對應的視圖(RemoteViews)”;調用RemoteViews中的方法能對layout中的組件進行設置  widgetViews.setOnClickPendingIntent(R.id.widget_btn, calendarIntent);  來設ID對應的Button的點擊響應事件)。


ps: 如果使自己的自定義的view顯示在widget上,我們必須在這個類中加上我們自定義的全路徑,前提是我們有權限修改rom.

    可以將RemoteViews看做是widget資源視圖的所有集合工具管理者。


2 RemoteViewsService


RemoteViewsService子類提供了RemoteViewsFactory用於填充遠程集合視圖。

具體地說,需要執行以下步驟:

子類RemoteViewsService。

 RemoteViewsService是一個遠程的服務適配器可以請求RemoteViews,管理RemoteViews的服務。 

   在你RemoteViewsService子類,包括一個實現RemoteViewsFactory接口的類。RemoteViewsFactory之間是一個適配器的接口遠程集合視圖(如列表視圖,顯示數據表格,等等)和底層數據視圖。實現執行RemoteViews對象中每一項的數據集。這個接口是一個適配器。

    一般,當App Widget 中包含“GridView、ListView、StackView等”集合視圖時,才需要使用RemoteViewsService來進行更新、管理。(集合視圖是指GridView、ListView、StackView等包含子元素的視圖) 
    RemoteViewsService更新“集合視圖”的一般步驟是: 
(01) 通過setRemoteAdapter來設置 “RemoteViews對應RemoteViewsService”。 
(02) 之後在RemoteViewsService中,實現RemoteViewsFactory接口。然後,在RemoteViewsFactory接口中對“集合視圖”的各個子項進行設置(“集合視圖”的各個子項:例如,GridView的每一個格子都是一個子項;ListView中的每一列也是一個子項)。
public class StackWidgetService extends RemoteViewsService {
    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent) {
        return new StackRemoteViewsFactory(this.getApplicationContext(), intent);
    }
}




3  RemoteViewsFactory


     RemoteViewsFactory接口提供了應用程序的小部件的數據項的集合。要做到這一點,它結合了你的應用程序部件條目XML佈局文件的源數據。這個源的數據可以從一個數據庫到一個簡單的數組。在StackView小部件示例中,數據源是一個WidgetItems數組。RemoteViewsFactory作爲適配器的將數據到設置到RemoteViews上

    最重要的兩個方法你需要實現你的RemoteViewsFactory子類onCreate()和getViewAt()。

   系統調用onCreate()首次在創建Factory。在這裏可以設初始化一些數據。例如,StackView小部件示例使用onCreate()來初始化一個WidgetItem對象數組。

    通過RemoteViewsService中的介紹,我們可以瞭解“RemoteViewsService是通過RemoteViewsFactory來具體管理layout中集合視圖的”,即“RemoteViewsFactory管理集合視圖的實施者”。 
    RemoteViewsFactory是RemoteViewsService中的一個接口。RemoteViewsFactory提供了一系列的方法管理“集合視圖”中的每一項。例如: 
(01)RemoteViews getViewAt(int position) 
      通過getViewAt()來獲取“集合視圖”中的第position項的視圖,視圖是以RemoteViews的對象返回的。
public RemoteViews getViewAt(int position) {

    // Construct a remote views item based on the app widget item XML file, 
    // and set the text based on the position.
    RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_item);
    rv.setTextViewText(R.id.widget_item, mWidgetItems.get(position).text);

    ...
    // Return the remote views object.
    return rv;
}

 
(02)int getCount() 
      通過getCount()來獲取“集合視圖”中所有子項的總數。  

class StackRemoteViewsFactory implements
RemoteViewsService.RemoteViewsFactory {
    private static final int mCount = 10;
    private List<WidgetItem> mWidgetItems = new ArrayList<WidgetItem>();
    private Context mContext;
    private int mAppWidgetId;

    public StackRemoteViewsFactory(Context context, Intent intent) {
        mContext = context;
        mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                AppWidgetManager.INVALID_APPWIDGET_ID);
    }

    public void onCreate() {
        // In onCreate() you setup any connections / cursors to your data source. Heavy lifting,
        // for example downloading or creating content etc, should be deferred to onDataSetChanged()
        // or getViewAt(). Taking more than 20 seconds in this call will result in an ANR.
        for (int i = 0; i < mCount; i++) {
            mWidgetItems.add(new WidgetItem(i + "!"));
        }
        ...
    }
...

 五 widget工作原理

   當widget指定其具體的AppWidgetProvider,AppWidgetProvider通過創建RemoteViews來加載視圖,其RemoteViews將會調用setRemoteViewsAdapter來設置內部適配器,此適配器也將會繼續獲取widget管理器調用updateAppWidget()方法,此方法有會用遠程視圖工廠(RemoteViewsFactroy)來初始化數據並調用其onDataSetChanged()來通知適配器更新數據,具體更新那個widget的界面,是通過其GetViewAt將界面更新後並返回,其詳細流程圖如下:



        Widget使用集合的特徵是能夠爲用戶圖提供更好的更新數據的視圖內容的方法。例如,考慮Android 3.0 Gmail應用部件,它爲用戶提供了一個快照的收件箱。爲做到這一點,你需要能夠觸發RemoteViewsFactory和RemoteViews獲取並顯示新的數據。使用AppWidgetManager的notifyAppWidgetViewDataChanged()即可通知更新數據,這個調用的結果會繼續回調到RemoteViewsFactory的 onDataSetChanged()方法,這裏你可以去拿初始化和拿任何的數據。請注意,調用onDataSetChanged()方法可以更新數據。在調用它之前將必須保證先完成從RemoteViewsFactory獲取元數據或視圖的數據。此外,調用在getViewAt()方法。如果這個調用需要很長時間去加載視圖(規定RemoteViewsFactory getLoadingView()方法)將會顯示在相應的位置集合視圖,直到它返回真正結果爲止。

  本文出處:http://blog.csdn.net/sk719887916/article/details/46853033 歡迎大家閱讀。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章