編輯:關於Android編程
通過widget定義,我們在widget列表中看到了我們的TestWidget,當我們拖拽widget到主頁時,如果在appwidet-provider中定義了android:configure的java類,在widget實例創建後會馬上喚起配置activity。這個activity主要完成兩個任務:1、配置初始化數據;2、將配置數據適配到widget實例中。
widget數據可以保持在文件、Share preference,或者SQLite3中。widget作為小工具配置數據量小,通常可以方便地存貯在preference中。preference中數據存貯和讀取使用BirthDayStoreData類來處理。我們在Pro Android學習筆記(六三):Preferences(7):代碼控制首選項中的“利用preference保存狀態”已經介紹過如何實現,在此,復習一下。
我們需要存貯的內容有widgetID,名字,生日,Preference是以鍵值對的方式保存,我們以name_widgetID作為key,生日作為value來進行信息存貯。
public class BirthDayStoreData {
private final static String BIRTHDAY_WIDGET_PROVIDER_NAME = "cn.wei.flowingflying.testwidget.provider";
//保存配置數據:創建widget實例,通過configure activity進行配置時,保存相關配置數據
public static void storeData(Context context,int widgetId, String name,String value){
String key = getKey(widgetId,name);
//第一個參數是preferences文件,如果不存在則創建之。具體為/data/data/cn.wei.flowingflying.testwidget/shared_prefs/cn.wei.flowingflying.testwidget.provider.xml,可以在DDMS中查看。
Editor editor = context.getSharedPreferences(BIRTHDAY_WIDGET_PROVIDER_NAME, Context.MODE_PRIVATE).edit();
editor.putString(key, value);
editor.commit();
}
//刪除配置數據:刪除widget實例的同時,需要刪除該實例的相關數據
public static void removeData(Context context, int widgetId){
String key = getKeyById(context, widgetId);
if(key == null)
return;
Editor editor = context.getSharedPreferences(BIRTHDAY_WIDGET_PROVIDER_NAME, Context.MODE_PRIVATE).edit();
editor.remove(key);
editor.commit();
}
//清空全部的配置數據
public static void removeAllData(Context context){
Editor editor = context.getSharedPreferences(BIRTHDAY_WIDGET_PROVIDER_NAME, Context.MODE_PRIVATE).edit();
editor.clear();
editor.commit();
}
//顯示配置數據:用於我們在LogCat中進行跟蹤,在此,我們也演示了如何通過輪詢方式,顯示全部的數據,通過類似的方式,我們可以同widgetId查得對應的名字和生日,通過類似的方法,可根據widgetId查詢key,名字,生日,相關代碼從略。
public static void showData(Context context){
SharedPreferences prefs = context.getSharedPreferences(BIRTHDAY_WIDGET_PROVIDER_NAME, Context.MODE_PRIVATE);
Map
for(String key:pairs.keySet()){
String value = (String)pairs.get(key);
Log.d("DATA",key + " - " + value);
}
}
public static String getNameById(Context context, int widgetId){
… …
}
public static String getDateById(Context context ,int widgetId){
… …
}
private static String getKey(int widgetId, String name){
return name + "_" + widgetId;
}
private static String getKeyById(Context context,int widgetId){
… …
}
}
配置configure activity的代碼如下:
public class ConfigBirthDayWidgetActivity extends Activity{
private static String tag = "ConfigActivity";
private int myWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
@Override //配置activity的操作和普通activity的一樣,但在被AppWidgetManage喚起時,intent是攜帶widgetId的信息,我們在onCreate()中獲取Widget ID。
protected void onCreate(Bundle savedInstanceState) {
… …
Intent intent = getIntent();
Bundle b = intent.getExtras();
if(b != null){
myWidgetId = b.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,AppWidgetManager.INVALID_APPWIDGET_ID);
}
if(myWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID){
Toast.makeText(this, "Widget Error : 無有效widget ID", Toast.LENGTH_LONG).show();
finish();
}
}
.. ….
//點擊配置button後調用的方法
private void getAndStoreConfigInfo(){
… … String name為用戶輸入名字,String date為用戶輸入的有效日期
//【1】在preference中保持數據,並顯示所有數據
BirthDayStoreData.storeData(this, myWidgetId, name, date);
BirthDayStoreData.showData(this);
//【2】將配置數據與具體的widget實例相關聯,具體實現見後面
BirthDayStoreData.updateAppWidget(this, myWidgetId,name, date);
//【3】將結果返回給AppWidget Manager,以通知它Configurator已經完成。作用如同startActivityForResult()給出返回值,通知AppWidgetManager某個widgetId已經完成配置,可以在主頁上顯示創建的widget實例
Intent resultIntent = new Intent();
resultIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, myWidgetId);
setResult(RESULT_OK, resultIntent);
//【4】關閉activity
finish();
}
}
配置數據適配到widget實例中
Widget實例的view要通過RemoteViews進行控制,小例子采用靜態方法的方式,代碼片段如下:
public class BirthDayStoreData {
... ...
public static void updateAppWidget(Context context,int widgetId,String name, String date){
//【1】設置Remote view的信息
// 1.1)、獲得remote view對象
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.birday_widget);
// 1.2)、對remote view進行setText()設置
views.setTextViewText(R.id.bd_name, widgetId + ":" + name);
views.setTextViewText(R.id.bd_date, date);
views.setTextViewText(R.id.bd_days, Long.toString(Utils.howFarInDays(Utils.getDate(date))));//Utils是處理日期的類
// 1.3)、通過PendingIntent設置某個view的點擊處理,采用intent方式,可以打開activity,service,receiver等等。本小例子將打開某個網頁
Intent intent = new Intent(Intent.ACTION_VIEW,Uri.parse("http://www.taobao.com"));
PendingIntent pi = PendingIntent.getActivity(context, 0, intent, 0);
views.setOnClickPendingIntent(R.id.bd_buy, pi);
//【2】通過AppWidgetManger,具體實施到widgetId實例上。
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
appWidgetManager.updateAppWidget(widgetId,views);
}
public static void updateAppWidget(Context context,int widgetId){
… …
}
}
根據widget定義,我們App Widget Provider的Java類為BirthDayWidgetProvider,這個類用於管理Widget的各個生命周期。
public class BirthDayWidgetProvider extends AppWidgetProvider{
private static String tag = "BirthDayWidgetProvider";
@Override /* 在3種情況下會調用OnUpdate()。onUpdate()是在main線程中進行,因此如果處理需要花費時間多於10秒,處理應在service中完成。
(1)在時間間隔到時調用,時間間隔在widget定義的android:updatePeriodMillis中設置;
(2)用戶拖拽到主頁,widget實例生成。無論有沒有設置Configure activity,我們在Android4.4的測試中,當用戶拖拽圖片至主頁時,widget實例生成,會觸發onUpdate(),然後再顯示activity(如果有)。這點和資料說的不一樣,資料認為如果設置了Configure acitivity,就不會在一開始調用onUpdate(),而實驗顯示當實例生成(包括創建和重啟時恢復),都會先調用onUpate()。在本例,由於此時在preference尚未有相關數據,創建實例時不能有效進行數據設置。
(3)機器重啟,實例在主頁上顯示,會再次調用onUpdate()*/
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
Log.d(tag,"onUpdate() called. 有 " + appWidgetIds.length + "個widgets");
for(int i = 0 ; i< appWidgetIds.length; i ++){
Log.d(tag,"update widget ID " + appWidgetIds[i]);
BirthDayStoreData.updateAppWidget(context, appWidgetIds[i]);
}
}
@Override /* 某個/些widget從主頁中刪除,在此刪除該widget的相關數據 */
public void onDeleted(Context context, int[] appWidgetIds) {
Log.d(tag,"onDeleted() called");
for(int i = 0 ; i < appWidgetIds.length; i ++){
Log.d(tag,"delete widget " + appWidgetIds[i] + " data");
BirthDayStoreData.removeData(context, appWidgetIds[i]);
}
BirthDayStoreData.showData(context);
}
@Override /* 一般無需重寫此方法。App Widget provider本質是receiver,在此可以跟蹤收到什麼消息,這些消息包括AppWidgetManager.ACTION_APPWIDGET_DELETED/UPDATE/ENABLED/DISABLED,super.onReceiver()會根據消息類型觸發不同的回調函數。如果采用AlarmManager或者自定義的廣播,可以再次進行處理。
*/
public void onReceive(Context context, Intent intent) {
Log.i(tag,"onReceive() : " + intent);
super.onReceive(context, intent);
}
@Override /* 表明至少有一個widget實例被拖到主頁上,即當第一個widget出現時的回調函數。我們需要允許廣播接收器接收消息,第一個widget出現了。我們可以在此注冊其它感興趣的自定義的廣播*/
public void onEnabled(Context context) {
Log.d(tag,"onEnabled() called, context " + context.toString());
// setComponentEnabledSetting相當於在AndriodMenifest.xml文件中隊組件設置android:enabled為true|false。此處是對receiver進行設置,如果true,則允許進行監聽,包括開機重啟。
PackageManager pm = context.getPackageManager();
/*使用new ComponentName("cn.wei.flowingflying.testwidget",".BirthDayWidgetProvider")出現不明原因錯誤,
* 可對類名采用完全名稱,及new ComponentName("cn.wei.flowingflying.testwidget",
* "cn.wei.flowingflying.testwidget.BirthDayWidgetProvider"),
* 或通過系統獲取組件名的方式new ComponentName(context, getClass())*/
pm.setComponentEnabledSetting(new ComponentName(context, getClass()),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
}
@Override /*最後一個widget已從主頁中刪除,在此,確保刪除所有配置數據,無需進行廣播監聽,色織enabled=false,如果有注冊的自定義廣播,在此unregister */
public void onDisabled(Context context) {
BirthDayStoreData.removeAllData(context);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(new ComponentName(context, getClass()),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
}
}
如果主頁沒有實例,新實例的生成觸發順序為:onEnabled() –>onUpdate() –>Configure Activity,頭兩個順序可能會出現變化。估計是AppWidgetManager的異步處理導致廣播消息出現的先後順序問題。如果已經有實例,新實例生成觸發順序為onUpdate() –> Configure activity。配置後,等待定義的時間間隔,進行定期觸發onUpdate()。
機器重啟 onEnabled() –>onUpdate() –> onUpdate(),同樣頭兩個順序可能會交換,此後,等待widget定義的時間間隔,進行定期觸發onUpdate()。
如果我們更新或重裝apk,實例並不會被刪除,會觸發onUpdate()。
補充:Widget圖標
Widget在widget列表中顯示的通常都是widget的外貌,Android模擬器有一個應用Widget Preview可以幫助我們獲取widget的外觀圖標,如下:
通過adb pull將存貯在模擬器SD卡Download路徑下的preview圖片獲取,作為列表顯示圖標。
三星s7微信消息推送收不到怎麼辦?不少用戶反映手機出現爆音、收不到微信、qq消息推送,那麼三星s7微信不提示怎麼辦?三星s7收不到微信消息推送怎麼解決?下文
如題View與ViewGroup裡的方法調用過程總結如下: 1.首先,Activity onCreate並初始化view 2.然後,Activity onResume後調
一、自定義View的分類1、組合View2、繼承重寫View3、全寫View二、簡介組合View,就是組合一些View來形成一個新的View。例如QQ的頭部欄三、了解**
曾經做過一個項目,其中登錄界面的交互令人印象深刻。交互設計師給出了一個非常作的設計,要求做出包含根據情況可變色的下劃線,左側有可變圖標,右側有可變刪除標志的輸入框,如圖