Android 2.2新增Widget之ProtipWidget源码
作者: Android开发网原创 时间: 2010-07-18
在Android 2.2 SDK中我们可能首次启动模拟器可以看到和以前不一样的是多出了一个绿色的小机器人提示信息,Google给我们了演示了Android中如何通过RemoteView和简单的图片轮换方式实现动画效果在桌面小工具中,appWidget的基类时AppWidgetProvider类,不过Widget本身的生命周期管理并非Activity,相对于的而是BroadcastReceiver广播方式处理的,Android 2.2新增的Widget的实现大家可以从中学习到很多有用的知识。

public class ProtipWidget extends AppWidgetProvider {
public static final String ACTION_NEXT_TIP = "com.android.misterwidget.NEXT_TIP"; //定义action切换到下一条提示
public static final String ACTION_POKE = "com.android.misterwidget.HEE_HEE"; //唤醒小机器人

public static final String EXTRA_TIMES = "times";

public static final String PREFS_NAME = "Protips";
public static final String PREFS_TIP_NUMBER = "widget_tip";

private static Random sRNG = new Random(); //轮换图片用到的,生成一个静态的随机数生成器

private static final Pattern sNewlineRegex = Pattern.compile(" *\\n *");
private static final Pattern sDrawableRegex = Pattern.compile(" *@(drawable/[a-z0-9_]+) *");

// 初始化时Droid是眼睛没有睁开,同时没有信息提示
private int mIconRes = R.drawable.droidman_open;
private int mMessage = 0;

private AppWidgetManager mWidgetManager = null;
private int[] mWidgetIds;
private Context mContext;

private CharSequence[] mTips;

private void setup(Context context) {
mContext = context;
mWidgetManager = AppWidgetManager.getInstance(context);
mWidgetIds = mWidgetManager.getAppWidgetIds(new ComponentName(context, ProtipWidget.class));

SharedPreferences pref = context.getSharedPreferences(PREFS_NAME, 0);
mMessage = pref.getInt(PREFS_TIP_NUMBER, 0);

mTips = context.getResources().getTextArray(R.array.tips);

if (mTips != null) {
if (mMessage >= mTips.length) mMessage = 0;
} else {
mMessage = -1;
}

}

public void goodmorning() { //Android开发网提示线程中轮换图片,使用了500,200,100等这样的0.5秒休眠,0.2秒休眠实现了动画的间隔效果
mMessage = -1;
try {
setIcon(R.drawable.droidman_down_closed);
Thread.sleep(500);
setIcon(R.drawable.droidman_down_open);
Thread.sleep(200);
setIcon(R.drawable.droidman_down_closed);
Thread.sleep(100);
setIcon(R.drawable.droidman_down_open);
Thread.sleep(600);
} catch (InterruptedException ex) {
}
mMessage = 0;
mIconRes = R.drawable.droidman_open;
refresh();
}

@Override
public void onReceive(Context context, Intent intent) { //上面android123已经讲到了,appWidget是基于broadcastreceiver类的,所以说需要响应onReceive通过action来驱动事件。
setup(context);

if (intent.getAction().equals(ACTION_NEXT_TIP)) {
mMessage = getNextMessageIndex();
SharedPreferences.Editor pref = context.getSharedPreferences(PREFS_NAME, 0).edit();
pref.putInt(PREFS_TIP_NUMBER, mMessage);
pref.commit();
refresh();
} else if (intent.getAction().equals(ACTION_POKE)) {
blink(intent.getIntExtra(EXTRA_TIMES, 1));
} else if (intent.getAction().equals(AppWidgetManager.ACTION_APPWIDGET_ENABLED)) {
goodmorning();
} else {
mIconRes = R.drawable.droidman_open;
refresh();
}
}

private void refresh() { //管理如果有多个本widget执行需要逐个更新
RemoteViews rv = buildUpdate(mContext);
for (int i : mWidgetIds) {
mWidgetManager.updateAppWidget(i, rv);
}
}

private void setIcon(int resId) {
mIconRes = resId;
refresh();
}

private int getNextMessageIndex() {
return (mMessage + 1) % mTips.length;
}

private void blink(int blinks) {
if (mMessage < 0) return;

//Android123提示大家比较有意思的就是小绿人眼睛的一开一闭,这里使用的是图片轮换方式来实现动画效果,在appWidget中我们可以用的控件十分少

setIcon(R.drawable.droidman_closed);
try {
Thread.sleep(100);
while (0<--blinks) {
setIcon(R.drawable.droidman_open);
Thread.sleep(200);
setIcon(R.drawable.droidman_closed);
Thread.sleep(100);
}
} catch (InterruptedException ex) { }
setIcon(R.drawable.droidman_open);
}

public RemoteViews buildUpdate(Context context) {
RemoteViews updateViews = new RemoteViews(
context.getPackageName(), R.layout.widget); //映射布局,widget.xml文件的源码在下面可以找到

// 按下bubble的事件,对应action_next_tip动作
Intent bcast = new Intent(context, ProtipWidget.class);
bcast.setAction(ACTION_NEXT_TIP);
PendingIntent pending = PendingIntent.getBroadcast(
context, 0, bcast, PendingIntent.FLAG_UPDATE_CURRENT);
updateViews.setOnClickPendingIntent(R.id.tip_bubble, pending);

//这里为action_poke
bcast = new Intent(context, ProtipWidget.class);
bcast.setAction(ACTION_POKE);
bcast.putExtra(EXTRA_TIMES, 1);
pending = PendingIntent.getBroadcast(
context, 0, bcast, PendingIntent.FLAG_UPDATE_CURRENT);
updateViews.setOnClickPendingIntent(R.id.bugdroid, pending);

// Tip bubble text
if (mMessage >= 0) {
String[] parts = sNewlineRegex.split(mTips[mMessage], 2);
String title = parts[0];
String text = parts.length > 1 ? parts[1] : "";

// Look for a callout graphic referenced in the text
Matcher m = sDrawableRegex.matcher(text);
if (m.find()) {
String imageName = m.group(1);
int resId = context.getResources().getIdentifier(

imageName, null, context.getPackageName());
updateViews.setImageViewResource(R.id.tip_callout, resId);
updateViews.setViewVisibility(R.id.tip_callout, View.VISIBLE);
text = m.replaceFirst("");
} else {
updateViews.setImageViewResource(R.id.tip_callout, 0);
updateViews.setViewVisibility(R.id.tip_callout, View.GONE);
}

updateViews.setTextViewText(R.id.tip_message,
text);
updateViews.setTextViewText(R.id.tip_header,
title);
updateViews.setTextViewText(R.id.tip_footer,
context.getResources().getString(
R.string.pager_footer,
(1+mMessage), mTips.length));
updateViews.setViewVisibility(R.id.tip_bubble, View.VISIBLE);
} else {
updateViews.setViewVisibility(R.id.tip_bubble, View.INVISIBLE);
}

updateViews.setImageViewResource(R.id.bugdroid, mIconRes);

return updateViews;
}
}


