在Launcher中增加一个widget,需要以下几个步骤:
-
修改AndroidManifest.xml,在其中引用widgetexample_info.xml
<receiver android:name=".widget.WidgetExample" > <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/widgetexample_info" /> </receiver>
-
将源文件放入src目录下合适的位置
-
widgetexample_info.xml 放入res/xml, 其中引用layout文件
-
layout文件 放入res/layout
在开发的过程中,遇到如下几个问题:
Widget 中不能自订字体
我在网上查了刚好有人碰到同样的问题,并且提出了一个很好的解决方案(http://www.hksilicon.com/kb/cn/articles/486492/Android-Widget)。这里引用一下:
在 Activity
中,可以用以下的方法使用其他字型:
Typeface customTypeFace = Typeface.createFromAsset(getAssets(),"fonts/custom_font.ttf");
((TextView)findViewById(R.id.custom_text_view)).setTypeface(customTypeFace);
可是在 Widget
中,由于只能使用 RemoteViews
,没有 setTypeface
。要使用其他非内置的字型时,只能将文字变成 Bitmap
,然后在 ImageView
显示。
问题来了,怎样知道文字变成图片后的长度和阔度呢?网上有不同的方法,但都不能因应 font size 而自动调整。几经研究后,才找到以下的方法:
Paint textPaint = new Paint();
textPaint.setTypeface(typeface);
textPaint.setStyle(Paint.Style.FILL);
textPaint.setTextAlign(Align.LEFT);
Rect bound = new Rect();
textPaint.getTextBounds(text, 0, text.length(), bound);
int imageViewSize = Math.max(Math.abs(bound.top), bound.width());
Bitmap myBitmap = Bitmap.createBitmap(imageViewSize, imageViewSize, Bitmap.Config.ARGB_8888);
Canvas myCanvas = new Canvas(myBitmap);
myCanvas.drawText(text, -bound.left + (imageViewSize - bound.width())/2, -bound.top + (imageViewSize -bound.height())/2, textPaint);
重点是 imageViewSize
的设定和最后 drawText
的 xy 座标。不过以上的字型是正方形的字体,若大家用的为长方形的话,可能又有所不同。
Widget的更新周期
不要倚赖
updatePeriodMillis
作更新,因为它的最短的更新时间为 30 分钟,就算你设定为一分钟也没有用。想要更短的更新时间的话只能用 AlarmManager,或者构建service用来更新数据。我这里需要更新桌面时钟,所以直接监听了时钟相关的intent。
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_TIME_TICK);
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
context.getApplicationContext().registerReceiver(mReceiver, filter);
Widget的context和Application的context
上面注册监听intent,一开始我直接用的
context.registerReceiver(mReceiver, filter);
一运行就报错:
android.content.ReceiverCallNotAllowedException: IntentReceiver components are not allowed to register to receive intents
查阅资料发现,将这里不能直接用context,而应该使用
context.getApplicationContext()
由此看来widget的context和application的context,生命周期是不同的。