Android中widget的应用【安卓源码解析三】

时间:2022-08-22 14:11:29

           最近看widget,这个widget可以任意放到桌面上(前提是你的桌面上有足够大的空间),感觉效果很炫,widget就是一个BroadcastReceiver,它们使用XML metedata来描述widget的细节。AppWidget framework通过Broadcast intents和Widget通信,widget的更新使用RemoteViews来发送。Remote Views被包装成一个layout和特定内容来显示到桌面上。 widget是继承于AppWidgetProvider类的, 重写几个方法,有:onEnabled():程序第一次启动的时候调用。onUpdate():程序更新的时候用,由android:updatePeriodMillis=“1000"这个来控制的。onDeleted():widget被删除时候调用。onDisabled()这个方法。还得写这个appwidget-provider 布局文件。这个widget在manifest中需要注册一下receiver

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

 在<appwidget-provider>中添加属性android:configure="com.cn.exmaple.widget.daming.ExampleAppWidgetConfigure",指向对应的配置类。这个类可以是继承activity的类。这个程序是我修改的apidemo中的代码。感觉这个例子写的还行,就整理下,方便自己用。下面看程序截图:

 

                          长按桌面,弹出dialog,选择窗口小部件:                 点击大明修改api后弹出的界面:

                            Android中widget的应用【安卓源码解析三】                          Android中widget的应用【安卓源码解析三】

 

                           输入文字:                                                                点击确定后,显示在桌面:

                            Android中widget的应用【安卓源码解析三】                          Android中widget的应用【安卓源码解析三】

 

源码奉上:

在ExmapleWidgetApp工程下:

一、在com.cn.exmaple.widget.daming包下面的ExampleAppWidgetProvider.java类中的代码:

/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.cn.exmaple.widget.daming;

import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.SystemClock;
import android.util.Log;
import android.widget.RemoteViews;

/**
* A widget provider. We have a string that we pull from a preference in order to show
* the configuration settings and the current time when the widget was updated. We also
* register a BroadcastReceiver for time-changed and timezone-changed broadcasts, and
* update then too.
*
* <p>See also the following files:
* <ul>
* <li>ExampleAppWidgetConfigure.java</li>
* <li>ExampleBroadcastReceiver.java</li>
* <li>res/layout/appwidget_configure.xml</li>
* <li>res/layout/appwidget_provider.xml</li>
* <li>res/xml/appwidget_provider.xml</li>
* </ul>
*/
public class ExampleAppWidgetProvider extends AppWidgetProvider {
// log tag
private static final String TAG = "ExampleAppWidgetProvider";

@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
Log.d(TAG, "onUpdate");
// For each widget that needs an update, get the text that we should display:
// - Create a RemoteViews object for it
// - Set the text in the RemoteViews object
// - Tell the AppWidgetManager to show that views object for the widget.
final int N = appWidgetIds.length;
for (int i=0; i<N; i++) {
int appWidgetId = appWidgetIds[i];
String titlePrefix = ExampleAppWidgetConfigure.loadTitlePref(context, appWidgetId);
updateAppWidget(context, appWidgetManager, appWidgetId, titlePrefix);
}
}

@Override
public void onDeleted(Context context, int[] appWidgetIds) {
Log.d(TAG, "onDeleted");
// When the user deletes the widget, delete the preference associated with it.
final int N = appWidgetIds.length;
for (int i=0; i<N; i++) {
ExampleAppWidgetConfigure.deleteTitlePref(context, appWidgetIds[i]);
}
}

@Override
public void onEnabled(Context context) {
Log.d(TAG, "onEnabled");
// When the first widget is created, register for the TIMEZONE_CHANGED and TIME_CHANGED
// broadcasts. We don't want to be listening for these if nobody has our widget active.
// This setting is sticky across reboots, but that doesn't matter, because this will
// be called after boot if there is a widget instance for this provider.
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(
new ComponentName("com.cn.exmaple.widget.daming", ".ExampleBroadcastReceiver"),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
}

@Override
public void onDisabled(Context context) {
// When the first widget is created, stop listening for the TIMEZONE_CHANGED and
// TIME_CHANGED broadcasts.
Log.d(TAG, "onDisabled");
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(
new ComponentName("com.cn.exmaple.widget.daming", ".ExampleBroadcastReceiver"),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
}

static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId, String titlePrefix) {
Log.d(TAG, "updateAppWidget appWidgetId=" + appWidgetId + " titlePrefix=" + titlePrefix);
// Getting the string this way allows the string to be localized. The format
// string is filled in using java.util.Formatter-style format strings.
CharSequence text = context.getString(R.string.appwidget_text_format,
ExampleAppWidgetConfigure.loadTitlePref(context, appWidgetId),
"0x" + Long.toHexString(SystemClock.elapsedRealtime()));

// Construct the RemoteViews object. It takes the package name (in our case, it's our
// package, but it needs this because on the other side it's the widget host inflating
// the layout from our package).
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider);
views.setTextViewText(R.id.appwidget_text, text);

// Tell the widget manager
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}


 

二、在ExampleAppWidgetConfigure.java中的代码:

/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.cn.exmaple.widget.daming;

import java.util.ArrayList;

import android.app.Activity;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;

/**
* The configuration screen for the ExampleAppWidgetProvider widget sample.
*/
public class ExampleAppWidgetConfigure extends Activity {
static final String TAG = "ExampleAppWidgetConfigure";

private static final String PREFS_NAME
= "com.cn.exmaple.widget.daming.ExampleAppWidgetProvider";
private static final String PREF_PREFIX_KEY = "prefix_";

int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
EditText mAppWidgetPrefix;

public ExampleAppWidgetConfigure() {
super();
}

@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);

