1. 程式人生 > >Android 自定義桌面空間 AppWidget

Android 自定義桌面空間 AppWidget

本篇文章主要是記錄自己學習Android自定義桌面懸浮控制元件,AppWidget的過程。

現在很多App都支援懸浮桌面的控制元件,我們可以通過系統選單長按,或者別的方式來設定,例如自帶的備忘錄,就可以寫完後掛在手機桌面上。
現在我們看看執行效果:

在安裝應用後,設定桌面懸浮的時候會看到我們剛才安裝的應用:

這裡寫圖片描述

然後我們點選這個控制元件,讓他顯示在桌面上:

這裡寫圖片描述

可以看到他已經現在我們桌面上了,而且長按可以拖動擺放他的位置。

現在來記錄一下學習過程,
一.設定簡單的自定義桌面控制元件:

1.在Layout下新建widget_setting.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>

這裡面設定了minHeight minWidth 和previreImage還有他的update函式時間間隔。

2.設計自己的AppWidget介面:

layout_widget.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
<LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/iamge_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/ic_launcher"/> <Button android:id="@+id/btn" android:textAllCaps="false" android:text="sure" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> <TextView android:id="@+id/text_view" android:textSize="20sp" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>

這裡我們的介面設計就是圖二顯示在桌面上的佈局設計。

3.建立TestWidget類繼承自AppWidgetProvider並且建立服務並且按時更新介面
就和廣播一樣,我們需要實現其方法onReceive;
在AppWidgetManager中有很多系統廣播,例如ACTION_APPWIDGET_UPDATE,
ACTION_APPWIDGET_DELETED
等,會在桌面控制元件更新,建立,和刪除時候發出廣播給廣播接收器,我們可以在裡面做一些操作。

TestWidget.java

public class TestWedigt extends AppWidgetProvider {
    private static final String WIDGET_BTN_ACTION = "WIDGET_BTN_ACTION";
    private static final int UPDATE_DURATION = 2 * 1000;
    private PendingIntent pendingIntent = null;
   static  int time =0;
    private WedigetService service  ;
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
        String action = intent.getAction();//獲取到廣播的Action
        if(intent!=null&& TextUtils.equals(intent.getAction(),WIDGET_BTN_ACTION))//廣播的Action為按鈕按下
        {
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.layout_widget);//注意:需要【重新】構造一個RemoteViewstime++;
            remoteViews.setTextViewText(R.id.text_view,"Hello Andoird"+time);
            remoteViews.setTextColor(R.id.text_view, Color.GRAY);
            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);// 單例模式
            ComponentName componentName = new ComponentName(context,TestWedigt.class);
            appWidgetManager.updateAppWidget(componentName, remoteViews);//setText之後,記得更新一下,傳入自定義介面
        }
        if(AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)){//廣播為更新廣播
            Bundle bundle = intent.getExtras();
            if(bundle!=null){
                int[] appWidgetIds = bundle.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
                if( appWidgetIds!=null&&appWidgetIds.length>0){
                    this.onUpdate(context,AppWidgetManager.getInstance(context),appWidgetIds);
                }
            }
        }else if(AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)){//廣播為刪除控制元件廣播
            Bundle bundle =  intent.getExtras();
            if(bundle!=null&&bundle.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)){
                final int appWidgetIds = bundle.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
                this.onDeleted(context,new int[]{appWidgetIds});
            }
        }
    }
/*
* 重寫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);
        Intent intent = new Intent();
        intent.setClass(context, TestWedigt.class);
        intent.setAction(WIDGET_BTN_ACTION);//設定廣播Action為BuutonAction
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
        remoteViews.setOnClickPendingIntent(R.id.btn, pendingIntent);//設定按鈕的點選監聽
        appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
        context.startService(new Intent(context, WedigetService.class));//開啟服務
    }

}

因為AppWidget的更新時間系統有個預設值,好像是二三十分鐘,所以我們為了實現到時間自動更新,我們就要自己設定。但是在widget_setting我們設定的那個時間間隔,是不起作用的,所以我們需要通過服務來達到計時,到時間,然後更新介面的操作,這樣一直迴圈。
我們這個類裡面,還設定了介面中Button的監聽事件,通過傳送廣播並且設定Action然後在onReceive中接受廣播,判斷Action的方法來設定監聽。

 RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.layout_widget);
        Intent intent = new Intent();
        intent.setClass(context, TestWedigt.class);
        intent.setAction(WIDGET_BTN_ACTION);//設定廣播Action為BuutonAction
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
        remoteViews.setOnClickPendingIntent(R.id.btn, pendingIntent);//設定按鈕的點選監聽
 public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
        String action = intent.getAction();//獲取到廣播的Action
        if(intent!=null&& TextUtils.equals(intent.getAction(),WIDGET_BTN_ACTION))//廣播的Action為按鈕按下
        {
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.layout_widget);//注意:需要【重新】構造一個RemoteViewstime++;
            remoteViews.setTextViewText(R.id.text_view,"Hello Andoird"+time);
            remoteViews.setTextColor(R.id.text_view, Color.GRAY);
            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);// 單例模式
            ComponentName componentName = new ComponentName(context,TestWedigt.class);
            appWidgetManager.updateAppWidget(componentName, remoteViews);//setText之後,記得更新一下,傳入自定義介面
        }

下面看看服務類

WeidgetService.java

public class WedigetService extends Service {

    static int time=0;
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
/*
*    每次重複啟動服務後,後會呼叫這個方法。
* */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        time++;
        buildUpdate();
        AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        Intent i = new Intent(this,WedigetService.class);
        long cuutime = SystemClock.elapsedRealtime()+1000;//時間間隔
        PendingIntent pi = PendingIntent.getService(this,0,i,0);
        alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,cuutime,pi);//設定Alarm然後到時間再次啟動服務,達成迴圈目的。
        return super.onStartCommand(intent, flags, startId);
    }
    /*
    * 更新控制元件的資料
    * */
    private void buildUpdate() {
        RemoteViews view = new RemoteViews(getPackageName(), R.layout.layout_widget);
        RemoteViews remoteViews = new RemoteViews(getApplicationContext().getPackageName(), R.layout.layout_widget);
        remoteViews.setTextViewText(R.id.text_view,""+time);
        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getBaseContext());
        appWidgetManager.updateAppWidget(new ComponentName(getBaseContext(), TestWedigt.class), remoteViews);
    }
}

這裡面沒什麼東西,主要就是在多次啟動服務的時候會重複呼叫onStartCommand方法,
所以我們在onStartCommand裡,設定了Alarm,然後設定時間間隔,每次到達時間後就進行更新介面操作和服務自啟動操作,這樣就可以讓服務一直執行並且自己到達時間後更新介面的目的。

4.添加註冊。
我們需要註冊服務和我們剛才寫的TestWidget.

    <receiver android:name=".TestWedigt">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@layout/widget_setting"
                ></meta-data>
        </receiver>
        <service android:name=".WedigetService">
        </service>
<meta-data
                android:name="android.appwidget.provider"
                android:resource="@layout/widget_setting"
                ></meta-data>

要注意這裡的寫法哦。