Android -- AppWidget 高级篇
3.1 AppWidget到底支持哪些view
在Android 2.2 SDK中我们首次启动模拟器可以看到和以前不一样的是多出了一个绿色的小机器人提示信息,Google给我们了演示了Android中如何通过 RemoteView和简单的图片轮换方式实现动画效果在桌面小工具中,appWidget的基类时AppWidgetProvider类,不过 Widget本身的生命周期管理并非Activity,相对于的而是BroadcastReceiver广播方式处理的。一直想知道如何在AppWidget里面添加 ListView,EditText 这些复杂的View.我们知道要在AppWidget里添加 View都是通过RemoteView来做到了,然而RemoteView本身功能很弱,支持的操作很少,而且支持RemoteView的Widget很 少:
A RemoteViews object (and, consequently, an App Widget) can support the following layout classes:
FrameLayout
LinearLayout
RelativeLayout
And the following widget classes:
AnalogClock
Button
Chronometer
ImageButton
ImageView
ProgressBar
TextView
Descendants of these classes are not supported.
从这里可以知道,为什么在AppWidget里添加 EditText会显示LoadError了,因为本身它就不支持这些复杂的 Widget.
但我们又会有疑问了, 为什么Google Search会有EditText呢?其实这些都是假象,并不是AppWidget支持EditText。细心的你应该会发现, AnalogClock也不是如Button,TextView的简单Widget ,其实 AnalogClock也是Google自定义的RemoteViews。
在网上可以看到,AppWidget很多特效,它确实支持了复杂Widget,比如:ListView/GridView,EditText. 这些确实是我们可以看到的,但它是怎么做到的呢?我也很想知道,AppWidget支持到那么强大,甚至超过了本身AP的功能,很抢眼。但不管是怎么实现 的,我想人家肯定是花了大力气去做到了,我猜想可能是将Google 提供的AppWidget进行了比较大的改动。我们查看一下framework下的appwidget:
:ls frameworks/base/core/java/android/appwidget/ -lh
total 60K
-rw-r--r-- 1 pjq users 7.9K 2009-09-29 21:49 AppWidgetHost.java
-rw-r--r-- 1 pjq users 12K 2009-09-29 21:49 AppWidgetHostView.java
-rw-r--r-- 1 pjq users 14K 2009-09-29 21:49 AppWidgetManager.java
-rw-r--r-- 1 pjq users 691 2009-09-29 21:49 AppWidgetProviderInfo.aidl
-rw-r--r-- 1 pjq users 5.6K 2009-09-29 21:49 AppWidgetProviderInfo.java
-rwxr-xr-x 1 pjq users 6.3K 2009-09-29 21:49 AppWidgetProvider.java
-rw-r--r-- 1 pjq users 1.5K 2009-09-29 21:49 package.html
可以看 到,appwidget的文件很少,虽然不能说明什么,但按照正常的推理,文件少功能一般也强大不到哪里去,这种想法虽然有些牵强,但暂且就这样认为吧。
3.2 如何自定义RemoteViews
要知道RemoteView的功能很少,特别是对事件处理的能力,都需要通过PendingIntent,传到BroadcastReceiver去处理。所以这里对一些事件处理也仅限于比较简单事 件:比如说:Button Clicked,其它的我好像还没怎么用过,对复杂的View:比如!ListView(当然这里还不支持,打个比方),!ListView里面那么多Item,要设置Listener,要传值,这些 RemoteView都不能像一个单纯的Activity那样处理,如果要实现,则需要更加复杂的手段,通过广播实现。
由于日历小部件需要实现onClick事件,显示日历,动画效果等复杂的操作和效果,AppWidget支持的操作远远不能满足,这就需要修改framework里的代码,目前我已经在AppWidget里显示CalendarView(日历)、Viewflipper复杂的Widget,同时实现了如何让这些自定义的RemoteViews与AppWidget进行交互。现在让我详细介绍如何在AppWidget里自定义CalendarView(当然是继承自view咯 ^_^)和Viewflipper(只是在原基础上做了修改)这些复杂的 Widget.
我们知道AppWidget只支持RemoteView,哪些Widget是RemoteView 呢,我来教你搜一下:
[python]
frameworks/base/core/java/android/widget $ grep -i -n -A 1 @remoteview *.java
AbsoluteLayout.java:40:@RemoteView
AbsoluteLayout.java-41-public class AbsoluteLayout extends ViewGroup {
--
AnalogClock.java:39:@RemoteView
AnalogClock.java-40-public class AnalogClock extends View {
--
Button.java:58:@RemoteView
Button.java-59-public class Button extends TextView {
--
Chronometer.java:45:@RemoteView
Chronometer.java-46-public class Chronometer extends TextView {
--
FrameLayout.java:47:@RemoteView
FrameLayout.java-48-public class FrameLayout extends ViewGroup {
--
ImageButton.java:66:@RemoteView
ImageButton.java-67-public class ImageButton extends ImageView {
--
ImageView.java:55:@RemoteView
ImageView.java-56-public class ImageView extends View {
--
LinearLayout.java:44:@RemoteView
LinearLayout.java-45-public class LinearLayout extends ViewGroup {
--
ProgressBar.java:122:@RemoteView
ProgressBar.java-123-public class ProgressBar extends View {
--
RelativeLayout.java:66:@RemoteView
RelativeLayout.java-67-public class RelativeLayout extends ViewGroup {
--
TextView.java:186:@RemoteView
TextView.java-187-public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
就是这些了,类名前面加了"@RemoteView",和我前面列出的那些是不是一样的呢?所以,如果你需要自定义一个(或者是一个已定义的复杂View如listview)作为RemoteViews使用,你就必须在其类名前加"@RemoteView"标识。
关于如何自定义一个Widget你完全可以参照frameworks/base/core/java/android/widget已有的这些Widget,照样写一个。
其实如果你需要自定义一个Widget,比如说支持ListView,你可以先在一个activity里实现它,然后将它移到framework下面去。
这 里说一下可能需要注意的地方:
1.如果有多个文件,需要Package的时候,名字最好按照这样的形 式:android.widget.CalendarView
其中CalendarView就是你要添加一个Widget存放的地方,这样的话你就可以在 frameworks/base/core/java/android/widget 目录下新增CalendarView文件夹,将java文件放在这个目录下。
如果你新增的Widget只有一个java文件就可以不用这样了,可以 完全按照已经存在的Widget的样子,直接将java文件放到frameworks/base/core/java/android/widget目录 下。
2.资源文件存放:
frameworks/base/core/res/res
资 源文件都放到这个目录下。
3.资源的引用:
要用这样的方式引用:com.android.internal.R.drawable.
记着在这个Customer Widget类名前加上"@RemoteView"标记.
这 些都做完了,你就已经将一个自定义的Widget添加到framework了。之后要做的工作就是编译整个工程了(在这里教一个比较懒的方法,直接编译frameworks 就OK,命令:
:mmm frameworks/base/
:adb push out/target/product/msm7627_ffa/system/framework/
framework.jar /system/framework/)。
最后你就可以在AppWidget引用你自定义的这个Widget了:
com.widget.CalendarView。
3.3 AppWidget访问RemoteViews的方法
至 此,你已经用上了你自定义的这个Widget,并且可以加到AppWidget。如果你想在自定义的RemoteViews上显示像日历的内容,你只需要在自定义的RemoteViews的onDraw()方法里实现就OK。但是并不是所有内容都是在RemoteViews预先设置好的,很多内容是由用户自己设置的。如,在AppWdiget显示”HelloWord”,我们是这样实现的
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider);
views.setTextViewText(R.id.appwidget_text, "HelloWord");
RemoteViews为外部提供的方法非常有限,查看API文档:
Public Methods View apply(Context context, ViewGroup parent)
Inflates the view hierarchy represented by this object and applies all of the actions.
int describeContents()
Describe the kinds of special objects contained in this Parcelable's marshalled representation.
int getLayoutId() String getPackage() boolean onLoadClass(Class clazz)
Hook to allow clients of the LayoutInflater to restrict the set of Views that are allowed to be inflated.
void reapply(Context context, View v)
Applies all of the actions to the provided view.
void setBitmap(int viewId, String methodName, Bitmap value)
Call a method taking one Bitmap on a view in the layout for this RemoteViews.
void setBoolean(int viewId, String methodName, boolean value)
Call a method taking one boolean on a view in
补充:移动开发 , Android ,