Widget開發中遇到的坑

1.在Manifest文件中聲明

<receiver android:name=".receiver.GoldWidgetProvider">
     <intent-filter>
         <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
         <action android:name="com.fuqi.gold.action_updatewidget" />
         <action android:name="com.fuqi.gold.action_click_updatewidget" />
         <action android:name="com.fuqi.gold.action_to_buygold" />
     </intent-filter>

     <meta-data
          android:name="android.appwidget.provider"
          android:resource="@xml/goldwidget_provider_info" />
</receiver>

這個就至關於一個廣播接收者,而後android:name屬性指定在第四步的時候建立的文件名html

android:resource="@xml/example_appwidget_info"/>是widget的配置文件java

2.添加Widget配置信息

在項目的 res/xml/ 目錄下。若是沒有xml目錄,須要新建一個android

<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:resizeMode="horizontal|vertical"
    android:widgetCategory="home_screen">
</appwidget-provider>
  • initialLayout是widget的佈局文件app

  • previewImage就是widget的預覽效果ide

3.新建一個appwidget_layout文件

<FrameLayout
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:padding="@dimen/widget_margin">

  <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:background="@drawable/my_widget_background">
    …
  </LinearLayout>

</FrameLayout>

4.新建一個AppWidgetProvider類

這個類實際上是繼承至廣播接收者的函數

public class AppWidgetProvider extends BroadcastReceiver {

而後呢,咱們通常只須要用到onUpdate()方法,這個方法的更新時間,是你在example_appwidget_info配置文件中設置的android:updatePeriodMillis屬性決定的,這個地方有個坑,稍候再說佈局

public class GoldWidgetProvider extends AppWidgetProvider {

    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        final int N = appWidgetIds.length;
        for (int i=0; i<N; i++) {
            int appWidgetId = appWidgetIds[i];
            Intent intent = new Intent(context, ExampleActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
 
            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout);
            views.setOnClickPendingIntent(R.id.button, pendingIntent);
  widget
            appWidgetManager.updateAppWidget(appWidgetId, views);
        }
    }
}

5.開發中的一些坑

1. updateMillies更新時間低於30分鐘,系統默認爲30分鐘。若是想要本身更新widget,將這個值設置爲0,而後本身開timer或者經過廣播去更新。我是經過廣播去更新的
// 重寫OnReceiver方法
    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
        String action = intent.getAction();

        // 須要在Manifest中註冊這個Action
        if (ACTION_UPDATE_WIDGET.equals(action)) { // update widget
            RemoteViews views = getRemoteViews(context, null);
            ComponentName name = new ComponentName(context, GoldWidgetProvider.class);
            AppWidgetManager.getInstance(context).updateAppWidget(name, views);

        }else if (ACTION_CLICK_UPDATE_WIDGET.equals(action)){
            RemoteViews remoteViews = getRemoteViews(context, "正在刷新...");
            ComponentName name = new ComponentName(context, GoldWidgetProvider.class);
            AppWidgetManager.getInstance(context).updateAppWidget(name, remoteViews);

            Intent goldPrice = new Intent(context, GoldPriceService.class);
            context.startService(goldPrice);

        }  
    }
    
    public static RemoteViews getRemoteViews(Context context, String updating) {
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.goldwidget);
        //1,設置金價
        if (TextUtils.isEmpty(updating)) {
            String buyGoldPrice = ShaPreUtils.getBuyGoldPrice(context);
            views.setTextViewText(R.id.widget_tv_goldprice, buyGoldPrice);
        } else {
            views.setTextViewText(R.id.widget_tv_goldprice, updating);
        }

        //2,設置持有金
        if (GoldApplication.getInstance().isLogined()) {
            String total = GoldApplication.getInstance().getUserLoginInfo().getAccountInfo().getTotal();
            ShaPreUtils.putTotalGold(context,total);
            views.setTextViewText(R.id.widget_tv_total, total);
            views.setViewVisibility(R.id.widget_total_layout,View.VISIBLE);
        }else if (TextUtils.isEmpty(ShaPreUtils.getTotalGold(context))){
            views.setViewVisibility(R.id.widget_total_layout,View.GONE);
        }else{
            views.setViewVisibility(R.id.widget_total_layout,View.VISIBLE);
            views.setTextViewText(R.id.widget_tv_total,ShaPreUtils.getTotalGold(context));
        }


        //3,點擊事件
        Intent toStartActivity = new Intent(context, AppStart.class);
        views.setOnClickPendingIntent(R.id.widget_to_app, PendingIntent.getActivity(context, 0, toStartActivity, 0));

        // 買金的時候發送一個廣播,當前類接收
        Intent to_buygold = new Intent(context, GoldWidgetProvider.class);
        to_buygold.setAction(ACTION_TO_BUYGOLD);
        views.setOnClickPendingIntent(R.id.widget_to_buygold, PendingIntent.getBroadcast(context, 0,to_buygold , 0));

        // 主動刷新廣播
        Intent layout = new Intent(context, GoldWidgetProvider.class);
        layout.setAction(ACTION_CLICK_UPDATE_WIDGET);
        views.setOnClickPendingIntent(R.id.widget_layout, PendingIntent.getBroadcast(context, 0, layout, 0));

        return views;
    }