// Set the result to CANCELED. This will cause the widget host to cancel
// out of the widget placement if they press the back button.
setResult(RESULT_CANCELED);

// Set the view layout resource to use.
setContentView(R.layout.appwidget_configure);

// Find the EditText
mAppWidgetPrefix = (EditText)findViewById(R.id.appwidget_prefix);

// Bind the action for the save button.
findViewById(R.id.save_button).setOnClickListener(mOnClickListener);

// Find the widget id from the intent.
Intent intent = getIntent();
Bundle extras = intent.getExtras();
if (extras != null) {
mAppWidgetId = extras.getInt(
AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
}

// If they gave us an intent without the widget id, just bail.
if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
finish();
}

mAppWidgetPrefix.setText(loadTitlePref(ExampleAppWidgetConfigure.this, mAppWidgetId));
}

View.OnClickListener mOnClickListener = new View.OnClickListener() {
public void onClick(View v) {
final Context context = ExampleAppWidgetConfigure.this;

// When the button is clicked, save the string in our prefs and return that they
// clicked OK.
String titlePrefix = mAppWidgetPrefix.getText().toString();
saveTitlePref(context, mAppWidgetId, titlePrefix);

// Push widget update to surface with newly set prefix
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
ExampleAppWidgetProvider.updateAppWidget(context, appWidgetManager,
mAppWidgetId, titlePrefix);

// Make sure we pass back the original appWidgetId
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
finish();
}
};

// Write the prefix to the SharedPreferences object for this widget
static void saveTitlePref(Context context, int appWidgetId, String text) {
SharedPreferences.Editor prefs = context.getSharedPreferences(PREFS_NAME, 0).edit();
prefs.putString(PREF_PREFIX_KEY + appWidgetId, text);
prefs.commit();
}

// Read the prefix from the SharedPreferences object for this widget.
// If there is no preference saved, get the default from a resource
static String loadTitlePref(Context context, int appWidgetId) {
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0);
String prefix = prefs.getString(PREF_PREFIX_KEY + appWidgetId, null);
if (prefix != null) {
return prefix;
} else {
return context.getString(R.string.appwidget_prefix_default);
}
}

static void deleteTitlePref(Context context, int appWidgetId) {
}

static void loadAllTitlePrefs(Context context, ArrayList<Integer> appWidgetIds,
ArrayList<String> texts) {
}
}



 

三、在ExampleBroadcastReceiver.java中的代码:

/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.cn.exmaple.widget.daming;

import android.appwidget.AppWidgetManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import java.util.ArrayList;

/**
* A BroadcastReceiver that listens for updates for the ExampleAppWidgetProvider. This
* BroadcastReceiver starts off disabled, and we only enable it when there is a widget
* instance created, in order to only receive notifications when we need them.
*/
public class ExampleBroadcastReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
Log.d("ExmampleBroadcastReceiver", "intent=" + intent);

// For our example, we'll also update all of the widgets when the timezone
// changes, or the user or network sets the time.
String action = intent.getAction();
if (action.equals(Intent.ACTION_TIMEZONE_CHANGED)
|| action.equals(Intent.ACTION_TIME_CHANGED)) {
AppWidgetManager gm = AppWidgetManager.getInstance(context);
ArrayList<Integer> appWidgetIds = new ArrayList<Integer>();
ArrayList<String> texts = new ArrayList<String>();

ExampleAppWidgetConfigure.loadAllTitlePrefs(context, appWidgetIds, texts);

final int N = appWidgetIds.size();
for (int i=0; i<N; i++) {
ExampleAppWidgetProvider.updateAppWidget(context, gm, appWidgetIds.get(i), texts.get(i));
}
}
}

}

 

四、在xml下的appwidget-provider的布局文件中:

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="60dp"
android:minHeight="30dp"
android:updatePeriodMillis="86400000"
android:initialLayout="@layout/appwidget_provider"
android:configure="com.cn.exmaple.widget.daming.ExampleAppWidgetConfigure"
>
</appwidget-provider>

 

五、在layout下的appwidget_provider.xml中的代码:

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2006 The Android Open Source Project

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/appwidget_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ff000000"
/>

 

六、在layout下的appwidget_configure.xml中的代码:

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2006 The Android Open Source Project

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/appwidget_configure_instructions"
/>

<EditText
android:id="@+id/appwidget_prefix"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>

<Button
android:id="@+id/save_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@android:string/ok"
/>

</LinearLayout>

 

七、AndroidManifest.xml中的代码:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cn.exmaple.widget.daming"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="8" />

<application android:icon="@drawable/icon" android:label="@string/app_name">

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

<activity android:name=".ExampleAppWidgetConfigure">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>

<receiver android:name=".ExampleBroadcastReceiver" android:enabled="true">
<intent-filter>
<action android:name="android.intent.ACTION_TIMEZONE_CHANGED" />
<action android:name="android.intent.ACTION_TIME" />
</intent-filter>
</receiver>

</application>
</manifest>

 

八、在values下的string.xml中的代码:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="hello">大明修改api</string>
<string name="app_name">大明修改api</string>
<string name="appwidget_configure_instructions">在编辑框中输入的内容可以以widget的方式显示在主界面的.</string>
<string name="appwidget_text_format"><xliff:g id="prefix">%1$s</xliff:g>: <xliff:g id="time">%2$s</xliff:g></string>
<string name="appwidget_prefix_default">《大明修改api》!</string>
</resources>