2012年1月6日 星期五

eclipse找不到HTC Desire

使用HTC開發程式, 可是 Windows | View | Device 看不到我可愛的HTC Device, 該安裝的都安裝了, 手機也沒有問題, 因為手機接在另一台 Win7 的電腦上時, 可以正常debug 程式, 在網上爬,...過程就不說了, 結果是, 在安裝移除程式, 把HTC的三個驅動都移除, 然後重新安裝.....OK,搞定 (移除前後, 看安裝移除程式,一個樣,看不出有何不同)

 

2012年1月3日 星期二

proguard and drawable not found

加上proguard後, 有一些resource/drawable找不到了, 因為, 在程式中是使用字串名稱去反查圖檔, 經過了 proguard, 名稱換了, 就找不到了, 有趣的是, 在apk中, 檔案都在, 名稱也沒有換, 看起來, proguard有作一個對照檔。怎麼辦? 在 proguard.cfg 加上幾行
-keepclassmembers class **.R$* {
    public static int back;
    public static int log;
    public static int click;
}
搞定 !!
這也讓我思考,是否有必要保留以名稱來找圖檔。

proguard

使用混淆器,proguard
1.從R12之後, default.properties 改名為 project.properties
2.project.properties的檔頭上說:
檔案上說 "Do not modify this files",不要理他,加上一行
proguard.config = proguard.cfg
也可以加上路徑 ".\proguard.cfg" 或是絕對路徑。
3.要Export Package才起作用(專案|右鍵Android Tools)

4.用新版的proguard
   原先的錯誤是
   爬文,說proguard不支援路徑中帶有空白字元,要修改SDK location,
   又說,修改 android-sdk路徑下,proguard路徑下,proguard.bat,
   將 %* 改成 %1,%2,...,%9,...看到一篇說,改成新版的progrard就好,       
   試著下載新版(4.7),解壓縮,直接覆蓋到adroid-sdk下之proguard,
   搞定。
中間還有一個插曲,使用Export unsigned..產生的APK複製到我的HTC Desire手機,無法安裝,但是使用Export signed就好了,APK檔案,從756K 降至 568K, BinGo!

別忘了,將專案下,proguard路徑下的對照檔,與產生的APK放在一起,以備偵錯使用。

2011年12月8日 星期四

AIDL, couldn't find import for class sni.lib.Note.GestureOperation

這個問題與路徑有關, 在函式庫的配合時,
sni.lib.Note..........class GestureOperation
com.sni.Note.......package to host the Service
sni.lib.Page..........package as Client to link the Service

製作了一個AIDL在com.sni.Note,
package com.sni.Note;
import com.sni.Note.GestureOperation;
interface INoteService {
 GestureOperation[] getOperations();
 }
出現
couldn't find import for class sni.lib.Note.GestureOperation
的錯誤,

然後, 加上GestureOperation.aidl的AIDL檔:
parcelable GestureOperation;
原來的錯誤沒有了,
但是所產生(gen)的java檔報錯,
GestureOperation cannot be resolved to a type
並要求import,
如果用了import, 那就違反了檔頭所說的:
* This file is auto-generated.  DO NOT MODIFY.
這是因為 GestureOperation.aidl 和GestureOperation.java不在一起,
怎麼辦?

將GestureOperation.aidl移至sni.lib.Note, 讓他們生活在一起,
當然, import的路徑要改,
import com.sni.Note.GestureOperation
結果, 原來import的錯誤又回來了,
如果在GestureOperation前加上package, 如:
package sni.lib.Note;
parcelable GestureOperation;
結果出現新的錯誤:
interface GestureOperation should be declared in a file called sni\lib\Note\GestureOperation.aidl.

所以, 暫時看起來沒有解, 有網路文章說, 與路徑有關, 就是GestureOperation放到sni.lib.Note, 但是 aidl compiler找不到, 目前有一個解, 是可以用, 但是如果有多個class要同時使用時, 就要費一點腦子,
sni.lib.Note .............. 放 GestureOperation (java & aidl), INoteGesture.aidl
com.sni.Note ........... 定義Service, 這是因為所需的資料在這個類別裡,
sni.lib.Page .............. 執行 bindService
這樣子, 是可以正常執行的, 因為
INoteGesture ..... GestureOpeartion.aidl ....... GestureOperation.java
都在一起, 避開了路徑的問題

