Android小挂件(APP Widgets)设计指导

应用小挂件(也叫做窗口小挂件)在android1.5的时候被第一次引出,后来再android3.0和android3.1中得到了极大的发展,他们可以展示一些应用的常用信息或者一些相关的信息到桌面上,标准的Android系统镜像中有很多自带的创口小挂件,例如:闹钟、音乐等

Figure 1. Example app widgets in Android 4.0.

本文将描述怎么去设计小挂件,以便于能很好的与其他挂件搭配的很默契,同时也会介绍一些小技巧。

AppWidget 剖析

一个典型的android挂件将会包含三个组件部分:一个边界框、一个挂件图形控件、其他的元素。挂件包含了一部分安卓 View 控件的子集,他支持:textlabel、button、image。其他可用的组件见API Guide部分的Creating the app widget layout(见左侧)

一个设计很好的挂件将会在边界框、框架之间留出一些外部边界,在内部的边界框中会留出一些内部边界。(也就是留出一些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他表示默认最少需要多少的空间展示。当用户添加挂件到他的主屏幕时,通常占用的空间会大于你给的这两个值。Android的主屏幕提供给用户一种方格子的可用空间来放置应用图标或者桌面挂件。这种矩阵方格子在不同的设备上有不同的格式。比如说:一般手持设备提供4 X 4的格子。但是平板设备可以通过8 X 7的格子。当你的挂件被添加的时候,他将会根据minWidth和minHeight指定的宽高自动拉伸去占据最少的格子。使用.9.png图片作为背景和使用可伸展的布局可使你的挂件布局能很好的适配设备的主屏幕格子,以达到很好的使用体验。
        你设置的宽度、高度,或者说margin宽度都会有可能运行到不同的设备上,你可以使用下面列出的每个小格子占据的空间的数据来大致的估算你的挂件的最小尺寸。

最佳实践是将你的minWidth与minHeight设置相对保守,定义最小尺寸是可以使你的挂件渲染出很好的默认状态。

比如说:假设你有个音乐播放器的挂件,他用作显示当前正在播放的专辑以及名字,我们就只需要一个播放按钮、一个下一曲按钮。

Figure 3. An example music player widget.

你最小的高度就应该为你的两个文本控件的高度+文本之间的margin高度和padding高度。你的最小宽度就应该为播放按钮最短宽度+ 下一曲按钮的最短宽度 + 文本的最短宽度(比如说最长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.

最后的结果如下:

minWidth = 144dp + (2 × 8dp) + (2 × 56dp) = 272dp
minHeight = 48dp + (2 × 4dp) = 56dp

如果你使用的.9.png图片与内容有固有的padding距离,你也需要加上.

可调节大小的挂件

在android 3.1以后,挂件在水平方向与竖直方向都可以被调节大小,意味着:minWidth和minHeight的值将变成挂件默认大小的值,你可以使用minResizeWidth和minResizeHeight来表示挂件真正的最小值,小于这个值时,控件将变得模糊和不可用。

特别是那些基于ListView或者GridView的集合类特征的挂件

为你的挂件添加margin(外边界宽度)

正如前面提到的,android4.0将可以为主屏幕的挂件自动添加小号、标准的外边界宽度(margin)。对于那些系统版本号在14或者以上的来说,为了平衡主屏幕的视觉,我们不推荐你再额外的添加margin到你的挂件外部。
        当然,对于那个更早一些的版本,添加自己的margin也不复杂,具体在API Guide中有介绍到。

设计挂件的布局和背景图片

很多挂件都只有一个固定的矩形背景或者圆角矩形的形状。其实最好的方法是使用.9.png图片来定义。(具体怎么使用.9.png图片,很简单这里不翻译了,自己去找资料学习)

对于挂件的内容部分,你应该使用可伸缩的布局方式。例如:RelativeLayout、LinearLayout、或者FrameLayout。这样可以让你的布局文件去适应很多种不同的屏幕尺寸。

下面是一个关于音乐播放的挂件的布局例子。他包含了一个文本域、一个暂停按钮、一个、下一曲按钮,他的margin取决于系统。

<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.

使用挂件模板包

当你要开始设计一个新的挂件或者更新现有的挂件,你可以先看一下下面的设计模板。下面的包是可以下载的,他包含了.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;
    }
  }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android开发之获取网络链接状态

    网络开发是Android程序设计一个非常重要的内容,今天本文就和大家一起分享一下Android网络开发的一点经验. 本文主要通过实例形式说明了Android获取网络链接状态的方法.具体内容如下: 就目前的Android手机来说,可能存在如下5种网络状态: ----无网络(这种状态可能是因为手机停机,网络没有开启,信号不好等原因) ----使用WIFI上网 ----CMWAP(中国移动代理) ----CMNET上网 ----2G/3G/4G上网 很多时候我们需要判断用户是否开启网络设置,通常通过C

  • Android中捕获TTextView文本中的链接点击事件方法

    Android中的TTextView很强大,我们可以不仅可以设置纯文本为其内容,还可以设置包含网址和电子邮件地址的内容,并且使得这些点击可以点击.但是我们可以捕获并控制这些链接的点击事件么,当然是可以的. 本文将一个超级简单的例子介绍一下如何实现在Android TextView 捕获链接的点击事件. 关键实现 实现原理就是将所有的URL设置成ClickSpan,然后在它的onClick事件中加入你想要的控制逻辑就可以了. 复制代码 代码如下: private void setLinkClick

  • Android中Textview超链接实现方式

    TextView中的超链接可以通过几种方式实现: 一.Html.fromHtml方式 TextView,本身就支持部分的Html格式标签.这其中包括常用的字体大小颜色设置,文本链接等.使用起来也比较方便,只需要使用Html类转换一下即可: textView.setText(Html.fromHtml(str)); 代码如下: public class Test10Activity extends Activity { TextView textView ; @Override protected

  • Android实现使用微信登录第三方APP的方法

    本文实例讲述了Android实现使用微信登录第三方APP的方法.分享给大家供大家参考,具体如下: 使用微信登录APP,免去注册过程,现在已经有很多的类似应用了.集成该功能过程不复杂,但还是有一些地方需要注意的. 开始之前,需要做下面的准备工作. 1.到微信开放平台注册你的APP,并申请开通微信登录的权限.参考这里: https://open.weixin.qq.com// 2.下载Android SDK和签名查看工具,请参考: https://open.weixin.qq.com/cgi-bin

  • iOS和Android用同一个二维码实现跳转下载链接的方法

    前言 最近一个项目需要iOS和安卓使用一个二维码,让扫描的机器自己识别操作系统实现跳转到相应的下载链接.比如iPhone用微信进行扫描就让他跳转appStore的下载页面,安卓机器使用微信扫描就直接跳浏览器下载.但是这二维码还有一个需求就是,用户已经下载了这个app,当用户打开app进入到注册页面时,再次扫描这个二维码时,自动填写邀请码进行注册.那么该如何实现,细节就不说了,直接上代码. 使用js实现,其实代码非常简单. 使用时直接拷贝代码,改掉相应的链接就好. PS:该链接在微信环境打开时还是

  • Android编程实现点击链接打开APP功能示例

    本文实例讲述了Android编程实现点击链接打开APP功能.分享给大家供大家参考,具体如下: 在Android中点击链接打开APP是一个很常见的需求.例如,电商为用户发送优惠券之后经常会下发一条短信:某某优惠券已发送到您的账户中,点击 xxx 链接即可查看!此时当用户点击链接之后会直接打开本地APP,进入相关页面. 功能实现: 1.在manifest中为相应的activity添加intent-filter: <activity android:name=".TestActivity&quo

  • Android开发技巧之在a标签或TextView控件中单击链接弹出Activity(自定义动作)

    在5.2.1节和5.2.2节介绍了<a>标签以及TextView自动识别的特殊文本(网址.电话号.Email等),这些都可以通过单击来触发不同的动作.虽然这些单击动作已经可以满足大多数需要了,但如果读者想在单击链接时执行任意自定义的动作,那么本节的内容非看不可. 现在让我们使用5.2.1节介绍的方法重新查看Html.java文件的内容,随便找一个处理Html标签的方法,例 如,endA方法.该方法用于处理</a>标签.我们会发现在该方法中如下的语句. text.setSpan(ne

  • 亲自动手实现Android App插件化

    Android插件化目前国内已经有很多开源的工程了,不过如果不实际开发一遍,很难掌握的很好. 下面是自己从0开始,结合目前开源的项目和博客,动手开发插件化方案. 按照需要插件化主要解决下面的几种问题: 1. 代码的加载 (1) 要解决纯Java代码的加载 (2) Android组件加载,如Activity.Service.Broadcast Receiver.ContentProvider,因为它们是有生命周期的,所以要特殊处理 (3) Android Native代码的加载 (4) Andro

  • Android下保存简单网页到本地(包括简单图片链接转换)实现代码

    最近在做一个项目涉及到将包含图片的简单网页下载到本地,方便离线时观看,在这里分享一下,大家做下简单修改就可以用到自己的项目中了.(这里用到了AQuery库) 复制代码 代码如下: package com.nekocode.xuedao.utils; import java.io.File;import java.io.FileOutputStream;import java.util.ArrayList;import java.util.regex.Matcher;import java.uti

  • Android实现TextView中文字链接的4种方式介绍及代码

    Android 的实现TextView中文字链接的方式有很多种. 总结起来大概有4种: 1.当文字中出现URL.E-mail.电话号码等的时候,可以将TextView的android:autoLink属性设置为相应的的值,如 果是所有的类型都出来就是android:autoLink="all".当然也可以在java代码里 做,textView01.setAutoLinkMask(Linkify.ALL); 2.将要处理的文字写到一个资源文件,如string.xml,然后的java代码里

  • Android如何动态改变App桌面图标

    时不时的我们就会发现,一些我们常见的应用,比如某宝,某东,在一些特殊的日子中,比如双十一,元旦,为了迎合这样一个日子的气氛,在桌面的应用图标就会发生改变,其实对于这样的一个桌面图标更换,Android中为我们提供了AndroidManifest.xml里的<activity-alias>标签实现方式(更多文章请关注我的微信公众账号,左边二维码). 我们知道,我们每写一个 Activity就要在AndroidManifest进行配置一下,我们才可以正常的启动它,除此之外,我们还可以对它设置一个别

随机推荐