糕飛.想點三十而立.努力發奮

2013年7月9日 星期二

關於寫 Android Widget 遇到的問題

+ 暫沒回應

為了寫「香港天晴」的 widget,費了不少時間,中間還遇到一些奇怪的情況,特此記下。



Widget 還是不要做為可自訂大小的好


自從 Android 3.0 以來,Widget 可以設定為自訂大小 (resizeable) ,可根據不同的大小顯示不同的資訊,如以下的天氣 widget:

圖片來自 Android Developers

這樣可以以一個 widget 便代替以上四個 widget,減少一個程式有太多不同大小的 widget的情況 。

可是呢,實際做下去會有點問題:
  • 你不會知道 widget 的大小
    因為只能靠 onAppWidgetOptionsChanged() 獲取 widget 的最小和最大大小,但卻不能知道那大小等於桌面的多少個 cells。而因為 android 有太多的 screen size,在手機上 4 個 cells 的大小,在 tablet 上是只是 3 個 cells 。要顯示得好的話要做大量的測試。
  • 不同 launcher 有不同的情況
    不同 launcher 對 resizeable widget 的操作也有所不同。有些如 android 所言,會執行 onAppWidgetOptionsCHanged() ,有些卻不會:如 Samsung Galaxy S3 和 Sony Xperia Z。在他們的預設 launcher 上 resize widget 也不會 call onAppWidgetOptionsChanged(),做成不同程度的顯示問題。
以上這些很難解決,也不值得費時間去解決。在寫了 resizeable widget 後,看到人說,resizeable widget 是以 GridLayout 、ScrollView 做的,而不是上面顯示的天氣 widget 的情況。那麼 Google 你拿這個天氣 widget 當例子是想做什麼啊?

勸大家暫時不是用 GridLayout 或 ScrollView 做 widget 的話,不要做可調較大小的 widget 吧。

RemoteView View ID 一定要存在

要更新 widget ,便要用 ViewID,在 RemoteViews 中更新:
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget); 
views.setTextViewText(R.id.textview, text_string);
在 ICS 以上,若 ID 在 layout 中沒有定義的話,是可以直接執行而不會有問題的。可是在 version 2.2 時,若 ID 不存在的話,widget 會直接顯示 Error view,而不會出現 Exception 的。這著實費了我一番功夫才找到問題所在。

Widget 中沒有自訂字型

在 app 中,可以用以下的方法使用其他 webfonts:
Typeface customTypeFace = Typeface.createFromAsset(getAssets(),
"fonts/custom_font.ttf");
((TextView)findViewById(R.id.custom_text_view)).setTypeface(customTypeFace);
可是在 Widget 中,由於只能使用 RemoteViews,所以不能執行 setTypeface。要使用 webfonts ,則只能將文字變成 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 coordinate。不過我的 fonts 是正方形的,若大家用的為非方形的話,可能又有所不同。

Android 2.x 支援

怎麼說呢?Android 2.x 跟 4.x 實在有點差異,很多時真不想再支援 2.x 版本。可是根據最新的調查,2.x的佔有率雖然首次被 4.x 超越,但也穩佔第二位,不支援它的話可是有大量用戶用不到的。

圖片來源 Engadget


以上是我遇的問題,希望對大家寫 Widget 有用吧。