另外在Serivce, action的定義名稱是自取的, 可以與package名稱不相干.

在bindService時也出現錯誤, (回傳 false), 這是因為在GroupActivity底下的Activity是無法使用bindService, 在我的個案中, 使用
getApplicationContext().bindService 依然不行, 但是使用
getParent().bindService就可以了, 這個在網路上有比較完整的討論,
http://blog.tourizo.com/2009/04/binding-services-while-in-activitygroup.html
http://randomizedsort.blogspot.com/2010/11/service-binding-workaround-in-tab.html

還有一點必須注意的是, bindService傳回true, 不代表就可以使用service interface, 必須等onConnection完成. 這是容易理解.



2011年12月6日 星期二

getLaunchIntentForPackage return null

1.確定被呼叫的APP有安裝 (getInstalledPackage)
2.確定被呼叫的APP可以正常執行 (MAIN, LAUNCHER有設定)
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
可是....就是回傳null,

結果發現是:
    <data android:scheme="id" />
在作怪, 只要有這一行, 就回傳null, 換句話說, 若是有 data/scheme就不能用getLauncherForPackage,

所以, 就改用 setClass
     Intent intent = new Intent(Intent.ACTION_MAIN);
     intent.setClassName(packageName, activityName);
     intent.setData(Uri.parse("id://"+id));
     startActivity(intent);
搞定

attempt to write a readonly database

錯誤:
12-06 10:16:43.523: E/AndroidRuntime(286): java.lang.RuntimeException: Unable to start activity ComponentInfo{sni.Note.Comm/sni.Note.Comm.CommActivity}: android.database.sqlite.SQLiteException: attempt to write a readonly database: BEGIN EXCLUSIVE;

1.確定是以ReadWrite的方式開啟的,
2.不應該要設定檔案夾或是檔案的權限, 因為這是在手機, 使用者是可能不會設定, 而且, 以前也不用,

搞了很久, 真是要去撞牆, 原來是權限忘了設:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

2011年11月13日 星期日

AsyncTask & notifyDataChanged

adapter 的notifyDataChanged()必須在UI thread 中呼叫, 如果不是, 例如, 如果在adapter中, 自行呼叫 notifyDataChanged() 將會造成許多問題, 例如, 按鍵無法接受。在我的個案中,按鍵無法接受,不管是長按或是短按,但是,滑動光學滑鼠之後,就正常了,查網,的確有這個問題。
http://stackoverflow.com/questions/4566101/how-to-fix-notifydatasetchanged-listview-problems-in-dynamic-adapter-wrapper-and

