2012年5月20日 星期日

[AndroidDev] Managing the Activity Lifecycle

  • 如果 AndroidManifest.xml 中沒有任何 activity 有宣告 MAIN action 與 LAUNCHER category,此 app 的 launch icon 則不會出現在 app list 中。
  • onPause <-> onResume
  • onStop -> onRestart -> onStart -> onResume

onCreate()
  • 適合處理在整個 activity life 中只應處理一次的邏輯,ex: 初始化 class-scope variables。

onRestart()
  • 當 activity 是從 onStop() 被啟動時,系統才會呼叫此 method。所以若是有在 onStop() 中釋放資源,記得在此初始或設定。

onStart()
  • activity 已為可見狀態,但會很快的便進入 onResume()。

onResume()
  • 會停留到 resume 完 activity 到暫停前的狀態。

onPause()
  • activity 仍為半可視狀態,ex: 被 dialog 覆蓋。
  • 可表示 user 離開此 activity 而進入 onStop(),所以可執行:
    • Stop animations or other ongoing actions that could consume CPU.
    • Commit unsaved changes, but only if users expect such changes to be permanently saved when they leave (such as a draft email).
    • Release system resources.
      • broadcast receivers, handles to sensors (like GPS).
      • Any resources that may affect battery life while your activity is paused and the user does not need them.
  • 不需要在此儲存 user 所輸入的資料,因為其實此 Activity instance 還是被儲存在 memory 中的,所以當在 onResume(),元件的狀態會再被重新載入,除非這是項明確功能:
    • ex: email 會自動儲存為草槁。
  • 不適合在此中處理會耗費 CPU 的工作,ex: 寫入 DB,因為會拖慢顯現下一個 activity 的速度,所以這類型的工作更適合在 onStop() 中處理。
    • 要到 activity B 前,activity A 需先進入 onPause()。

onStop()
  • 保證 UI 不是可視的,user 已在使用另一個 activity 或 app。
  • 適合做耗費 CPU 的工作:
    • ex: 寫入 DB。
  • 不需要在此儲存 user 所輸入的資料,因為其實此 Activity instance 還是被儲存在 memory 中的,所以當 onResume(),元件的狀態會再被重新載入。
    • ex: If the user entered text into an EditText widget, that content is retained so you don't need to save and restore it.
    • 即使 activity 被 destroy,其中的 View objects 仍會被儲存在 Bundle 中,並且在 user 回到此 activity(the same instance of the activity) 時被重新載入。
  • 以下情況使得 activity A 會被 stopped 與 restarted:
    • user 由 Recent Apps window 開啟另一個 app,activity A 會進入 onStop(),若 user 再由 Home launcher icon or Recent Apps window 開啟你的 app,那麼 activity A 會進入 onRestart()。
    • 在你的 app 中進入下一個 activity,activity A 會進入 onStop(),若 user 按下 Back 鍵,activity A 會進入 onRestart()。
    • user 在使用 app 時有來電。

onDestroy()
  • The system calls onDestroy() after it has already called onPause() and onStop() in all situations except one:
    • when you call finish() from within the onCreate() method.
    • onCreate() 會直接進入 onDestroy()。

以下情況可能會使得你的 activity 被 destroyed:
  • The user presses the Back button.
  • Calling finish().
  • Activity is in onStop() 且已長時間沒有被使用而被系統回收。
  • Foreground activity 需要更多資源,因此關閉 background processes to recover memory。

若 activity 是被系統回收,那麼系統會記下它的狀態(is called the "instance state", stored in a Bundle object.),以在 user 想開啟此 activity 時能 restore 該狀態。