2.appwidget_info裏的android:configure屬性

一開始的時候照着官方文檔在appwidget_info xml文件裏設置了這個屬性,在添加到桌面上的時候老是不成功,將這個屬性去掉就能夠了,不知道這個屬性具體怎麼用,有知道的麻煩告訴我一聲,感激涕零...
查看官方文檔法相這個屬性是給widget設置一個config,具體怎麼使用能夠查看官方文檔ui

3.appWidgetManager更新的兩個方法
  • updateAppWidget(int[] appWidgetIds, RemoteViews views)code

  • updateAppWidget(ComponentName provider, RemoteViews views) {xml

updateAppWidget(int[] appWidgetIds, RemoteViews views)代碼以下:

// 更新 widget 的廣播對應的action
    private final String ACTION_UPDATE_ALL = "com.skywang.widget.UPDATE_ALL";
    // 保存 widget 的id的HashSet,每新建一個 widget 都會爲該 widget 分配一個 id。
    private static Set idsSet = new HashSet();

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        Log.d(TAG, "onUpdate():    appWidgetIds.length="+appWidgetIds.length);

        // 每次 widget 被建立時,對應的將widget的id添加到set中
        for (int appWidgetId : appWidgetIds) {
            idsSet.add(Integer.valueOf(appWidgetId));
        } 
    } 

    @Override  
    public void onDeleted(Context context, int[] appWidgetIds) {  
        Log.d(TAG, "onDeleted(): appWidgetIds.length="+appWidgetIds.length);

        // 當 widget 被刪除時,對應的刪除set中保存的widget的id
        for (int appWidgetId : appWidgetIds) {
            idsSet.remove(Integer.valueOf(appWidgetId));
        } 
        
        super.onDeleted(context, appWidgetIds);  
    }

    //接收廣播的回調函數
    @Override  
    public void onReceive(Context context, Intent intent) {  

        final String action = intent.getAction(); 
        if (ACTION_UPDATE_ALL.equals(action)) {
            // 「更新」廣播
            updateAllAppWidgets(context, AppWidgetManager.getInstance(context), idsSet);
        }   
    } 

    // 更新全部的 widget 
    private void updateAllAppWidgets(Context context, AppWidgetManager appWidgetManager, Set set) {
        Log.d(TAG, "updateAllAppWidgets(): size="+set.size());
       
        // widget 的id
        int appID;
        // 迭代器,用於遍歷全部保存的widget的id
        Iterator it = set.iterator();

        while (it.hasNext()) {
            appID = ((Integer)it.next()).intValue();     
            if (DEBUG) Log.d(TAG, "onUpdate(): index="+index);            
            // 獲取 example_appwidget.xml 對應的RemoteViews            
            RemoteViews remoteView = getRemoteViews(context,"");

            // 更新 widget
            appWidgetManager.updateAppWidget(appID, remoteView);        
        }        
    }

上面這些代碼有個隱患,在應用退出後,或是被第三方軟件殺死,點擊桌面widget就不會更新了。緣由是updateid我是用static的set集合保存的,應用退出後,這個set集合就被清空了,後面又換成了updateAppWidget(ComponentName)方法後就行了。

RemoteViews views = getRemoteViews(context, null);
ComponentName name = new ComponentName(context, GoldWidgetProvider.class);
AppWidgetManager.getInstance(context).updateAppWidget(name, views);
4.在用setOnClickPendingIntent()方法設置點擊事件發送Broadcast時,不能直接new Intent(acton)
Intent intent = new Intent(ACTION_CLICK_UPDATE_WIDGET);
views.setOnClickPendingIntent(R.id.widget_layout, PendingIntent.getBroadcast(context, 0, intent, 0));

上面這種寫法在應用退出後在有些手機上收不到廣播,而後換成下面這種寫法

Intent layout = new Intent(context, GoldWidgetProvider.class);
layout.setAction(ACTION_CLICK_UPDATE_WIDGET);
views.setOnClickPendingIntent(R.id.widget_layout, PendingIntent.getBroadcast(context, 0, layout, 0));
相關文章
相關標籤/搜索