文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>[译]了解 HS Widgets和AppWidget framework

[译]了解 HS Widgets和AppWidget framework

时间:2009-06-16  来源:okitamicuki1412

前边有几篇针对Android 1.5 SDK “cupcake” 的讨论 (提前品尝 Android 1.5 SDK的滋味(CupCake), 第二代Android的“Cupcake计划”)。 Android开发小组前些天发布了”early-look” android 1.5 SDK,其目的是让大家预先了解即将在新版本中出现的新功能。而且前边也提到过,在今后会陆续针对这个版本的新功能做一些介绍,帮助大家能更好的利用Android小组成员智慧的“结晶”。

这次将与大家一同了解 AppWidget framework,这个令人期待的新平台内嵌架构可以允许开发者编写”widgets”。用户可以将它直接拖放到主窗口作为互动的接口,或者可以通过主 窗口的Widget辅助某些APPs和后台服务程序的应用,例如:显示calendar中即将开始任务、查看后台正在播放的音乐信息等。

当有新的Widgets被拖放到主窗口的同时,它们也会被分配(或者预留)一个显示更多信息内容的区域,其信息可以是由它所服务的Apps提供,用 户就可以方便快捷的通过Widget对相应的app做出一些快速的操作或回应。可以有两种方式对当前Widget提供更新,其一是通过后台的服务,根据日 程设定来对某些内容作出更新,另外一种方法是直接应用AppWidget framework预置的自动更新框架来满足基本需要(Automatic update mechanism)。(注:这里所提到的更新,并不是指程序自身的更新,而是其所显示的内容或者某些应用级别的更新)。

从较高的实现层级来分析Widget的工作原理。可以发现它其实是BroadcastReceiver,并借助相应xml与其匹配来描述更多 widget自身细节信息,从而可以使AppWidget framework借助broadcast intents与某些Widget通讯。其通讯的信息内容,是借助于RemoteViews将Layout和内容捆绑为独立的对象,Widget作为一个载体将其所包含的内容显示在主窗口。

