Android小挂件(APP Widgets)设计指导
应用小挂件(也叫做窗口小挂件)在android1.5的时候被第一次引出,后来再android3.0和android3.1中得到了极大的发展,他们可以展示一些应用的常用信息或者一些相关的信息到桌面上,标准的Android系统镜像中有很多自带的创口小挂件,例如闹钟、音乐等
Figure 1. Example app idgets in Android 4.0.
本文将描述怎么去设计小挂件,以便于能很好的与其他挂件搭配的很默契,也会介绍一些小技巧。
AppWidget 剖析
一个典型的android挂件将会包含三个组件部分一个边界框、一个挂件图形控件、其他的元素。挂件包含了一部分安卓 Vie 控件的子集,他支持textlabel、button、image。其他可用的组件见API Guide部分的Creating the app idget layout(见左侧)
一个设计很好的挂件将会在边界框、框架之间留出一些外部边界,在内部的边界框中会留出一些内部边界。(也就是留出一些padding与margin)。如下图所示
Figure 2. Widgets generally have margins beteen the bounding box and frame,and padding beteen the frame and idget control
Note
在android4.0中,挂件将自动的与边框之间将上margin。
为你的挂件决定大小
每一个挂件都必须指定minWidth 和 minHeight他表示默认最少需要多少的空间展示。当用户添加挂件到他的主屏幕时,通常占用的空间会大于你给的这两个值。Android的主屏幕提供给用户一种方格子的可用空间来放置应用图标或者桌面挂件。这种矩阵方格子在不同的设备上有不同的格式。比如说一般手持设备提供4 X 4的格子。平板设备可以通过8 X 7的格子。当你的挂件被添加的时候,他将会根据minWidth和minHeight指定的宽高自动拉伸去占据最少的格子。使用.9.png图片作为背景和使用可伸展的布局可使你的挂件布局能很好的适配设备的主屏幕格子,以达到很好的使用体验。
你设置的宽度、高度,或者说margin宽度都会有可能运行到不同的设备上,你可以使用下面列出的每个小格子占据的空间的数据来大致的估算你的挂件的最小尺寸。
最佳实践是将你的minWidth与minHeight设置相对保守,定义最小尺寸是可以使你的挂件渲染出很好的默认状态。
比如说假设你有个音乐播放器的挂件,他用作显示当前正在播放的专辑以及名字,我们就只需要一个播放按钮、一个下一曲按钮。
Figure 3. An example music player idget.
你最小的高度就应该为你的两个文本控件的高度+文本之间的margin高度和padding高度。你的最小宽度就应该为播放按钮最短宽度+ 下一曲按钮的最短宽度 + 文本的最短宽度(比如说最长10个字符)+水平的一些margin和padding距离
Figure4. Example sizes and margins for minWidth/minHeight calculations.We chose 144dp as an example good minimum idth for the text labels.
的结果如下
minWidth = 144dp + (2 × 8dp) + (2 × 56dp) = 272dp
minHeight = 48dp + (2 × 4dp) = 56dp
如果你使用的.9.png图片与内容有固有的padding距离,你也需要加上.
可调节大小的挂件
在android 3.1以后,挂件在水平方向与竖直方向都可以被调节大小,意味着minWidth和minHeight的值将变成挂件默认大小的值,你可以使用minResizeWidth和minResizeHeight来表示挂件真正的最小值,小于这个值时,控件将变得模糊和不可用。
特别是那些基于ListVie或者GridVie的集合类特征的挂件
为你的挂件添加margin(外边界宽度)
正如前面提到的,android4.0将可以为主屏幕的挂件自动添加小号、标准的外边界宽度(margin)。对于那些系统版本号在14或者以上的来说,为了平衡主屏幕的视觉,我们不推荐你再额外的添加margin到你的挂件外部。
,对于那个更早一些的版本,添加自己的margin也不复杂,具体在API Guide中有介绍到。
设计挂件的布局和背景图片
很多挂件都只有一个固定的矩形背景或者圆角矩形的形状。其实最好的方法是使用.9.png图片来定义。(具体怎么使用.9.png图片,很简单这里不翻译了,自己去找资料学习)
对于挂件的内容部分,你应该使用可伸缩的布局方式。例如RelativeLayout、LinearLayout、或者frameLayout。这样可以让你的布局文件去适应很多种不同的屏幕尺寸。
下面是一个关于音乐播放的挂件的布局例子。他包含了一个文本域、一个暂停按钮、一个、下一曲按钮,他的margin取决于系统。
如果你看了上面的例子和说明,你也可以开始做一个有弹性的布局
Figure 6. Excerpt flexible layouts and attributes.
使用挂件模板包
当你要开始设计一个新的挂件或者更新现有的挂件,你可以先看一下下面的设计模板。下面的包是可以下载的,他包含了.9.png背景图片和XML和一些针对不同像素密度的PS文件
下面是一个实例源代码
package .example.android.iktionary; import .example.android.iktionary.SimpleWikiHelper.ApiException; import .example.android.iktionary.SimpleWikiHelper.ParseException; import android.app.PendingIntent; import android.app.Service; import android.appidget.AppWidgetManager; import android.appidget.AppWidgetProvider; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android..Uri; import android.os.IBinder; import android.text.format.Time; import android.util.Log; import android.idget.RemoteVies; import java.util.regex.Matcher; import java.util.regex.Pattern; public class WordWidget extends AppWidgetProvider { public static final String WOTD_PATTERN = "(?s)\{\{otd\|(.+?)\|(.+?)\|([^#\|]+).?\}\}"; @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { Log.d("WordWidget.UpdateService", "onUpdate()"); // To prevent any ANR timeouts, e perform the update in a service context.startService(ne 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 idget update for today RemoteVies updateVies = buildUpdate(this); Log.d("WordWidget.UpdateService", "update built"); // Push update for this idget to the home screen ComponentName thisWidget = ne ComponentName(this, WordWidget.class); AppWidgetManager manager = AppWidgetManager.getInstance(this); manager.updateAppWidget(thisWidget, updateVies); Log.d("WordWidget.UpdateService", "idget updated"); } @Override public IBinder onBind(Intent intent) { return null; } public RemoteVies 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 = ne Time(); today.setTono(); // Build the page title for today, such as "March 21" String pageName = res.getString(R.string.template_otd_title, monthNames[today.month], today.monthDay); String pageContent = null; try { // Try querying the Wiktionary API for today's ord 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); } RemoteVies vies = null; Matcher matcher = null; Prefs prefs = ne Prefs(this); if (pageContent == null) { // could not get content, use cache // could be null pageContent = prefs.getPageContent(); } if (pageContent != null) { // e have page content // is it valid? matcher = Pattern.pile(WOTD_PATTERN).matcher(pageContent); } if (matcher != null && matcher.find()) { // valid content, cache it // ensure that latest valid content is // alays cached in case of failures prefs.setPageContent(pageContent); // Build an update that holds the updated idget contents vies = ne RemoteVies(context.getPackageName(), R.layout.idget_ord); String ordTitle = matcher.group(1); vies.setTextVieText(R.id.ord_title, ordTitle); vies.setTextVieText(R.id.ord_type, matcher.group(2)); vies.setTextVieText(R.id.definition, matcher.group(3).trim()); // When user clicks on idget, launch to Wiktionary definition page String definePage = String.format("%s://%s/%s", ExtendedWikiHelper.WIKI_AUTHORITY, ExtendedWikiHelper.WIKI_LOOKUP_HOST, ordTitle); Intent defineIntent = ne Intent(Intent.ACTION_VIEW, Uri.parse(definePage)); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0 , defineIntent, 0 ); vies.setonClickPendingIntent(R.id.idget, pendingIntent); } else { // Didn't find ord of day, so sho error message vies = ne RemoteVies(context.getPackageName(), R.layout.idget_message); vies.setTextVieText(R.id.message, context.getString(R.string.idget_error)); } return vies; } } }
空调维修
- 温岭冰箱全国统一服务热线-全国统一人工【7X2
- 荆州速热热水器维修(荆州热水器维修)
- 昆山热水器故障码5ER-昆山热水器故障码26
- 温岭洗衣机24小时服务电话—(7X24小时)登记报
- 统帅热水器售后维修服务电话—— (7X24小时)登
- 阳江中央空调统一电话热线-阳江空调官方售后电
- 乌鲁木齐阳春燃气灶厂家服务热线
- 珠海许昌集成灶售后服务电话-全国统一人工【
- 乌鲁木齐中央空调维修服务专线-乌鲁木齐中央空
- 新沂热水器故障电话码维修-新沂热水器常见故障
- 诸城壁挂炉24小时服务热线电话
- 靖江空调24小时服务电话-——售后维修中心电话
- 空调室外滴水管维修(空调室外排水管维修)
- 九江壁挂炉400全国服务电话-(7X24小时)登记报修
- 热水器故障码f.22怎么解决-热水器f0故障解决方法
- 营口热水器售后维修服务电话—— 全国统一人工