解決的方法是使用AsyncTask, 例如:
 class AppendTask extends AsyncTask<Integer, Float, Exception> {
    @Override
    protected void onPreExecute() {
    ... run in UI thread
 
 
 return float-value; 
    }
    @Override
    protected Exception doInBackground(Integer... params) {
    ...
    ... run in WORKING thread
    ...publishProgress(a,b,c);
    }
 
 @Override
     protected void onProgressUpdate(Float progress) {
     ...run in UI thread
     }
    @Override
    protected void onPostExecute(Exception e) {
     ... run in UI thread
  }
  AsyncTask 的三個參數中,第一個參數(Integer), 是doInBackground()的輸入參數類別,是不定個數的參數。第二個參數(Float) 是onProgressUpdate的輸入參數類別,也是不定個數。第三個參數(如例中之Exception)則為doInBackground的傳回值類別,也是onPostExecute()的輸入類別。
四個函數,只有doInBackground在Working thread(沒有UI的thread)中執行,onPreExecute()第一個被執行,接著就執行doInBackground(), 這是不能省略的函數,在函數中可呼叫(也可以不呼叫)onProgressUpdate一次或多次。onPostExecute()則是在doInBackground結束後執行。

2011年11月8日 星期二

CharBuffer.toString() trap-Example: int32toHex

使用CharBuffer.toString()時, 要注意, 輸出的字串, 是從指標所在的位置開始寫的, 因此, 在輸出之前, 要加上: position(0), 如:
以下是一個範例, 將32位元有正負的整數, 傳成十六進位數字,
 /**
  * Convert 32 bits integer number to hex-decimal string
  * @param num number to convert
  * @return hex-decimal string
  */
public static String Int32ToHex(int num) {
  final int BUFFER_SIZE=8;
  final int BITS=32;
  String pattern="0123456789ABCDEF";
  CharBuffer digits=CharBuffer.allocate(BUFFER_SIZE);
  int digit=0;
  int ref=0x80000000;
  int bitValue=0x8;
  for(int i=0;i<BITS;++i) {
      int offset=i%4;
      if(offset==0 && i!=0) {
      digits.append(pattern.charAt(digit));
      digit=0;
      bitValue=0x8;
   }
   if((num & ref)!=0) digit += bitValue;
   bitValue >>= 1;
   /***
    * Notice that,

    *        0x80000000(-2147483648) >> 1 is NOT 0x40000000, but 0xC0000000(-1073741824)
    *        (-->E0000000(536870912)-->F0000000(268435456)-->F8000000(-134217728))
    *        Always divide by 2, no matter positive or negative number
    * and 0xFFFFFFFF(-1) >> 1 is NOT 0x7FFFFFFF, but still 0xFFFFFFFF
    * For the positive integer, the bit operation is running just as expectation.
    */
   if(ref==0x80000000) ref=0x40000000;
   else ref >>= 1;
  }
  digits.append(pattern.charAt(digit));
  digits.position(0);
  return digits.toString();
}

2011年11月3日 星期四

Spinner trap

發現spinner設定OnItemSelectedListener時, 會自動呼叫一次,
而且是選項0, 在網站上有找到類似的說法,
http://blog.csdn.net/androidbluetooth/article/details/6670153
結果解決的方法, 有點怪異,
原來是,
spinner.setSelection(itemID);
spinner.setOnItemSelectedListener(...);
結果在執行時, 會自動呼叫listener, 傳入的選項是0,
將這兩行交換,
先設定Listener, 再設定選項,
一切就OK了 !!

2011年11月2日 星期三

reference link

在Java 中都是用物件的連結,
例如:
Date a;
Date b=a;
則常a的值改變時,b也會跟著變,
此時, 用以下兩個指令:
(1) a=new Date(); 和
(2) a.setTime(new Date().getTime());
將會有不一樣的效果,
用前者, 則中斷了b 與 a 的連結, 原來, b 指向 a 的位址, 但是new 指令, 結束了 a 原來的位址, 而產生了一個新的位址(與本體), 因此, a, b 成為獨立的兩個變數.
進一步地探討, 如果:
a=b;
c=a;
那麼b-->a-->c 三個會連動, 但是如果,
a=b;
b=c;
則 a 將獨立, 而b,c 連動.

這一點在函數傳遞時也有影響,

a=null;
func(a); 在執行時, a 改變了, 外面的a 是不會變的, 但是如果
Date a;
func(a);
如果在函數中, 使用 a=b, 則連動就結束了,
但如果用a.setTime(...), 那函數結束後, a 的值也改了,
但如果用 b=a, 那a, b 就會連動,

有趣!!

2011年10月27日 星期四

自行設計的可視元件在Eclipse的layout中報錯

程式執行正常, 可是在Eclipse的layout中報錯,



無法產生元件, 查看 Winodws > Show View > Error Log 可以看到
android.content.res.Resources$NotFoundException: String array resource ID #0x7f050001
 at android.content.res.Resources.getStringArray(Resources.java:385)
 at com.sni.Task.DateIcon.init(DateIcon.java:72)
 at com.sni.Task.DateIcon.<init>(DateIcon.java:47)
 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
 at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
 at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
 at com.android.ide.eclipse.adt.internal.editors.layout.ProjectCallback.instantiateClass(ProjectCallback.java:387)
 at com.android.ide.eclipse.adt.internal.editors.layout.ProjectCallback.loadView(ProjectCallback.java:155)
 at android.view.BridgeInflater.loadCustomView(BridgeInflater.java:198)
 at android.view.BridgeInflater.createViewFromTag(BridgeInflater.java:126)
 at android.view.LayoutInflater.rInflate(LayoutInflater.java:618)
 at android.view.LayoutInflater.rInflate(LayoutInflater.java:621)
 at android.view.LayoutInflater.inflate(LayoutInflater.java:407)
 at android.view.LayoutInflater.inflate(LayoutInflater.java:296)
 at com.android.layoutlib.bridge.Bridge.computeLayout(Bridge.java:397)
 at com.android.ide.common.rendering.LayoutLibrary.createLegacySession(LayoutLibrary.java:492)
 at com.android.ide.common.rendering.LayoutLibrary.createSession(LayoutLibrary.java:337)
 at com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart.renderWithBridge(GraphicalEditorPart.java:1510)
 at com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart.renderWithBridge(GraphicalEditorPart.java:1347)
 at com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart.recomputeLayout(GraphicalEditorPart.java:1078)
 at com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart$ReloadListener.reloadLayoutSwt(GraphicalEditorPart.java:1716)
 at com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart$ReloadListener.access$0(GraphicalEditorPart.java:1654)
 at com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart$ReloadListener$1.run(GraphicalEditorPart.java:1648)
 at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:35)
 at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:135)
 at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:4140)
 at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3757)
 at org.eclipse.ui.internal.Workbench.runEventLoop(Workbench.java:2696)
 at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:2660)
 at org.eclipse.ui.internal.Workbench.access$4(Workbench.java:2494)
 at org.eclipse.ui.internal.Workbench$7.run(Workbench.java:674)
 at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332)
 at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:667)
 at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149)
 at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:123)
 at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196)
 at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:110)
 at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:79)
 at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:344)
 at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:179)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:622)
 at org.eclipse.equinox.launcher.Main.basicRun(Main.java:577)
 at org.eclipse.equinox.launcher.Main.run(Main.java:1410)
 at org.eclipse.equinox.launcher.Main.main(Main.java:1386)