有关AndroidManifest.xml中详细的recevier代码如下

<receiver android:name=".ProtipWidget" android:label="@string/widget_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="com.android.protips.NEXT_TIP" />
<action android:name="com.android.protips.HEE_HEE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_build" />
</receiver>

有关res/xml/widget_build.xml的代码如下

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="294dip"
android:minHeight="72dip"
android:updatePeriodMillis="0"
android:initialLayout="@layout/widget" />

有关res/layout/widget.xml的代码如下,注意下面使用了布局文件套嵌的include方式

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widget"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="5dip"
>

<include layout="@layout/droid" />
<include layout="@layout/bubble" />
</RelativeLayout>

有关res/layout/droid.xml的代码如下

<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/bugdroid"
android:src="@drawable/droidman_down_closed"
android:scaleType="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
/>

有关res/layout/bubble.xml的代码如下

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tip_bubble"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/bugdroid"
android:layout_centerVertical="true"
android:gravity="center_vertical|left"
android:layout_marginRight="2dip"
android:visibility="invisible"
android:background="@drawable/droid_widget"
android:focusable="true"
>
<TextView
android:layout_width="0dip"
android:layout_height="0dip"
android:layout_alignParentTop="true"
android:layout_marginTop="-100dip"
android:text="@string/widget_name"
/>
<TextView
android:layout_width="0dip"
android:layout_height="0dip"
android:layout_alignParentTop="true"
android:layout_marginTop="-90dip"
android:text="@string/tts_pause"
/>
<TextView
android:id="@+id/tip_footer"
style="@style/TipText.Footer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginRight="1dip"
/>
<ImageView
android:id="@+id/tip_callout"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:gravity="center"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_above="@id/tip_footer"
android:visibility="gone"
android:padding="4dip"
/>
<TextView
android:id="@+id/tip_header"
style="@style/TipText.Header"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toLeftOf="@id/tip_callout"
android:layout_alignWithParentIfMissing="true"
android:layout_marginTop="0dip"
android:layout_marginLeft="3dip"
/>
<TextView
android:id="@+id/tip_message"
style="@style/TipText.Message"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/tip_header"
android:layout_alignLeft="@id/tip_header"
android:layout_alignRight="@id/tip_header"
android:layout_marginTop="1dip"
/>
</RelativeLayout>

有关上面bubble.xml中的drawable对象droid_widget的代码如下

<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@drawable/droid_widget_pressed" />
<item android:state_focused="true" android:state_window_focused="true" android:drawable="@drawable/droid_widget_focused" />
<item android:state_focused="true" android:state_window_focused="false" android:drawable="@drawable/droid_widget_normal" />
<item android:drawable="@drawable/droid_widget_normal" />
</selector>

更多相关文章

  1. 在Android上实现WLAN的一点理解
  2. 在Android上实现WLAN的一点理解
  3. Android中的资源与国际化!
  4. Android高手进阶教程之----Android(安卓)中自定义属性(attr.xml,
  5. Android(安卓)ui基础——gravity 与 layout_gravity 的区别
  6. Android中获取网页表单中的数据
  7. android 混淆jar及apk的心得
  8. Android.os.NetworkOnMainThreadException
  9. AndroidStudio用gradle编译中文乱码

随机推荐

  1. 【Android游戏开发二十五】在Android上的
  2. 一周一本技术书(第四周)《Android谷歌官方T
  3. android设计的网页工具+灵感
  4. 2019秋招android总结
  5. 【android】如何有效的减少重复的代码
  6. android第一周总结——但愿这次可以发出
  7. Android几种Service常驻内存的小思路
  8. Android——UI篇:Android(安卓)图片选择器
  9. Android(安卓)studio 3.6 NDK开发 基本流
  10. Android(安卓)数据持久化(一)之简单数据持