借用现有程序,可以非常容易为其添加Widget功能。这里提供了一个完整的实例用于演示其所包含的所有内容(实例所实现的功能是显示维基词典的 “Word of the day”。接下来就一些比较重要和典型的代码做一些详尽的补充和解释。

首先需要创建一个XML Metadata文件作为描述Widget属性的载体,其内容包括这个Widget在主窗口需要被保留的区域、一个初始化的layout和定义更新频率的 属性。针对主窗口预留区域的尺寸,是基于标准尺寸而根据一定比例划分的,并不能随意定义尺寸的数值。下边这个是定义区域尺寸的计算公式(Width)。

Minimum size in dip = (Number of cells * 74dip) - 2dip

在这个例子中的尺寸是二倍的标准宽度(cell width)和一个单元高度(cell height),转化为实际的dip尺寸是:146 dip × 72 dip。同时定义Widget每天更新一次(由例子的需求而定),转化为milliseconds:86,400,000。下边是完成后的XML matedata:

?View Code XML
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="146dip"
android:minHeight="72dip"
android:initialLayout="@layout/widget_message"
android:updatePeriodMillis="86400000"
/>

下一步,将前面设置好的XML Matedata与相应的BroadcastReceiver通过manifest.xml相关联:

?View Code XML
<!-- Broadcast Receiver that will process AppWidget updates -->
<receiver android:name=".WordWidget" android:label="@string/widget_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_word" />
</receiver>

<!-- Service to perform web API queries -->
<service android:name=".WordWidget$UpdateService" />

最后,实现上一操作中提到的BroadcastReceiver,由它来实际的负责有关AppWidget请求,帮助Widgets来管理不同的 Broadcast事件。这里用到一个比较关键的类 APPWidgetProvider(官方暂时还没有提供详尽的文档)。另外需要着重注意的一点,当通过后台的服务来负责Widget实际的更新需求时, 其所应用到的BroadcastReceivers从属于Application Not Responding (ANR) timer,在遇到某些延迟或者通讯响应过长时,ANR会促使系统为用户提供一个选择来强行关闭当前程序。针对基于网络应用情况,通常会遇到因为网络原因 而引起的延迟问题,通过创建相应的Service来避免前边所提到的情况(ANR Timeouts)。以下是实现这个例子中BroadcastReceiver的源代码:

01./** 02. * Define a simple widget that shows the Wiktionary "Word of the day." To build 03. * an update we spawn a background {@link Service} to perform the API queries. 04. */ 05.public class WordWidget extends AppWidgetProvider { 06.    @Override 07.    public void onUpdate(Context context, AppWidgetManager appWidgetManager, 08.        int[] appWidgetIds) { 09.        // To prevent any ANR timeouts, we perform the update in a service 10.        context.startService(new Intent(context, UpdateService.class)); 11.    } 12.  13.    public static class UpdateService extends Service { 14.        @Override 15.        public void onStart(Intent intent, int startId) { 16.            // Build the widget update for today 17.            RemoteViews updateViews = buildUpdate(this); 18.  19.            // Push update for this widget to the home screen 20.            ComponentName thisWidget = new ComponentName(this, WordWidget.class); 21.            AppWidgetManager manager = AppWidgetManager.getInstance(this); 22.            manager.updateAppWidget(thisWidget, updateViews); 23.        } 24.  25.        /** 26.         * Build a widget update to show the current Wiktionary 27.         * "Word of the day." Will block until the online API returns. 28.         */ 29.        public RemoteViews buildUpdate(Context context) { 30.            // Pick out month names from resources 31.            Resources res = context.getResources(); 32.            String[] monthNames = res.getStringArray(R.array.month_names); 33.  34.            // Find current month and day 35.            Time today = new Time(); 36.            today.setToNow(); 37.  38.            // Build today's page title, like "Wiktionary:Word of the day/March 21" 39.            String pageName = res.getString(R.string.template_wotd_title, 40.                monthNames[today.month], today.monthDay); 41.            RemoteViews updateViews = null; 42.            String pageContent = ""; 43.  44.            try { 45.                // Try querying the Wiktionary API for today's word 46.                SimpleWikiHelper.prepareUserAgent(context); 47.                pageContent = SimpleWikiHelper.getPageContent(pageName, false); 48.            } catch (ApiException e) { 49.                Log.e("WordWidget", "Couldn't contact API", e); 50.            } catch (ParseException e) { 51.                Log.e("WordWidget", "Couldn't parse API response", e); 52.            } 53.  54.            // Use a regular expression to parse out the word and its definition 55.            Pattern pattern = Pattern.compile(SimpleWikiHelper.WORD_OF_DAY_REGEX); 56.            Matcher matcher = pattern.matcher(pageContent); 57.            if (matcher.find()) { 58.                // Build an update that holds the updated widget contents 59.                updateViews = new RemoteViews(context.getPackageName(), R.layout.widget_word); 60.  61.                String wordTitle = matcher.group(1); 62.                updateViews.setTextViewText(R.id.word_title, wordTitle); 63.                updateViews.setTextViewText(R.id.word_type, matcher.group(2)); 64.                updateViews.setTextViewText(R.id.definition, matcher.group(3).trim()); 65.  66.                // When user clicks on widget, launch to Wiktionary definition page 67.                String definePage = res.getString(R.string.template_define_url, 68.                        Uri.encode(wordTitle)); 69.                Intent defineIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(definePage)); 70.                PendingIntent pendingIntent = PendingIntent.getActivity(context, 71.                        0 /* no requestCode */, defineIntent, 0 /* no flags */); 72.                updateViews.setOnClickPendingIntent(R.id.widget, pendingIntent); 73.  74.            } else { 75.                // Didn't find word of day, so show error message 76.                updateViews = new RemoteViews(context.getPackageName(), R.layout.widget_message); 77.                CharSequence errorMessage = context.getText(R.string.widget_error); 78.                updateViews.setTextViewText(R.id.message, errorMessage); 79.            } 80.            return updateViews; 81.        } 82.  83.        @Override 84.        public IBinder onBind(Intent intent) { 85.            // We don't need to bind to this service 86.            return null; 87.        } 88.    } 89.}

回顾文章所应用的例子,如果Widget在特定的条件下需要更新内容时,将通过一个在线的API接口取得最新的数据,然后通过APPWidget framework自动根据我们的需要来请求更新。正如将这个Widget拖放到Home Screen中,每天都会自动的加载最新的”word of the day”。

有关系统资源合理应用的提醒:如果Widgets被设计的过于频繁更新其内容,将会吃掉非常多的系统电量资源,所以为你的Widget更新提供一个 优化的更新周期是非常必要的。可以为用户提供一个可以自由设定更新周期的接口,这样就可以让用户根据当前设备状况来及时作出调整,另外可以让程序根据获取的当前设备电量来自动调整更新周期。

我们期待着你所创造出方便用户使用的Widget,或者提供一些惊奇的想法和大家来共同实现。

排行榜 更多 +
摩托城市兜风模拟

摩托城市兜风模拟

模拟经营 下载
开课云

开课云

学习教育 下载
风度

风度

游戏工具 下载