Android小掛件(APP Widgets)設(shè)計指導(dǎo)
應(yīng)用小掛件(也叫做窗口小掛件)在android1.5的時候被第一次引出,后來再android3.0和android3.1中得到了極大的發(fā)展,他們可以展示一些應(yīng)用的常用信息或者一些相關(guān)的信息到桌面上,標(biāo)準(zhǔn)的Android系統(tǒng)鏡像中有很多自帶的創(chuàng)口小掛件,例如:鬧鐘、音樂等
Figure 1. Example app widgets in Android 4.0.
本文將描述怎么去設(shè)計小掛件,以便于能很好的與其他掛件搭配的很默契,同時也會介紹一些小技巧。
AppWidget 剖析
一個典型的android掛件將會包含三個組件部分:一個邊界框、一個掛件圖形控件、其他的元素。掛件包含了一部分安卓 View 控件的子集,他支持:textlabel、button、image。其他可用的組件見API Guide部分的Creating the app widget layout(見左側(cè))
一個設(shè)計很好的掛件將會在邊界框、框架之間留出一些外部邊界,在內(nèi)部的邊界框中會留出一些內(nèi)部邊界。(也就是留出一些padding與margin)。如下圖所示:
Figure 2. Widgets generally have margins between the bounding box and frame,and padding between the frame and widget control
Note:
在android4.0中,掛件將自動的與邊框之間將上margin。
為你的掛件決定大小
每一個掛件都必須指定minWidth 和 minHeight他表示默認(rèn)最少需要多少的空間展示。當(dāng)用戶添加掛件到他的主屏幕時,通常占用的空間會大于你給的這兩個值。Android的主屏幕提供給用戶一種方格子的可用空間來放置應(yīng)用圖標(biāo)或者桌面掛件。這種矩陣方格子在不同的設(shè)備上有不同的格式。比如說:一般手持設(shè)備提供4 X 4的格子。但是平板設(shè)備可以通過8 X 7的格子。當(dāng)你的掛件被添加的時候,他將會根據(jù)minWidth和minHeight指定的寬高自動拉伸去占據(jù)最少的格子。使用.9.png圖片作為背景和使用可伸展的布局可使你的掛件布局能很好的適配設(shè)備的主屏幕格子,以達(dá)到很好的使用體驗。
你設(shè)置的寬度、高度,或者說margin寬度都會有可能運行到不同的設(shè)備上,你可以使用下面列出的每個小格子占據(jù)的空間的數(shù)據(jù)來大致的估算你的掛件的最小尺寸。
最佳實踐是將你的minWidth與minHeight設(shè)置相對保守,定義最小尺寸是可以使你的掛件渲染出很好的默認(rèn)狀態(tài)。
比如說:假設(shè)你有個音樂播放器的掛件,他用作顯示當(dāng)前正在播放的專輯以及名字,我們就只需要一個播放按鈕、一個下一曲按鈕。
Figure 3. An example music player widget.
你最小的高度就應(yīng)該為你的兩個文本控件的高度+文本之間的margin高度和padding高度。你的最小寬度就應(yīng)該為播放按鈕最短寬度+ 下一曲按鈕的最短寬度 + 文本的最短寬度(比如說最長10個字符)+水平的一些margin和padding距離
Figure4. Example sizes and margins for minWidth/minHeight calculations.We chose 144dp as an example good minimum width for the text labels.
最后的結(jié)果如下:
minWidth = 144dp + (2 × 8dp) + (2 × 56dp) = 272dp
minHeight = 48dp + (2 × 4dp) = 56dp
如果你使用的.9.png圖片與內(nèi)容有固有的padding距離,你也需要加上.
可調(diào)節(jié)大小的掛件
在android 3.1以后,掛件在水平方向與豎直方向都可以被調(diào)節(jié)大小,意味著:minWidth和minHeight的值將變成掛件默認(rèn)大小的值,你可以使用minResizeWidth和minResizeHeight來表示掛件真正的最小值,小于這個值時,控件將變得模糊和不可用。
特別是那些基于ListView或者GridView的集合類特征的掛件
為你的掛件添加margin(外邊界寬度)
正如前面提到的,android4.0將可以為主屏幕的掛件自動添加小號、標(biāo)準(zhǔn)的外邊界寬度(margin)。對于那些系統(tǒng)版本號在14或者以上的來說,為了平衡主屏幕的視覺,我們不推薦你再額外的添加margin到你的掛件外部。
當(dāng)然,對于那個更早一些的版本,添加自己的margin也不復(fù)雜,具體在API Guide中有介紹到。
設(shè)計掛件的布局和背景圖片
很多掛件都只有一個固定的矩形背景或者圓角矩形的形狀。其實最好的方法是使用.9.png圖片來定義。(具體怎么使用.9.png圖片,很簡單這里不翻譯了,自己去找資料學(xué)習(xí))
對于掛件的內(nèi)容部分,你應(yīng)該使用可伸縮的布局方式。例如:RelativeLayout、LinearLayout、或者FrameLayout。這樣可以讓你的布局文件去適應(yīng)很多種不同的屏幕尺寸。
下面是一個關(guān)于音樂播放的掛件的布局例子。他包含了一個文本域、一個暫停按鈕、一個、下一曲按鈕,他的margin取決于系統(tǒng)。
<FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:padding="@dimen/widget_margin"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" android:background="@drawable/my_widget_background"> <TextView android:id="@+id/song_info" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" /> <Button android:id="@+id/play_button" android:layout_width="@dimen/my_button_width" android:layout_height="match_parent" /> <Button android:id="@+id/skip_button" android:layout_width="@dimen/my_button_width" android:layout_height="match_parent" /> </LinearLayout> </FrameLayout>
如果你看了上面的例子和說明,你也可以開始做一個有彈性的布局
Figure 6. Excerpt flexible layouts and attributes.
使用掛件模板包
當(dāng)你要開始設(shè)計一個新的掛件或者更新現(xiàn)有的掛件,你可以先看一下下面的設(shè)計模板。下面的包是可以下載的,他包含了.9.png背景圖片和XML和一些針對不同像素密度的PS文件
下面是一個實例源代碼
/* * Copyright (C) 2009 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.example.android.wiktionary; import com.example.android.wiktionary.SimpleWikiHelper.ApiException; import com.example.android.wiktionary.SimpleWikiHelper.ParseException; import android.app.PendingIntent; import android.app.Service; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.net.Uri; import android.os.IBinder; import android.text.format.Time; import android.util.Log; import android.widget.RemoteViews; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Define a simple widget that shows the Wiktionary "Word of the day." To build * an update we spawn a background {@link Service} to perform the API queries. */ public class WordWidget extends AppWidgetProvider { /** * Regular expression that splits "Word of the day" entry into word * name, word type, and the first description bullet point. */ public static final String WOTD_PATTERN = "(?s)\\{\\{wotd\\|(.+?)\\|(.+?)\\|([^#\\|]+).*?\\}\\}"; @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { Log.d("WordWidget.UpdateService", "onUpdate()"); // To prevent any ANR timeouts, we perform the update in a service context.startService(new Intent(context, UpdateService.class)); } public static class UpdateService extends Service { @Override public void onStart(Intent intent, int startId) { Log.d("WordWidget.UpdateService", "onStart()"); // Build the widget update for today RemoteViews updateViews = buildUpdate(this); Log.d("WordWidget.UpdateService", "update built"); // Push update for this widget to the home screen ComponentName thisWidget = new ComponentName(this, WordWidget.class); AppWidgetManager manager = AppWidgetManager.getInstance(this); manager.updateAppWidget(thisWidget, updateViews); Log.d("WordWidget.UpdateService", "widget updated"); } @Override public IBinder onBind(Intent intent) { return null; } /** * Build a widget update to show the current Wiktionary * "Word of the day." Will block until the online API returns. */ public RemoteViews buildUpdate(Context context) { // Pick out month names from resources Resources res = context.getResources(); String[] monthNames = res.getStringArray(R.array.month_names); // Find current month and day Time today = new Time(); today.setToNow(); // Build the page title for today, such as "March 21" String pageName = res.getString(R.string.template_wotd_title, monthNames[today.month], today.monthDay); String pageContent = null; try { // Try querying the Wiktionary API for today's word SimpleWikiHelper.prepareUserAgent(context); pageContent = SimpleWikiHelper.getPageContent(pageName, false); } catch (ApiException e) { Log.e("WordWidget", "Couldn't contact API", e); } catch (ParseException e) { Log.e("WordWidget", "Couldn't parse API response", e); } RemoteViews views = null; Matcher matcher = null; Prefs prefs = new Prefs(this); if (pageContent == null) { // could not get content, use cache // could be null pageContent = prefs.getPageContent(); } if (pageContent != null) { // we have page content // is it valid? matcher = Pattern.compile(WOTD_PATTERN).matcher(pageContent); } if (matcher != null && matcher.find()) { // valid content, cache it // ensure that latest valid content is // always cached in case of failures prefs.setPageContent(pageContent); // Build an update that holds the updated widget contents views = new RemoteViews(context.getPackageName(), R.layout.widget_word); String wordTitle = matcher.group(1); views.setTextViewText(R.id.word_title, wordTitle); views.setTextViewText(R.id.word_type, matcher.group(2)); views.setTextViewText(R.id.definition, matcher.group(3).trim()); // When user clicks on widget, launch to Wiktionary definition page String definePage = String.format("%s://%s/%s", ExtendedWikiHelper.WIKI_AUTHORITY, ExtendedWikiHelper.WIKI_LOOKUP_HOST, wordTitle); Intent defineIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(definePage)); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0 /* no requestCode */, defineIntent, 0 /* no flags */); views.setOnClickPendingIntent(R.id.widget, pendingIntent); } else { // Didn't find word of day, so show error message views = new RemoteViews(context.getPackageName(), R.layout.widget_message); views.setTextViewText(R.id.message, context.getString(R.string.widget_error)); } return views; } } }
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
android+json+php+mysql實現(xiàn)用戶反饋功能方法解析
相信每個項目都會有用戶反饋建議等功能,這個實現(xiàn)的方法很多,下面是我實現(xiàn)的方法,供大家交流2012-11-11Android Insets相關(guān)知識總結(jié)
這篇文章主要介紹了Android Insets相關(guān)知識總結(jié),幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下2021-03-03Android 圖文詳解Binder進(jìn)程通信底層原理
Android系統(tǒng)中,多進(jìn)程間的通信都是依賴于底層Binder IPC機(jī)制,Binder機(jī)制是一種RPC方案。例如:當(dāng)進(jìn)程A中的Activity與進(jìn)程B中的Service通信時,就使用了binder機(jī)制2021-10-10Android藍(lán)牙的開啟和搜索設(shè)備功能開發(fā)實例
這篇文章主要介紹了Android藍(lán)牙服務(wù)啟動搜索流程,了解內(nèi)部原理是為了幫助我們做擴(kuò)展,同時也是驗證了一個人的學(xué)習(xí)能力,如果你想讓自己的職業(yè)道路更上一層樓,這些底層的東西你是必須要會的2023-04-04Android通過AlarmManager類實現(xiàn)簡單鬧鐘功能
這篇文章主要為大家詳細(xì)介紹了Android通過AlarmManager類實現(xiàn)簡單鬧鐘功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-06-06Android NDK生成及連接靜態(tài)庫與動態(tài)庫的方法
這篇文章主要介紹了Android NDK生成及連接靜態(tài)庫與動態(tài)庫的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-08-08