onSaveInstanceState()
  • 當系統要 destroy this activity,會呼叫此 method,並且傳入 Bundle object 來儲存 activity 資訊,所以你若有需要保存的資訊也可儲存在此 Bundle 中。
  • 然後此 activity 會被系統 recreate 並且傳入與 destroy 時相同的 Bundle object,因此可以在 onCreate() 中從 Bundle 中取出之前所儲存的資訊。
    • 但因為 activity 可以是完全新建立或是重新被建立的,也就是說傳入的 Bundle 不一定都是有值的,若是要使用,務必判斷是不是 null。
    • 或者與其在 onCreate() 中 restore 資訊,也可以選擇在 onRestoreInstanceState() 做。
      • 此 method 會在 onStart() 後被呼叫。
      • 只有在 Bundle != null 時會被呼叫。
  • The default implementation of this method saves information about the state of the activity's view hierarchy:
    • ex: he text in an EditTextwidget or the scroll position of a ListView. 
    • 所以在此 method 和 onRestoreInstanceState() 中記得呼叫 super.xxxx; 以儲存與重載 the state of the view hierarchy。


* Reference
- Starting an Activity | Android Developers
- Pausing and Resuming an Activity | Android Developers
- Stopping and Restarting an Activity | Android Developers
- Recreating an Activity | Android Developers

[AndroidLayout] Input Method Editors

若沒有做任何設定與調整,在輸入時所跳出的鍵盤(IMEs, Input Method Editors)很有可能會擋住正在輸入文字的元件,可以用以下方式處理:
  • 畫面中使用 scrollView,讓使用者可以自己適當的調整畫面位置。
  • 在該 activity 設 android:windowSoftInputMode ="adjustPan|adjustResize",讓系統幫忙處理。


android:windowSoftInputMode ="adjustPan"
  • Can prevent your background from resizing.
  • 不會呼叫 View.onSizeChanged()。
  • 不會調整 layout 所以不能保證能看到整個 layout 但會保持必能看到輸入框(平移)。
  • 設為 fullscreen 有時會使此參數失效,所以若是用 webview fullscreen 得注意。

