Android的Launcher啟動(dòng)器中添加快捷方式及小部件實(shí)例
前言: 最近一直在看Launcher模塊,經(jīng)過差不多兩個(gè)月學(xué)習(xí),終于摸透了Launcher的一些主要功能實(shí)現(xiàn),目前繼續(xù)還處于
摸索狀態(tài)。未看Launcher時(shí),于我而言,只能膜拜,以為所有功能都是它實(shí)現(xiàn)的 ;入門后,才發(fā)現(xiàn),Launcher的很多功能只是
集成了框架/應(yīng)用程序提供的功能。很多陌生的東西,只有接觸了才感嘆:“oh ,原來是這樣的!”
添加快捷方式
今天先給大家分享下Launcher如何實(shí)現(xiàn)添加快捷方式(Shortcut) ,后續(xù)會(huì)慢慢增加其他方面的功能,幫助大家“一葉而知秋”。
具體來說,Launcher中的快捷方式有兩種類型:
1 、"偽"快捷方式 —— 應(yīng)用程序類型
2 、"真"快捷方式 —— Activity具備<action/>為ACTION_CREATE_SHORTCUT的配置信息
這兩種類型的快捷方式是怎么勾搭在一起的,在下面大家通過代碼自己理解,也不方便細(xì)說了。
知識(shí)點(diǎn)介紹:
知識(shí)點(diǎn)一 、ACTION_PICK_ACTIVITY使用說明 ,具體可以參考SDK Intent類
功能:顯示匹配附加值為EXTRA_INTENT的所有Activity,并將它們以列表呈現(xiàn)給用戶。當(dāng)用戶從該列表選中一項(xiàng)
時(shí),并不會(huì)啟動(dòng)該Activity(這與與ACTION_CHOOSER不同,此Action會(huì)啟動(dòng)用戶選擇的Activity),而是將該Activity的詳細(xì)信
息(可能包括Action、ComponentName、data信息等)以Intent對(duì)象返回給調(diào)用者(通常為onActivityResult方法)。
附加值:EXTRA_INTENT 顯示所有匹配顯示所有匹配附加值為EXTRA_INTENT的Activity,
EXTRA_TITLE 作為顯示列表即所有Activity的標(biāo)題 。
因此,根據(jù)ACTION_PICK_ACTIVITY的特性,真正地創(chuàng)建快捷方式需要兩步走:
第一步:發(fā)送ACTION_PICK_ACTIVITY以及EXTRA_INTENT,找到我們希望能創(chuàng)建快捷方式的Activity列表。
第二步:根據(jù)第一步所選擇的Activity返回的Intent對(duì)象,再次發(fā)送此Intent對(duì)象,即可創(chuàng)建該Activity提供給
我們快捷方式了。
例如,下面我們只是簡(jiǎn)單的發(fā)送一個(gè)請(qǐng)求顯示所有應(yīng)用程序的Intent,如下:
//重新發(fā)送一個(gè)Action為Pick_Activity的Intent,獲取所有應(yīng)用程序信息 Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY); Intent mainIntent = new Intent () ; mainIntent.setAction(Intent.ACTION_MAIN); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent); pickIntent.putExtra(Intent.EXTRA_TITLE, "選擇應(yīng)用程序"); //設(shè)置界面title //繼續(xù)選擇所有應(yīng)用程序 startActivityForResult(pickIntent,MY_REQUEST_ALL_APPLICATION );
ACTION_PICK_ACTIVITY效果圖如下:
點(diǎn)擊某一具體Activity , 即可選擇創(chuàng)建該Activity的快捷方式了。
知識(shí)點(diǎn)二、 Intent.ShortcutIconResource類介紹
功能: 為快捷方式(Shortcut)和文件夾(live folder)提供圖片資源
常用方法為:
public static Intent.ShortcutIconResource fromContext(Context context, int resourceId)
功能: 創(chuàng)建一個(gè) Intent.ShortcutIconResource 對(duì)象
參數(shù)說明:context Context類對(duì)象
resourceId 具體的圖片資源id 。
常用屬性:
packageName 該應(yīng)用程序所在包名,類型為 packageName:type/entityname
resourceName resourceId所對(duì)應(yīng)地的資源名
例如: 某個(gè)圖片資源 R.id.icon = 0x7f020000, 則resourceName 為 packageName:drawable/icon
具體怎么通過 Intent.ShortcutIconResource對(duì)象獲取圖片資源,請(qǐng)參考示例Demo。
示例Demo
說明:點(diǎn)擊創(chuàng)建快捷方式對(duì)話框后, 選擇某一項(xiàng)具體的快捷方式,即可添加至MainActivity界面中 ,繼續(xù)點(diǎn)擊每個(gè)View,則
可啟動(dòng)該快快捷方式的App,挺給力的吧。
PS: 由于我只是簡(jiǎn)單的利用了LinearLayout去當(dāng)容器,會(huì)存在局限性,大家可在此基礎(chǔ)上,利用GridView/ListView構(gòu)建更好
的布局,當(dāng)然更NB的是,去提供類似Launcher的自定義布局。
由于執(zhí)行快捷方式可能需要一些特定的權(quán)限,因此我們必須得在AndroidManifest.xml里配置對(duì)應(yīng)的權(quán)限。例如,直接撥打電話
需要的權(quán)限為: <uses-permission android:name="android.permission.CALL_PHONE"></uses-permission>
如上效果圖,增加幾個(gè)快捷方式后截圖如下,點(diǎn)擊即可啟動(dòng)該應(yīng)用。
主工程邏輯如下:
package com.qin.addshortcut; import java.util.ArrayList; import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.content.Intent.ShortcutIconResource; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Parcelable; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; /** * * @author http://http://blog.csdn.net/qinjuning */ public class MainActivity extends Activity implements View.OnClickListener { private LinearLayout linearlayout ; //用來存放所有快捷方式的容器 --- 父視圖 private Button btAddShortCut; private static final int MY_REQUEST_SHORT_CUT = 1; // 第一步 、 顯示所有能創(chuàng)建快捷方式的Activity private static final int MY_CREATE_SHOURT_CUT = 2; //第二步、 創(chuàng)建 真快捷方式 private static final int MY_REQUEST_ALL_APPLICATION = 3 ; //第二步 、創(chuàng)建 偽快捷方式 -- 應(yīng)用程序類 private static String TAG = "AddShortActivity" ; private PackageManager mPackageManager = null ; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); btAddShortCut = (Button) findViewById(R.id.bt_addShortcut); linearlayout = (LinearLayout)findViewById(R.id.linearLayout) ; linearlayout.setOnClickListener(this) ; //演示如何通過一個(gè)資源的type獲取該資源文件在R文件中的整數(shù)描述符 // getIdentifier 參數(shù)為 package:type/entry . 例如求icon的資源描述符如下: int iconId = this.getResources().getIdentifier("com.qin.addshortcut:drawable/icon", null, null); Log.i(TAG, " icon id : " + iconId); //獲取PackageManager對(duì)象 mPackageManager = getPackageManager(); btAddShortCut.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //創(chuàng)建快捷方式對(duì)話框 createShortDialog() ; } }); } private void createShortDialog(){ //begin :添加創(chuàng)建應(yīng)用程序的圖標(biāo)('快捷方式") // 此bundle值為附加的創(chuàng)建PICK_ACTIVITY的一個(gè)列表項(xiàng) Bundle bundle = new Bundle() ; //設(shè)置name ArrayList<String> shortcutNames = new ArrayList<String>(); shortcutNames.add("應(yīng)用程序"); bundle.putStringArrayList(Intent.EXTRA_SHORTCUT_NAME, shortcutNames); //設(shè)置對(duì)應(yīng)的頭像 ArrayList<ShortcutIconResource> shortcutIconRes= new ArrayList<ShortcutIconResource>(); shortcutIconRes.add(ShortcutIconResource.fromContext(MainActivity.this, R.drawable.icon)); bundle.putParcelableArrayList(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, shortcutIconRes); // end Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY); Intent extraIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT); pickIntent.putExtra(Intent.EXTRA_INTENT, extraIntent);//所要查找的Activity Intent pickIntent.putExtra(Intent.EXTRA_TITLE, "選擇快捷方式"); // Title //看到?jīng)] , 偽快捷方式 是在這兒創(chuàng)建地。 pickIntent.putExtras(bundle); startActivityForResult(pickIntent, MY_REQUEST_SHORT_CUT); } protected void onActivityResult(int requestCode, int resultCode, final Intent data) { super.onActivityResult(requestCode, resultCode, data); //未成功的選擇任何一個(gè)shortcut(比如直接按back鍵) , 直接返回 if(resultCode == RESULT_CANCELED ) return ; switch(requestCode){ case MY_REQUEST_SHORT_CUT: //第一次發(fā)送 PICK_Activity后 ,對(duì)所選列表項(xiàng)進(jìn)行選擇后,做的中間處理操作,需要判斷是選擇“應(yīng)用程序” 抑或真正滴快捷方式 // 獲得快捷方式Label String label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); //這個(gè)字段是我們之前主動(dòng)添加的 if(label.equals("應(yīng)用程序")){ //重新發(fā)送一個(gè)Action為Pick_Activity的Intent,獲取所有應(yīng)用程序信息 Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY); Intent mainIntent = new Intent () ; mainIntent.setAction(Intent.ACTION_MAIN); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent); pickIntent.putExtra(Intent.EXTRA_TITLE, "選擇應(yīng)用程序"); //設(shè)置界面title //繼續(xù)選擇所有應(yīng)用程序 startActivityForResult(pickIntent,MY_REQUEST_ALL_APPLICATION ); } else{ //重新請(qǐng)求創(chuàng)建上一次點(diǎn)擊的Activity , 啟動(dòng)后即可創(chuàng)建 Log.v(TAG, "MY_REQUEST_SHORT_CUT Intent Info--- >" + data); //下一步,創(chuàng)建某一具體的shortcut startActivityForResult(data, MY_CREATE_SHOURT_CUT); } break ; //以下操作才是真正地處理添加快捷方式的操作 case MY_CREATE_SHOURT_CUT: //data數(shù)據(jù)封裝了所有快捷方式應(yīng)該該附加的值 Log.v(TAG, "MY_CREATE_SHOURT_CUT Intent Info--- >" + data); completeAddShortCut(data); break ; case MY_REQUEST_ALL_APPLICATION: //創(chuàng)建一個(gè)應(yīng)用程序的"快捷方式" //data數(shù)據(jù)封裝了點(diǎn)擊某個(gè)應(yīng)用程序的intent,包括action,ComponentName組信息等,繼而我們可以通過intent對(duì)象獲取該應(yīng)用程序的信息 Log.v(TAG, "MY_REQUEST_ALL_APPLICATION Intent Info--- >" + data); completeAddApplication(data) ; break ; default : break ; } } //添加一個(gè)應(yīng)用程序的"快捷方式" private void completeAddApplication(Intent data){ //打印應(yīng)用程序返回的intent信息, 一個(gè)完整的啟動(dòng)應(yīng)用程序的Intent Log.i(TAG, "Application intent info ---->" +data) ; ComponentName componentName = data.getComponent() ; Log.i(TAG, "ComponentName Info ----> " + componentName) ; try { //獲取資源圖片 ActivityInfo activityInfo = mPackageManager.getActivityInfo(componentName, 0); CharSequence applabel = activityInfo.loadLabel(mPackageManager) ; Drawable appIcon = activityInfo.loadIcon(mPackageManager) ; //創(chuàng)建一個(gè)View對(duì)象 View view = makeViewForShortcut(applabel , appIcon) ; //為該快捷方式的View設(shè)置onClick監(jiān)聽 view.setOnClickListener(this) ; //將該intent對(duì)象設(shè)置為View的tag屬性 , onClick時(shí)獲取該tag , --->getTag() view.setTag(data) ; //將該View對(duì)象添加至LinearLayout中,由于大小發(fā)生了變化,系統(tǒng)會(huì)重新走”measure“ , ”layout“, ”draw“ 過程 //設(shè)置長(zhǎng)寬高 LinearLayout.LayoutParams llparams = new LinearLayout.LayoutParams(80,90) ; linearlayout.addView(view,llparams) ; } catch (NameNotFoundException e) { Log.e(TAG, "NameNotFoundException at completeAddApplication method") ; } } //添加快捷方式(真正地,非應(yīng)用程序) private void completeAddShortCut(Intent data){ Drawable shortcutIcon = null; //快捷方式的圖標(biāo) , 可以有兩種方式獲取,如下 if else 判斷 // 獲得快捷方式Label String label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON); // 直接了圖片 , 即設(shè)置了 EXTRA_SHORTCUT_ICON 參數(shù)值 if (bitmap != null && bitmap instanceof Bitmap) { shortcutIcon = new BitmapDrawable((Bitmap) bitmap); } else //設(shè)置了EXTRA_SHORTCUT_ICON_RESOURCE 附加值 { Parcelable iconParcel = data .getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE); if(iconParcel != null && iconParcel instanceof ShortcutIconResource) { // 獲得ShortcutIconResource對(duì)象 ShortcutIconResource iconRes = (ShortcutIconResource) iconParcel; //獲得inconRes對(duì)象的Resource對(duì)象 try { //獲取對(duì)應(yīng)packageName的Resources對(duì)象 Resources resources = mPackageManager.getResourcesForApplication(iconRes.packageName); //獲取對(duì)應(yīng)圖片的id號(hào) int iconid = resources.getIdentifier(iconRes.resourceName, null, null); Log.i(TAG, "icon identifier is " + iconRes.resourceName) ; //獲取資源圖片 shortcutIcon = resources.getDrawable(iconid); } catch (NameNotFoundException e) { Log.e(TAG, "NameNotFoundException at completeAddShortCut method") ; } } } //可能快捷方式?jīng)]有為我們?cè)O(shè)置任何圖像以及ShortcutIconResource對(duì)象,我們需要重置快捷方式的頭像 if ( shortcutIcon == null) { // 一定會(huì)有圖片,這兒我簡(jiǎn)單的處理了 . Toast.makeText(MainActivity.this, "sorry , we could not shortcut image", Toast.LENGTH_SHORT) ; return ; } // 獲得快捷方式Intent , 直接startActivity 即可 Intent shortcut_intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT); if (shortcut_intent != null) Log.i(TAG, "shortCut intent info ----> "+shortcut_intent) ; //創(chuàng)建一個(gè)View對(duì)象 View view = makeViewForShortcut(label , shortcutIcon) ; //為該快捷方式的View設(shè)置onClick監(jiān)聽 view.setOnClickListener(this) ; //將該intent對(duì)象設(shè)置為View的tag屬性 , onClick時(shí)獲取該tag , --->getTag() view.setTag(shortcut_intent) ; //將該View對(duì)象添加至LinearLayout中,由于大小發(fā)生了變化,系統(tǒng)會(huì)重新走”measure“ , ”layout“, ”draw“ 過程 //設(shè)置長(zhǎng)寬高 LinearLayout.LayoutParams llparams = new LinearLayout.LayoutParams(100,90) ; linearlayout.addView(view,llparams) ; } //點(diǎn)擊事件 @Override public void onClick(View v) { Object tag = v.getTag() ; if(tag !=null && tag instanceof Intent){ Intent intent = (Intent)tag ; startActivityForSafely(intent) ; } } //安全啟動(dòng)一個(gè)Activity private void startActivityForSafely(Intent data) { Intent launchIntent = data ; //有些啟動(dòng)后的Activity需要設(shè)置該選項(xiàng),即為啟動(dòng)的Activity設(shè)置一個(gè)界面,例如聯(lián)系人的快捷方式, so , 我們加上它吧 launchIntent.setSourceBounds(new Rect(0,0,300,300)); launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(launchIntent) ; } //為每個(gè)快捷方式創(chuàng)建一個(gè)View對(duì)象 private View makeViewForShortcut(CharSequence label , Drawable icon){ LayoutInflater inflater = LayoutInflater.from(this) ; View shortcut_view = inflater.inflate(R.layout.shortcut_view, null) ; TextView tv = (TextView)shortcut_view.findViewById(R.id.shortcut_label) ; tv.setText(label) ; ImageView img = (ImageView)shortcut_view.findViewById(R.id.shortcut_img) ; img.setImageDrawable(icon) ; return shortcut_view ; } }
添加窗口小部件
我們知道:
1、每個(gè)AppWidget都有一個(gè)AppWidgetProviderInfo對(duì)象,該對(duì)象描述了每個(gè)AppWidget的基本數(shù)據(jù)(meta-data)信息 ,
其定義在<appwidget-provider>節(jié)點(diǎn)信息。
2、每個(gè)AppWidget都對(duì)應(yīng)一個(gè)RemoteViews視圖對(duì)象,該RemoteViews提供了特定AppWidget的展示(View視圖)和操作
(例如,點(diǎn)擊該RemoteViews會(huì)跨進(jìn)程處理一些事情)。
3、AppWidgetManager類維護(hù)了應(yīng)用程序中所有的AppWidget,并且為給每個(gè)AppWidget特定的Id去標(biāo)識(shí)他們(一般我們
用 appWidgetId去標(biāo)識(shí))。通過給定的appWidgetId,AppWidgetManager可以管理對(duì)應(yīng)的AppWidget,例如:更新該
AppWidgetId的RemoteViews視圖,刪除該AppWidget對(duì)象等 。
4、AppWidgetProvider廣播類從來說是一個(gè)監(jiān)聽器,系統(tǒng)把對(duì)AppWidget的操作(例如,創(chuàng)建和更新等)分發(fā)給
AppWidgetProvider類去處理。
對(duì)每個(gè)AppWidget,我們可以創(chuàng)建多個(gè)其多個(gè)實(shí)例,當(dāng)然這些實(shí)例對(duì)應(yīng)于不同的appWidgetId。 假設(shè)存在這么個(gè)
MyAppWidgetProvider廣播類,以及對(duì)應(yīng)的MyAppWidgetProviderInfo對(duì)象。 那么,則存在如下關(guān)系:
MyAppWidgetProvider.class : 代表了由該MyAppWidgetProvider創(chuàng)建的窗口小部件(AppWidget)的類型,一般用
CompontentName對(duì)象形式表示 。 那么存在如下關(guān)系:
從上圖可是,每個(gè)appWidget都對(duì)應(yīng)于一個(gè)MyAppWidgetProvider類,于是當(dāng)任何一個(gè)appWidgetId發(fā)生變化時(shí),我們需要
同步其他實(shí)例,保持同步性。
AppWidgetProviderInfo類補(bǔ)充說明:
publicComponentNameconfigure: 一般為一個(gè)Activity,表明該Activity復(fù)雜需要管理AppWidget的創(chuàng)建操作。
public int updatePeriodMillis:用來更新AppWidget,但該屬性在SDK1.5已廢除
AppWidgetProvider類介紹:
常用方法:
onDeleted() : 當(dāng)該類型的AppWidget每次被刪除時(shí),調(diào)用此方法
onDisabled() : 當(dāng)該類型的窗口小部件(AppWidget)全被刪除時(shí),調(diào)用此方法
onEnabled() : 當(dāng)?shù)谝淮蝿?chuàng)建該類型的AppWidget時(shí),調(diào)用此方法
onReceive() : 廣播接受者方法 , 用來接受廣播消息
onUpdate() : 每次創(chuàng)建該類型的AppWidget都會(huì)調(diào)用此方法 , 通常來說我們需要在該方法里為該AppWidget指定
RemoteViews對(duì)象。
AppWidgetManager類介紹:
常用常量:
ACTION_APPWIDGET_PICK= "android.appwidget.action.APPWIDGET_PICK“
說明:列出所有能夠創(chuàng)建AppWidget的對(duì)象,該對(duì)象一般為自定義的AppWidgetProvider廣播接受者。
注意:發(fā)送該Intent必須添加附加值:EXTRA_APPWIDGET_ID 。
該EXTRA_APPWIDGET_ID含義:該appWidgetId與我們發(fā)送Action為ACTION_APPWIDGET_PICK 后
所選擇的AppWidget綁定。因此,我們可以通過這個(gè)appWidgetId獲取該AppWidget的信息了。
ACTION_APPWIDGET_CONFIGURE= "android.appwidget.action.APPWIDGET_CONFIGURE”
說明: 如果選擇的AppWidget配置了android:configure 屬性,需要再次啟動(dòng)性對(duì)應(yīng)的Activity,繼而進(jìn)一步去選擇
AppWidget。同時(shí)發(fā)送該Intent必須添加附加值:EXTRA_APPWIDGET_ID,含義同上。
常用方法:
public int[] getAppWidgetIds(ComponentName provider)
功能:獲取對(duì)應(yīng)ComponentName類型的所有appWidgetId
參數(shù)說明: provider 通常為 XXXAppProvider.class類型
publicAppWidgetProviderInfo getAppWidgetInfo(int appWidgetId)
功能: 獲取特定appWidgetId對(duì)應(yīng)的AppWidgetProviderInfo對(duì)象
public staticAppWidgetManager getInstance(Contextcontext)
功能: 獲取 AppWidgetManager對(duì)象
public void updateAppWidget(int appWidgetId, RemoteViews views)
功能: 以特定的views視圖更新appWidgetId的窗口小部件(AppWidget) 。同時(shí)會(huì)發(fā)送ACTION_APPWIDGET_UPDATE廣播
public void updateAppWidget(int[] appWidgetIds,RemoteViews views)
功能:以特定的views視圖更新所有appWidgetIds的窗口小部件(AppWidget),同時(shí)發(fā)送ACTION_APPWIDGET_UPDATE
廣播
public void updateAppWidget(ComponentName provider, RemoteViews views)
功能: 已特定的views更新組件類型為provider的所有窗口小部件(AppWidget),同時(shí)發(fā)送ACTION_APPWIDGET_UPDATE
廣播。
示例Demo :
說明:創(chuàng)建一個(gè)簡(jiǎn)單的AppWidget實(shí)例,點(diǎn)擊按鈕后可以更該圖片資源顯示 ,具體代碼在
截圖為:
關(guān)于如何創(chuàng)建一個(gè)AppWidget的教材,我也不再多說了,大家可以參考上面我提到的兩篇重量級(jí)博客去學(xué)習(xí):
1、SDK對(duì)AppWidget的介紹
2、Android 桌面組件【widget】初探
PS: 具體代碼可在后面下載 。
再次強(qiáng)調(diào)一點(diǎn),每個(gè)AppWidget都對(duì)應(yīng)與AppWidgetProvider , 我們需要同步更新這些AppWidget對(duì)象。
在自己的應(yīng)用程序中添加窗口小部件
本部分的主要功能是像Launchcer那樣添加AppWidget 。 知識(shí)點(diǎn)介紹如下:
AppWidgetHost 類
功能:對(duì)每個(gè)應(yīng)用程序App,該類提供了和AppWidgetService(該AppWidgetService用來管理所有AppWidget,類似于
NotificationManagerService系統(tǒng)服務(wù)管理所有Notifciation,不懂?其實(shí)我也不懂,知其大意即可)交互,用來更新、管理
AppWidget。打個(gè)比喻:AppWidgetHost是宿主對(duì)象,每個(gè)AppWidget都是寄生蟲,可以附加在(顯示)AppWidgetHost上。
每個(gè)能添加、顯示AppWidget的Activity都是一個(gè)AppWidgetHost對(duì)象,比如Launcher.java(Activity對(duì)象),以及我們后面自定
義的MainActivity.java(Activity對(duì)象)。
常用方法為:
public AppWidgetHost(Context context, int hostId)
功能:構(gòu)造一個(gè)AppWidgetHost對(duì)象
參數(shù): hostId 大意是該AppWidgetHost(宿主對(duì)象)對(duì)應(yīng)的Id號(hào),一般賦予一整數(shù)即可。
public int allocateAppWidgetId()
功能:申請(qǐng)一個(gè)新的appWidgetId ,該id會(huì)與新創(chuàng)建的AppWidget綁定。
public void startListening()
功能:監(jiān)聽所有AppWidget的變化 ,該方法必須在Activity的onCreate()/onStart()調(diào)用,否則 AppWidget是不會(huì)得到更新的
public void stopListening()
功能: 對(duì)應(yīng)于startListening(),即停止對(duì)AppWidget的更新監(jiān)聽??梢栽贏ctivity的onStop()方法里調(diào)用 ,
一般無需調(diào)用此方法去停止監(jiān)聽。
public final AppWidgetHostView createView(Context context, int appWidgetId, AppWidgetProviderInfo appWidget)
功能: 根據(jù)指定的appWidgetId以及AppWidgetProviderInfo對(duì)象去構(gòu)建一個(gè)AppWidgetHostView對(duì)象(具體該對(duì)象,
參見下 文)。
AppWidgetHostView 與 RemoteViews的區(qū)別
對(duì)每個(gè)AppWidget內(nèi)部而言,都有一個(gè)RemoteViews對(duì)象,用于視圖顯示;而對(duì)于外部而已,則以AppWidgetHostView形式代言 這個(gè)RemoteViews視圖。換句話來說就是,AppWidgetHost對(duì)象而言,它并不知道RemoteViews存在,而只是RemoteViews 的代言人AppWidgetHostView。
好了 ,該說明的都說明了,下面最后給大家補(bǔ)充一下如何利用在自己的應(yīng)用程序里添加窗口小部件(AppWidget) 。
也是兩步走:
第一步: 發(fā)送Action為ACTION_APPWIDGET_PICK的 Intent ,則所有能創(chuàng)建窗口小部件的AppWidgetProvider的廣播
接收者都會(huì)顯示 ,同時(shí)為該新創(chuàng)建的AppWidget分配一個(gè)appWidgetId ,該appWidgetId即可唯一標(biāo)記我們選擇的
AppWidget。
第二步:如果選擇的AppWidget對(duì)應(yīng)地AppWidgetProviderInfo對(duì)象配置了android:configure屬性,則需要在此啟動(dòng)該配置
屬性(一般為一個(gè)Activity類) ,然后在完成添加AppWidget的操作 ; 否則,沒有配置android:configure屬性,就可以添加
AppWidget的操作。
示例Demo截圖:
主工程流程如下:
package com.qin.addappwidget; import android.app.Activity; import android.appwidget.AppWidgetHost; import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends Activity { private static String TAG = "AddAppWidget" ; private Button btAddShortCut; private LinearLayout linearLayout ; // 裝載Appwidget的父視圖 private static final int MY_REQUEST_APPWIDGET = 1; private static final int MY_CREATE_APPWIDGET = 2; private static final int HOST_ID = 1024 ; private AppWidgetHost mAppWidgetHost = null ; AppWidgetManager appWidgetManager = null; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); btAddShortCut = (Button) findViewById(R.id.bt_addShortcut); linearLayout = (LinearLayout)findViewById(R.id.linearLayout) ; //其參數(shù)hostid大意是指定該AppWidgetHost 即本Activity的標(biāo)記Id, 直接設(shè)置為一個(gè)整數(shù)值吧 。 mAppWidgetHost = new AppWidgetHost(MainActivity.this, HOST_ID) ; //為了保證AppWidget的及時(shí)更新 , 必須在Activity的onCreate/onStar方法調(diào)用該方法 // 當(dāng)然可以在onStop方法中,調(diào)用mAppWidgetHost.stopListenering() 停止AppWidget更新 mAppWidgetHost.startListening() ; //獲得AppWidgetManager對(duì)象 appWidgetManager = AppWidgetManager.getInstance(MainActivity.this) ; btAddShortCut.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //顯示所有能創(chuàng)建AppWidget的列表 發(fā)送此 ACTION_APPWIDGET_PICK 的Action Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK) ; //向系統(tǒng)申請(qǐng)一個(gè)新的appWidgetId ,該appWidgetId與我們發(fā)送Action為ACTION_APPWIDGET_PICK // 后所選擇的AppWidget綁定 。 因此,我們可以通過這個(gè)appWidgetId獲取該AppWidget的信息了 //為當(dāng)前所在進(jìn)程申請(qǐng)一個(gè)新的appWidgetId int newAppWidgetId = mAppWidgetHost.allocateAppWidgetId() ; Log.i(TAG, "The new allocate appWidgetId is ----> " + newAppWidgetId) ; //作為Intent附加值 , 該appWidgetId將會(huì)與選定的AppWidget綁定 pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, newAppWidgetId) ; //選擇某項(xiàng)AppWidget后,立即返回,即回調(diào)onActivityResult()方法 startActivityForResult(pickIntent , MY_REQUEST_APPWIDGET) ; } }); } // 如果 protected void onActivityResult(int requestCode, int resultCode, Intent data) { //直接返回,沒有選擇任何一項(xiàng) ,例如按Back鍵 if(resultCode == RESULT_CANCELED) return ; switch(requestCode){ case MY_REQUEST_APPWIDGET : Log.i(TAG, "MY_REQUEST_APPWIDGET intent info is -----> "+data ) ; int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID , AppWidgetManager.INVALID_APPWIDGET_ID) ; Log.i(TAG, "MY_REQUEST_APPWIDGET : appWidgetId is ----> " + appWidgetId) ; //得到的為有效的id if(appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID){ //查詢指定appWidgetId的 AppWidgetProviderInfo對(duì)象 , 即在xml文件配置的<appwidget-provider />節(jié)點(diǎn)信息 AppWidgetProviderInfo appWidgetProviderInfo = appWidgetManager.getAppWidgetInfo(appWidgetId) ; //如果配置了configure屬性 , 即android:configure = "" ,需要再次啟動(dòng)該configure指定的類文件,通常為一個(gè)Activity if(appWidgetProviderInfo.configure != null){ Log.i(TAG, "The AppWidgetProviderInfo configure info -----> " + appWidgetProviderInfo.configure ) ; //配置此Action Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE) ; intent.setComponent(appWidgetProviderInfo.configure) ; intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); startActivityForResult(intent , MY_CREATE_APPWIDGET) ; } else //直接創(chuàng)建一個(gè)AppWidget onActivityResult(MY_CREATE_APPWIDGET , RESULT_OK , data) ; //參數(shù)不同,簡(jiǎn)單回調(diào)而已 } break ; case MY_CREATE_APPWIDGET: completeAddAppWidget(data) ; break ; } } //向當(dāng)前視圖添加一個(gè)用戶選擇的 private void completeAddAppWidget(Intent data){ Bundle extra = data.getExtras() ; int appWidgetId = extra.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID , -1) ; //等同于上面的獲取方式 //int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID , AppWidgetManager.INVALID_APPWIDGET_ID) ; Log.i(TAG, "completeAddAppWidget : appWidgetId is ----> " + appWidgetId) ; if(appWidgetId == -1){ Toast.makeText(MainActivity.this, "添加窗口小部件有誤", Toast.LENGTH_SHORT) ; return ; } AppWidgetProviderInfo appWidgetProviderInfo = appWidgetManager.getAppWidgetInfo(appWidgetId) ; AppWidgetHostView hostView = mAppWidgetHost.createView(MainActivity.this, appWidgetId, appWidgetProviderInfo); //linearLayout.addView(hostView) ; int widget_minWidht = appWidgetProviderInfo.minWidth ; int widget_minHeight = appWidgetProviderInfo.minHeight ; //設(shè)置長(zhǎng)寬 appWidgetProviderInfo 對(duì)象的 minWidth 和 minHeight 屬性 LinearLayout.LayoutParams linearLayoutParams = new LinearLayout.LayoutParams(widget_minWidht, widget_minHeight); //添加至LinearLayout父視圖中 linearLayout.addView(hostView,linearLayoutParams) ; } }
最后 ,關(guān)于AppWidget點(diǎn)擊后觸發(fā)的相應(yīng)事件,是通過設(shè)置該AppWidget的RemoteViews的某個(gè)控件點(diǎn)擊事件而觸發(fā)的。
- 適配android7.0獲取文件的Uri的方法
- Android7.0 工具類:DiffUtil詳解
- Android7.0 MessageQueue詳解
- Android7.0上某些PopuWindow出現(xiàn)顯示位置不正確問題的解決方法
- Android開發(fā)實(shí)現(xiàn)Launcher3應(yīng)用列表修改透明背景的方法
- Android開發(fā)中Launcher3常見默認(rèn)配置修改方法總結(jié)
- Android launcher中模擬按home鍵的實(shí)現(xiàn)
- Android6.0 Launcher2應(yīng)用解析
- Android實(shí)現(xiàn)向Launcher添加快捷方式的方法
- Android7.0開發(fā)實(shí)現(xiàn)Launcher3去掉應(yīng)用抽屜的方法詳解
相關(guān)文章
Android Studio格式化(Format)代碼快捷鍵介紹
這篇文章主要介紹了Android Studio格式化(Format)代碼快捷鍵,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01Android仿知乎懸浮功能按鈕FloatingActionButton效果
前段時(shí)間在看屬性動(dòng)畫,恰巧這個(gè)按鈕的效果可以用屬性動(dòng)畫實(shí)現(xiàn),下面通過本文給大家分享adroid仿知乎懸浮功能按鈕FloatingActionButton效果,需要的朋友參考下吧2017-04-04Android實(shí)現(xiàn)短信驗(yàn)證碼自動(dòng)攔截讀取功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)短信驗(yàn)證碼自動(dòng)攔截讀取功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-08-08Android shape和selector 結(jié)合使用實(shí)例代碼
本篇文章主要介紹了Android shape和selector 的使用,這里提供了shape 和selector 的詳細(xì)介紹,并附有代碼實(shí)例,有興趣的朋友可以參考下2016-07-07Android nativePollOnce函數(shù)解析
這篇文章主要介紹了Android nativePollOnce函數(shù)解析的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下2021-03-03flutter項(xiàng)目引入iconfont阿里巴巴圖標(biāo)
這篇文章主要為大家介紹了flutter項(xiàng)目引入iconfont阿里巴巴圖標(biāo)的過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05Android中控件GridView實(shí)現(xiàn)設(shè)置行列分割線的方法示例
這篇文章主要介紹了利用Android中控件GridView實(shí)現(xiàn)設(shè)置行列分割線的方法,文中給出了詳細(xì)的介紹與示例代碼,相信對(duì)大家具有一定的參考價(jià)值,有需要的朋友們下面來一起看看吧。2017-01-01Android編程實(shí)現(xiàn)拍照功能的2種方法分析
這篇文章主要介紹了Android編程實(shí)現(xiàn)拍照功能的2種方法,結(jié)合具體實(shí)例形式對(duì)比分析了Android通過調(diào)用系統(tǒng)攝像頭及程序調(diào)用照相機(jī)功能兩種實(shí)現(xiàn)技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-07-07Android學(xué)習(xí)筆記之應(yīng)用單元測(cè)試實(shí)例分析
這篇文章主要介紹了Android學(xué)習(xí)筆記之應(yīng)用單元測(cè)試,結(jié)合實(shí)例形式較為詳細(xì)的分析了Android單元測(cè)試的實(shí)現(xiàn)原理與具體步驟,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11android當(dāng)前apn的狀態(tài)以及獲取方法
在絕大多數(shù)android機(jī)器etc路徑下存放一個(gè)的apns-conf.xml文件,表示當(dāng)前機(jī)器使用的apn信息通過root機(jī)器可以push出來看看,具體路徑可以上網(wǎng)搜下,接下來介紹獲取apn的狀態(tài)的方法2013-01-01