看這一長串可怕的資料, 其實指到一行:
weekDays = this.getResources().getStringArray(R.array.week_short);
(resource ID #0x7f050001)當然, 資源是有的, 否則程式不能執行正常, 但是在Eclipse的Layout中讀不到陣列的資料, 因此就報錯了, 發現, 所有使用Context的資料都會報錯, 解決的方法就是用 this.isInEditMode()包起來, 如:
if (! this.isInEditMode()) {
     ....(使用到Context的Code, 或是, 全部包起來)
}
然後就在Eclipse和執行都正常了, 當然, 是在constructor中使用.

2011年10月26日 星期三

Dialog 中的 ListView 只顯示文字長度

在Dialog中, 使用ListView, 乖乖, 選項只有半截, 或者說, 只有文字的長度, 選擇時, 也只能選擇文字所在的區域, 選擇後面的空白就變成無效的選擇, 如下圖:
怎麼會這樣, 一直都是好好的呀, 只好一行一行去試, 結果發現, 是 FEATURE_NO_TITLE,
只要設定這個選項時, 就會有這個錯誤, 如下例:
Dialog dlg = new Dialog(this);
dlg.requestWindowFeature(Window.FEATURE_NO_TITLE);

ArrayList<HashMap<String,Object>> laHelp=new ArrayList<HashMap<String,Object>>();
HashMap<String,Object> map=new HashMap<String,Object>();
map.put(HelpDialog.ICON_KEY, R.drawable.task_cancelled);
map.put(HelpDialog.TEXT_KEY, "test");
laHelp.add(map);
ListView lvw=new ListView(this);
lvw.setAdapter(new SimpleAdapter(this,laHelp, R.layout.help_item,
            new String[] {HelpDialog.ICON_KEY, HelpDialog.TEXT_KEY}, new int[] {R.id.HelpItemImage, R.id.HelpItemText} ));
dlg.setContentView(lvw);
dlg.show();
關鍵就在紅字那一行, 如果刪除, 就正常了, 但是多了抬頭, 怎麼辦呢? 我的方向就是不用SimpleAdapter, 改寫自己的 Adapter, 就正常了.

2011年10月20日 星期四

Re-indent

如果你的原代碼所有的指令都在一行, 只有一行, 很長的一行, 好啦, 許多行也行, 反正就是格式亂七八糟, 如何在Eclipse中排整齊,
使用 Ctrl-Shift-F, 如果有 "//" 就麻煩一點, 必須先將 // 刪除, 或是改成 /* ... */, Ctrl-Shift-F 的選項在,
Source / Format

http://stackoverflow.com/questions/1207264/how-do-i-reindent-all-my-codes-in-eclipse

2011年10月17日 星期一

query like

使用Android 的query指令時,
使用, "Title like ?%", new String[] { string }
或是, "Title like '?%'", new String[] { string }
都是錯誤的, 前者在全部符合時才傳回, 後者根本找不到,
或是改成, "Title like ?", new String { string+"%" }
就可以了,
異哉 !! (應該是 % 的處理, 但是改成 \% 則文法錯誤)

2011年10月13日 星期四

SQLite 的效能

要寫入近二百條的資料到數據庫, 很慢, 受不了, 後來查網路文章, 竟然後發, 將指令包在transaction之內, 就變得很快了, 差有百倍以上,
真是好.

2011年9月21日 星期三

Button Background

按鍵如果設定背景色, 將會從按鍵形狀, 變成一個方型,
如果要透過程式變會原來的按鍵形狀, 並沒有清除背景的設定,
可以用
<code>
  Button btn=new Button(this);
...
  btn.setBackgroundResource(android.R.drawable.btn_default);
</code>

Eclipse start but return exit code=1

環境: Win 7, 64 bits, eclipse 1.6.26 64 bits
錯誤:
在 eclipse.ini 中加入這一行, 就搞定了,
-XX:-UseCompressedOops
其中 -UseCompressed Oops中的減號, 代表 "不要用",
因此, 讓是元件(Oops)壓縮(Compress)程解壓縮的錯誤,

找到一些說明:

-XX command-line options


JVM command-line options that are specified with -XX are not stable and are not recommended for casual use.
These options are subject to change without notice.
-XXallowvmshutdown:[false|true]
This option is provided as a workaround for customer applications that cannot shut down cleanly, as described in APAR IZ59734. Customers who need this workaround should use -XXallowvmshutdown:false. The default option is -XXallowvmshutdown:true forJava 6 SR5 onwards.
-XX:MaxDirectMemorySize=<size>
Sets the maximum size for an nio direct buffer. By default, the maximum size is 64 MB.
-XX:-StackTraceInThrowable
This option removes stack traces from exceptions. By default, stack traces are available in exceptions. Including a stack trace in exceptions requires walking the stack and that can affect performance. Removing stack traces from exceptions can improve performance but can also make problems harder to debug.
When this option is enabled, Throwable.getStackTrace() returns an empty array and the stack trace is displayed when an uncaught exception occurs. Thread.getStackTrace() and Thread.getAllStackTraces() are not affected by this option.
-XX:+UseCompressedOops
(64-bit only) This option enables compressed references in 64-bit JVMs and is provided to help when porting applications from the Sun JVM to the IBM® JVM. This option might not be supported in subsequent releases. The -XX:+UseCompressedOops option is similar to specifying -Xcompressedrefs, which is detailed in the topic JVM command-line options .
-XX:-UseCompressedOops
(64-bit only) This option prevents the use of compressed references in 64-bit JVMs. The option is provided to help when porting applications from the Sun JVM to the IBM JVM, and might not be supported in subsequent releases.
Reference:
http://stackoverflow.com/questions/5054035/eclipse-cdt-crashes-java
http://publib.boulder.ibm.com/infocenter/javasdk/v6r0/index.jsp?topic=%2Fcom.ibm.java.doc.user.aix64.60%2Fdiag%2Fappendixes%2Fcmdline%2Fcommands_jvm_xx.html

*******
其實這個問題並沒有解決, 偶爾還是會, 後來配置又作了一些修改:
-startup
plugins/org.eclipse.equinox.launcher_1.2.0.v20110502.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_64_1.1.100.v20110502
-vm
C:/Program Files/Java/jdk1.6.0_26/bin/javaw.exe
-product
org.eclipse.epp.package.java.product
--launcher.defaultAction
openFile
--launcher.XXMaxPermSize
256M
-showsplash
org.eclipse.platform
--launcher.XXMaxPermSize
512m
--launcher.defaultAction
openFile
-vmargs
-server
-Xss2m
-XX:+DoEscapeAnalysis
-XX:+UseConcMarkSweepGC
-Dosgi.requiredJavaVersion=1.5
-Xms200m
-Xmx1024m
-XX:-UseCompressedOops
-Xint

2011年8月29日 星期一

無法上傳程式至安卓市集

從星期日開始,就無法上傳程式到安卓市集,出現以下的錯誤。
想,可能是谷歌服務器的關係,就不理會,等一陣子再說吧! 等了一天,還是如此,想,會不會是週末放假了! 或是我們白天,美國晚上的關係,又等了一天,也上網查一下,是否有這方面的訊息或報導。結果,並沒有發現,到了今天早上,想,不該如此的,就換了一部電腦,結果....上傳正常。那就是電腦的問題了!!接著想,那換一個瀏覽器看看,換成火狐,卻出現另外一個問題。無法報到,出現如下訊息:
上面說,是Cookie的問題,或許是,就到IE去,把Cookie給刪了...結果仍然相同,看火狐上面的解釋,都是舊版本的說明,是否是IE9.0的原因呢???但是上傳圖片是好的,是權限的問題嗎?
先治標吧,到火狐,上傳程式,就正常了。




2011年8月9日 星期二

invalidate 部分畫面

寫了一個程式, 自己設計了一個view, MyView 繼承至View
在畫圖(onDraw())時, 座標的計算是相對座標, 例如, (0,0) 對應的是畫布的左上角, 但是在整個螢幕上, 並不是左上角。
在整個螢幕上,有二行,一行是系統的狀態行,一行是標題行。
找Google, 很容易可以找到指令。

Rect rectgle= new Rect();
Window window= getWindow();
window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
int StatusBarHeight= rectgle.top;
int contentViewTop=
window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
int TitleBarHeight= contentViewTop - StatusBarHeight;

重點是:這些指令必須在view所在的thread中執行, 因此,如果你想要在Activity中執行,必須加上:

MyView.post(new Runnable() {
@Override
public void run() {
/* 將執行程式放在此處 */
}
});

另外一個方式是,直接放在View的onDraw()函數。
看起來好像可以用了, 但是,
Invalidate()會調用onDraw(), 沒有錯,
但限定範圍的Invalidate(範圍), 產生的結果卻很怪異,
通常前幾次 (幾乎都是三次,也出現過部分是一次),會畫整個畫面,如同invalidate() (没有參數),造成整個畫布的重劃,這些次數過後,就正常了,只重劃指定的部分。改成postInvalidate()也是相同,只是怪。
指定的操作是:
  1. 設定繪畫方法為call back
  2. 執行invalidate調用onDraw
  3. onDraw呼叫設定的繪畫方法

/**
* Show item is checked
* @param canvas
* @param date
* @param id
*/
public void ShowChecked(Canvas canvas, int date, int id) {
XY xy=this.getXY(date, id);
if(checked==null) {
checked = BitmapFactory.decodeResource(getResources(),R.drawable.yes);
checked = Bitmap.createScaledBitmap(checked,(int)(unit_width*0.8),(int)(unit_height*0.8), true);
}
canvas.drawBitmap(checked, xy.x+(unit_width*0.1f), xy.y, new Paint());
}
/**
* Call the ShowChecked from outside
* @param date
* @param id
*/
public void ShowChecked(final int date, final int id) {
action.add(new Action() {
@Override
public void GoDraw(Canvas canvas) {
ShowChecked(canvas,date,id);
}
});
this.invalidate(region(date,id));
}