android:windowSoftInputMode ="adjustResize"
  • 把原本的 layout(include background!) 往上頂,移出空間給 IME,但僅發生在 application 有可調整的空間。
  • 沒加此的話,不會一開啟就 focus on editText(?);加的話,一開啟就會 focus on 且有 soft input(?。
  • 會呼叫 View.onSizeChanged()。
  • 設為 fullscreen 有時會使此參數失效,所以若是用 webview fullscreen 得注意。


* Reference
- android:windowSoftInputMode
- Android Development Tips: Resizing layout while on-screen keyboard is displayed
- Issue 5497: adjustResize windowSoftInputMode breaks when activity is fullscreen
- Android windowSoftInputMode – Resize the application for the soft-keyboard
- Android: Soft Keyboard resizes background image
- android软键盘弹出引起的各种不适终极解决方案
- Android软键盘的隐藏显示研究 ***
- 关于android的输入法弹出来 覆盖输入框的问题
- Issue 14596: adjustPan not working correctly in landscape mode

2012年5月13日 星期日

[Android] The way to parser xml - SAX

Parser XML 可以用以下 API 達成:
  • SAX
  • Digester


SAX 
  • Simple API for XML。
  • 實作方式:
    • extends DefaultHandler
    • new SAXParserFactory
      • SAXParserFactory sAXParserFactory  =  SAXParserFactory.newInstance();
    • new SAXParser:
      • SAXParser sAXParser = sAXParserFactory.newSAXParser();
    • 從 SAXParser 中得到一個 XMLReader 的實例:
      • XMLReader xMLReader = sAXParser.getXMLReader();
    • Set Handler 到 XMLReader
      • xMLReader.setContentHandler(rssParser);
    • 藉 InputStream 取得 XML,用 handler 進行 parser。
      • xMLReader.parse(is);


DefaultHandler 執行方式:
  • startDocument()
    • 適合建立儲存物的集合 (ex: list)。
  • startElement(String namespaceURI, String localName, String qName, Attributes atts)
    • 解析過程中,遇到 XML 中的 tag 時,此 method 會被呼叫,可以由 localName 得知解析到那個 element 了。
    • 可以適當的建立相對物件。
    • attributes.getValue(NAME)
  • characters(char ch[], int start, int length)
    • 在執行 startElement() 後,此 method 會被呼叫,参数 ch[] 就是 element 中所帶的内容。
  • endElement(String namespaceURI, String localName, String qName)
  • endDocument()


* Reference
- android解析xml文件的方式(其二) - 东子哥 - 博客园
- 使用SAXParser取得XML文件里的属性值 - XML - AJava
- Android RSS解析步骤 | 易网联信
- SAX之:SAXParserFactory与SAXParser - 小文字 - 博客园

2012年5月6日 星期日

[Spring] configurations


<!-- Activates various annotations to be detected in bean classes -->
<!-- 設定註釋注册到Spring容器 -->
<context:annotation-config />

<!-- Scans the classpath for annotated components that will be auto-registered as Spring beans.
 For example @Controller and @Service. Make sure to set the correct base-package-->
<!-- 在 base-package下尋找有 @Component 和 @Configuration 的 target Class予註冊為 bean -->
<context:component-scan base-package="org.krams.tutorial" />

<!-- Configures the annotation-driven Spring MVC Controller programming model.
Note that, with Spring 3.0, this tag works in Servlet MVC only!  -->
<mvc:annotation-driven />


* Reference
- krams::: Spring 3 MVC: Using @ModelAttribute in Your JSPs

2012年5月5日 星期六

[AndroidLayout] declare-styleable and TypedArray

自定 View,且定訂屬性可以使用在 layout xml 中並取得其值。
  • Define attributes in <declare-styleable>。
  • extends View,畫出自己的圖,也可取得 xml 中的設定值。
  • 可在 layout xml 中引用自訂的 View。


Define attributes in <declare-styleable>。
  
      
          
          
      
  

extends View,畫出自己的圖,也可取得 xml 中的設定值。
public class CustomView extends View {
    private Paint paint;

    public CustomView(Context context,AttributeSet attrs) {  
        super(context,attrs);  
        paint = new Paint();  
          
        // 取出自定的 attributes from MyView.
        TypedArray a = context.obtainStyledAttributes(attrs,  
                R.styleable.MyView);  

        // 個別取出 attribute,要給預設值,以在 xml 中沒有定義時使用。  
        int textColor = a.getColor(R.styleable.MyView_textColor,  
                Color.WHITE);  
        float textSize = a.getDimension(R.styleable.MyView_textSize, 36);  
          
        paint.setTextSize(textSize);  
        paint.setColor(textColor);  
        
        // Give back a previously retrieved StyledAttributes, for later re-use.
        a.recycle();  
    }  
}

可在 layout xml 中引用自訂的 View。
  
  

* Reference
- Android 中自定义控件和属性(attr.xml,declare-styleable,TypedArray)的方法和使用 - jincf2011的专栏 - 博客频道 - CSDN.NET
- Declaring a custom android UI element using XML
- public void recycle ()

2012年5月2日 星期三

[Publish] Filters on Google Play

  • Google Play 會 filter app 是否相容於 device 來決定是否顯示於 user,filter 主要條件是:
    • 以宣告在 AndroidManifest.xml 中的 設定來和 device's configuration 做比對。
    • User's country and carrier, the presence or absence of a SIM card, and other factors. 
    • Filter 是以版本為根本,假設 user 已安裝 v1,但 v2 不適用於他的 device 那麼 user 便不會收到此 app 的更新通知。

Based
  • <supports-screens>
    • Smaller layout can be used on Larger screens; but Larger screen cannot be used on Smaller screens.
    • ex: Only declares: normalScreen
      • Available to both normal- and large-screen devices.
      • Not available to small-screen devices.
  • <uses-configuration>
    • Device Configuration: keyboard, navigation, touch screen
  • <uses-feature>
    • This functionality was introduced in Android 2.0 (API Level 5).
    • Device Features from name, ex:
      • <uses-feature android:name="android.hardware.sensor.light" />
    • OpenGL-ES Version(openGlEsVersion), ex:
      • <uses-feature android:openGlEsVersion="int">
    • 即使有些 device features 開發者並沒有宣告在此,Google Play 也會藉由<uses-permission>推出些 app 中應該會用到的 features 來做為 filter 條件。
  • <uses-library>
    • Device 中應該要有的 shared library。
  • <uses-permission>
    • 事實上並沒有直接使用宣告於此的做為 filter 條件,但會從此推論出應該使用到的 device feaures。
  • <uses-sdk> 
    • Minimum Framework Version (minSdkVersion) 
      • <uses-sdk android:minSdkVersion="3">
    • Maximum Framework Version (maxSdkVersion)
      • <uses-sdk android:maxSdkVersion ="10"> 
      • Deprecated. Android 2.1 以上(含)已不會再檢查此 attribute
      • The SDK will not compile ifmaxSdkVersion is set in an app's manifest. For devices already compiled with maxSdkVersion, Google Play will respect it and use it for filtering.
      • 不建議宣告此 attribute。


Advanced manifest filters
以下 attributes 通常使用於 high-performance games and similar applications that require strict controls on application distribution. Most applications should never use these filters.
  • <compatible-screens>
    • Google Play filters the application if the device screen size and density does not match any of the screen configurations (declared by a <screen> element) in the <compatible-screens>element.
    • Normally, you should not use this manifest element. Using this element can dramatically reduce the potential user base for your application, by excluding all combinations of screen size and density that you have not listed. 
  • <supports-gl-texture>


Other Filters
  • Application and publishing characteristics that affect filtering on Google Play.
  • Publishing Status
    • Only published applications will appear in searches and browsing within Google Play.
  • Priced Status
    • Not all users can see paid apps. 
    • To show paid apps, a device must have a SIM card and be running Android 1.1 or later, and it must be in a country (as determined by SIM carrier) in which paid apps are available.
  • Country / Carrier Targeting
  • Native Platform
  • Copy-Protected Applications

How Google Play filters by features

Google Play compares:
  • Features required by the application
    • An application declares features in <uses-feature> elements in its manifest 
  • Features available on the device, in hardware or software
    • A device reports the features it supports as read-only system properties.
    • 當 user 開啟 Google Play 會呼叫 PackagManager.getSystemAvailableFeatures() 取得 device features. 


How Google Play handles app
每次 developer 上傳 app 時,會列出 AndroidManfest.xml 中會用來做為條件的內容。
這些所得的 list 會做為 apk 的 metadata 與 the application .apk and the application version 儲存一起.
  • Filtering based on explicitly declared features
    • <uses-feature> 中  anandroid:required=["true" | "false"] attribute (API level 5 or higher),預設為 true,表示一定要符合。
    • 所以若不想因為此 feature 而被 filter 掉,可宣告此 feature 並將此參數設為 false。 
  • Filtering based on implicit features
    • 有可能因為下列因素 app 中有使用到某些 features 但並未被宣告
    • Compiled Android Library
      • Android 1.5  前並未有 <uses-feature> element.
      • The developer 誤以為所有裝置都會有此 feature 因此沒有宣告。
      • The developer 忘了.
      • The developer declared the feature explicitly, but the declaration was not valid. 
        • For example, 拼錯 feature 名稱。
    • 所以..
      • Google Play 會利用 <uses-permission> 去推論會使用到的 features 並自行做為 filter 條件。
      • 但如果不想要這樣則必須明確宣告在 <uses-feature> element 並且設 android:required="false"。


Special handling for Bluetooth feature
  • Google Play enables filtering for the Bluetooth feature only if the application declares its lowest or targeted platform as Android 2.0 (API level 5) or higher
  • However, note that Google Play applies the normal rules for filtering when the application explicitly declares the Bluetooth feature in a <uses-feature> element.


執行下列 command 可預知 Google Play 所得的 filter 條件

Android Tools > Export Unsigned/signed Application Package

# aapt dump badging <path_to_exported_.apk>


* Reference
- Filters on Google Play
<uses-feature>

[UI] Supports Multiple Screen - Scaling

If layout is dependency on pixel...
  • mdpi -> ldpi: 圖會變大而失真
  • mdpi -> hdpi: 圖會變小 (因為 pixel 變多/ inch)
  • 也因此在 ldpi mdpi hdpi 上會顯得大小不一

但若是採用 independency methods...
  • 則系統會選擇不同的 resource 所以如果只有 mdpi:
    • 則會縮小顯示在 ldpi device 上
    • 則會放大顯示在 hdpi device 上 而可能失真
  • 最終在 ldpi mdpi hdpi 大小會相同

Multiple screens support
  • Started in Android 1.1
  • Resources management added in 1.5
  • Compatibility mode added in 1.6

Inside Android
  • Everything is pixels
  • Resolution independent units
    • dip and dp
    • pt and sp
    • mm, in
  • Layouts

Resources management
  • Pre-scaling
  • Auto-scaling
  • By default, resources are mdpi
    • res/drawable
  • Use dpi-specific resources
    • res/drawable-hdpi
    • res/drawable-ldpi
    • res/drawable-mdpi


Pre-scaling (android:anydensity="true")
  • 系統善用 resources 的方式。系統會根據裝置 density 去選擇要使用那種大小的圖片(res/drawable-ldpi, drawable-mdpi, drawable-hdpi...)或者是放大/縮小 default drawable 來符合當下螢幕密度。
  • 如果 application 中沒有相符合的 resources 那麼系統就會使用預設的(res/drawable or res/drawablw-mdpi)並且放大或縮小它來顯示。
    • res/drawable 被視為 baseline screen density (mdpi)。
  • 如果取得已被放大/縮小的圖片的 dimension,那麼值會是已經放大/縮小後的。
  • Examples:
    • for an hdpi device, when there is only mdpi image 100 x 100, then the image will be pre-scaled to 150 x 150;
    • for an mdpi device, when there is only hdpi image 150 x 150, then the image will be pre-scaled to 100 x 100;
  • 此功能預設是 true,如果不想讓圖片被經過這樣的處理或是放大/縮小後顯示,可...
    • 將 resources 放在 res/drawable-nodpi 下。
    • 在 AndroidManifest 中設 <supports-screens android:anydensity="false">。
    • 設定 Bitmap 的 isScaled() 為 false。
    • 當 pre-scaling 被關閉,系統便會在畫圖時縮放圖片(see Auto-scaling),但不建議關閉 pre-scaling。

Auto-Scaling
  • 若想強迫使用 auto-scaling,則是要關閉 pre-scaling,ex: 在 AndroidManifest 中設 <supports-screens android:anydensity="false">。
  • 為了確保在不同裝置上能顯示相同的大小,所以即使 pre-scaling is disable 仍會再畫圖時做 auto-scaling。
  • CPU expensive, but use less memory.
  • Bitmap created at runtime will auto-scaled.


AndroidManifest.xml
  • android:anyDensity: 程序是否可在任何密度的螢幕上執行。主要是為使用了 px 作為單位的圖片。
    • If false: 系統就會 pre-scaling resources/density-compability,根據不同的屏幕密度將 px 值轉換為合理的大小。
    • If true: 就會關閉 pre-scaling resources/density-compability 。
    • 如果程序是以 dp 作為長度單位的,則不會受此值影響。
  • android:xxxScreens: 是否支持某屏幕(physical size)。
    • If false: 表示不支援該大小的螢幕,系統會啟用 size-compability 特性,即只顯示標準屏幕(normal size, mdpi)的大小。
      • ex: 即使是 hdpi 仍以 mdpi 的大小顯示,其餘部份則會留黑。
    • If true: 表示支援該大小的螢幕,系統就不會做任何處理直接顯示。

可以利用下列兩種方式來在程式中相對的長度:

計算出 dipValue 在該螢幕中應該為的 pixel value。
// Convert the dips to pixels
final float scale = getContext().getResources().getDisplayMetrics
().density;
pixels = ( int) (dipValue * scale + 0.5f );
宣告 dip 在 resources values 中,再取出該 pixel size by getDimensionPixelSize()

    20dip 

Resources r = aContext.getResources();
int length = r.getDimensionPixelSize(R.dimen.length);


* Reference
- 2009_Android_resolution_independence_and_high_performance_graphics_RomainGuy ***
Supporting Multiple Screens
- Situee's Blog - iPhone,Technology: Pre-scaling, Auto-Scaling and Screen Density
- How to Support Multiple Screens in Android without the need to Provide different bitmap drawables for different screen densities
- Android Samsung S I9000 screen size and density issues
- Android 屏幕兼容性
- Android ApiDemos示例解析(63):Graphics->Density (上)  ***
- Android ApiDemos示例解析(63):Graphics->Density (下)  ***
- android应用 DPI不同的适配问题分析 **

2012年5月1日 星期二

[AndroidGraphic] Canvas and Drawables

Drawing 2D graphics:
  • 由 layout 來畫。適用於不是太複雜、不需動態更動、像遊戲需要要求效率的圖 。
  • 直接用 Canvas 來畫出你的圖。適用於會規律性更新內容的圖。
    • You personally call the appropriate class's onDraw() method (passing it your Canvas), or one of the Canvas draw...() methods (like drawPicture()).
    • Doing in which thread:
      • In UI thread: call invalidate() and then handle the onDraw() callback.
      • In a separate thread: wherein you manage a SurfaceView and perform draws to the Canvas as fast as your thread is capable (you do not need to request invalidate()).

Draw with a Canvas
  • A Canvas works for you as a pretense, or interface, to the actual surface upon which your graphics will be drawn — it holds all of your "draw" calls. 
  • 有兩種使用方式
    • On a View (extends View)
      • Using within the onDraw() callback method, the Canvas is provided for you and you need only place your drawing calls upon it. 
    • On a SurfaceView
      • You can also acquire a Canvas from SurfaceHolder.lockCanvas(), when dealing with a SurfaceView object.
  • If you need to create a new Canvas, then you must define the Bitmap upon which drawing will actually be performed. 
  • The Bitmap is always required for a Canvas.
  • You can set up a new Canvas like this:
// 在此 Canvas 上所畫的內容,會以傳入的 Bitmap 為底。
Bitmap b = Bitmap.createBitmap(100,100,Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);

// 然後可以藉由 Canvas.drawBitmap() 得到所畫的 Bitmap。
// Drawable has its own draw() method that takes your Canvas as an argument.
drawable.draw(canvas); // 將 drawable 畫到 canvas 上。
It's recommended that you ultimately draw your final graphics through a Canvas offered to you by View.onDraw() or SurfaceHolder.lockCanvas().


On a View
如果 view 中沒有含大量處理過程或是會一直更新畫面(frame-rate speed, ex: chess game, a snake game, or another slowly-animated application),那麼可以繼承 View 客制自己的 view,在 View.onDraw() 中利用 Android Framework 傳入的 Canvas 畫圖。
  • 當要畫出你的 View 時,Android Framework 會呼叫 View.onDraw() ,但 The Android framework 只會在需要時去呼叫 onDraw(),不過你也可以藉 invalidate() 去觸發此呼叫,你的 view 會被重繪。
  • invalidate() 只能在 UI/main thread 中被呼叫,若是要在 non-UI thread 中呼叫,改使用 postInvalidate().


On a SurfaceView (android.view.SurfaceView)
  • The SurfaceView is a special subclass of View that offers a dedicated drawing surface within the View hierarchy.
  • 可以控制它的格式和大小。
  • SurfaceView 會負責把它的 surface 放在螢幕上正確的位置。
Purpose:
  • 在 View hierarchy 還沒準備好前,便可以在 secondary thread 中利用 SurfaceView 和它的 Canvas 繪畫且在 secondary thread rendering 它到 screen。
    • To offer this drawing surface to an application's secondary thread, so that the application isn't required to wait until the system's View hierarchy is ready to draw.
    • To provide a surface in which a secondary thread can render into the screen. 
  • Instead, a secondary thread that has reference to a SurfaceView can draw to its own Canvas at its own pace.
Process:
  • 藉 SurfaceHolder interface 的 getHolder() 可以取得和它相關的 surface。
  • 當 SurfaceView's window 為可視時,Surface 會被建立。
  • Implement surfaceCreated(SurfaceHolder) and surfaceDestroyed(SurfaceHolder) 就可以看到當 window 顯示與隱藏時,Surface 也被建立與摧毀。
To be aware of some threading semantics:
  • All SurfaceView and SurfaceHolder.Callback methods will be called from the thread running the SurfaceView's window (typically the main thread of the application).
    • They thus need to correctly synchronize with any state that is also touched by the drawing thread.
  • You must ensure that the drawing thread only touches the underlying Surface while it is valid -- between SurfaceHolder.Callback.surfaceCreated() and SurfaceHolder.Callback.surfaceDestroyed().

How to use?
  • Create a new class that extends SurfaceView.
  • The class should also implement SurfaceHolder.Callback.
  • This subclass is an interface that will notify you with information about the underlying Surface, such as when it is created, changed, or destroyed.
  • Inside your SurfaceView class is also a good place to define your secondary Thread class, which will perform all the drawing procedures to your Canvas.

Note:
  • Instead of handling the Surface object directly, you should handle it via a SurfaceHolder.
  • When your SurfaceView is initialized, get the SurfaceHolder by calling getHolder().
  • Notify the SurfaceHolder that you'd like to receive SurfaceHolder callbacks (from SurfaceHolder.Callback) by calling addCallback() (pass it this).
  • Override each of the SurfaceHolder.Callback methods inside your SurfaceView class.
  • On each pass you retrieve the Canvas from the SurfaceHolder, the previous state of the Canvas will be retained. In order to properly animate your graphics, you must re-paint the entire surface. 
    • For example, you can clear the previous state of the Canvas by filling in a color with drawColor() or setting a background image with drawBitmap(). Otherwise, you will see traces of the drawings you previously performed.


Drawables
  • A Drawable is a general abstraction for "something that can be drawn."
  • In application, 一個 drawable 只會擁有一份,狀態是共享,因此不管怎麼取得擁有,修改了其中的狀態,則原圖(其他 instance) 也會改變。
    • Each unique resource in your project can maintain only one state, no matter how many different objects you may instantiate for it. 
    • For example, if you instantiate two Drawable objects from the same image resource, then change a property (such as the alpha) for one of the Drawables, then it will also affect the other. So when dealing with multiple instances of an image resource, instead of directly transforming the Drawable, you should perform a tween animation.
  • 可以使用 mutate() 取得原圖,這樣這個就不會被改變到。

There are three ways to define and instantiate a Drawable:
  • Using an image saved in your project resources;
    • Supported file types are PNG (preferred), JPG (acceptable) and GIF (discouraged).
    • 放在 res/drawable/ 下的圖片,在 build 的過程,aapt 會自動以失真最少的方式壓縮圖片簡省空間。
    • 若是圖片不想被壓縮則可以放在 res/raw/ 下。
  • Using an XML file that defines the Drawable properties;
    • This philosophy caries over from Views to Drawables. 
    • If there is a Drawable object that you'd like to create, which is not initially dependent on variables defined by your application code or user interaction, then defining the Drawable in XML is a good option. 
    • Even if you expect your Drawable to change its properties during the user's experience with your application, you should consider defining the object in XML, as you can always modify properties once it is instantiated.
    • Any Drawable subclass that supports the inflate() method can be defined in XML and instantiated by your application.
  • Using the normal class constructors.


Shape Drawable
  • When you want to dynamically draw some two-dimensional graphics, a ShapeDrawable object will probably suit your needs.
  • With a ShapeDrawable, you can programmatically draw primitive shapes and style them in any way imaginable.


* Reference
- Canvas and Drawables | Android Developers