星期六, 1月 21, 2012

Animation (三)

Android應用程式學習筆記

How Property Animation Works

首先,我們就用一個簡單的例子說明動畫的運行,圖一描繪了一個假想的物件,針對它的X屬性的動畫,顯示在屏幕的水平的位置。動畫時間設定為40毫秒且行程距離40像素,每10毫秒物件水平移動10像素,在40毫秒時,動畫結束,物件停在水平位置40的地方。這是一個線性插補的動畫例子,意味著物件以等速移動。

圖一 線性動畫

你也可以指定動畫有非線性的插補,圖二說明了假想物件在動畫開始時加速,在動畫結束時減速。物件仍是在40毫秒內移動40像素,但視為非線性地,在動畫開始,動畫加速到半途的位置,接著減速直到動畫結束,圖二顯示物件在開始與結束時的行程距離比在行程中間少。

圖二 非線性動畫

接著讓我們來看看屬性動畫系統如何計算項以上動畫的細節,圖三說明動畫的主要類別。

圖三 動畫的計算

ValueAnimator物件保存你的動畫的時序,比如動畫的時間長度及動畫的屬性。
ValueAnimator物件封裝了一個TimeInterpolator,它定義了動畫的插補,及一個TypeEvaluator,它定義了如何計算動畫中的屬性數值。例如圖二中的例子,TimeInterpolator為AccelerateDecelerateInterpolator且TypeEvaluator為IntTypeEvaluator。

啟動一個動畫,建立一個ValueAnimator並給定動畫的起始數值與結束數值,當你呼叫start()方法時動畫開始。在整個動畫中,ValueAnimator根據動畫的時間與以動畫的時間計算elapsed fraction,elapsed fraction介於0與1之間,elapsed fraction顯示動畫完成的時間比例,0表示0%;1表示100%。例如圖一,在時間10毫秒時,elapsed fraction為0.25,因為動畫的總時間為40毫秒。

當ValueAnimator正在計算elapsed fraction時,它會呼叫TimeInterpolator去計算Interpolated fraction,一個Interpolated fraction映射一個elapsed fraction為新的fraction。比如圖二,因為動畫為慢慢加速,在10毫秒時,interpolated fraction為0.15,比起elapsed fraction為0.25小,圖一,interpolated fraction與elapsed fraction一直相同。

當interpolated fraction已計算出來,ValueAnimator呼叫適當的TypeEvaluator,根據interpolated fraction計算動畫的屬性數值,起始的數值,結束的數值。舉例,圖一,在10毫秒時interpolated fraction為0.15,所以屬性數值在此時間時為0.15*(40 - 0),或者6。



Animation (二)

Android應用程式學習筆記

Property Animation

屬性動畫系統是一個健全的框架,幾乎允許你實現任何動畫。你可以定義一個動畫隨時間改變物件的任何屬性,無論是否繪製在屏幕,屬性動畫在指定的時間改變屬性值。想完成甚麼樣動畫,你必須指定想動畫的物件屬性,比如物件在屏幕上的位置、動畫時間、動畫間數值的改變。

屬性動畫系統讓你定義以下的動畫特性:

  • Duration:你可以指定動畫的時間,預設是300毫秒。
  • Time interpolation:You can specify how the values for the property are calculated as a function of the animation's current elapsed time.
  • Repeat count and behavior:你可以指定是否在動畫結束時重複動畫及重複次數,你也可以指定動畫是否倒帶,設定動畫可以來回重複倒帶播放,直到達到重複次數。
  • Animator set:你可以組合動畫為邏輯組合一起播放,或是依序播放,或是在動畫中間加入延遲時間。
  • Frame refresh delay:你可以指定多常刷新你的動畫,預設是每10毫秒刷新一次,但是在你的應用程式可以最快刷新的速度要看系統忙碌的程度以及系統提供服務的速度決定。

星期五, 1月 20, 2012

Animation (一)

Android應用程式學習筆記

Animation

Android框架提供兩種動畫系統:屬性動畫(在Android3.0介紹)及視圖動畫。兩種動畫系統都是可行的選擇,但是通常屬性動畫系統是首選使用的系統,因為它更加彈性和提供更多特性。除了這兩個動畫系統外,你可以利用繪製動畫,它可以允許你載入繪製資源並顯示。

視圖動畫系統所提供的功能只能用在視圖物件動畫上,所以如果你想要動畫非視圖物件,你必須用自己的程式碼實現。視圖動畫系統也受到一個事實限制,它只公開視圖的一些屬性能動畫,比如放大縮小或是旋轉視圖,但不能改變背景顏色。

視圖動畫系統的其他缺點是它只改變視圖繪製的位置,並不是實際視圖本身。舉例,如果你在屏幕上動畫移動一個按鈕,按鈕正確地繪製,但實際你可以點擊的位置卻沒有改變,所以你必須執行你的邏輯來處理。

用屬性動畫系統,以上這些問題完全被移除了,你可以動畫任何物件的任何屬性(視圖與非視圖),物件自己是確實被改變。屬性動畫系統在執行動畫方面也更加健全強大。在高層級上,你指定了你想要動畫的特性,比如,顏色、位置或大小,並可以定義動畫方面,比如插補、多個動畫同步。

然而視圖動畫系統花費較少時間設置及編輯較少的程式碼,如果視圖動畫已完成任何你想要的,或者如果你有的程式碼已經準備按你的方式運行,就沒有需要使用屬性動畫系統。你可以依照你的情況使用這兩種動畫系統。

星期二, 11月 15, 2011

Fragment (十)

Adnrdoid應用程式學習筆記

Coordinating with the activity lifecycle

Fragment的寄主activity的生命週期直接影響fragment的生命週期,如此,activity的生命週期回呼方法與fragment的生命週期回呼方法類似,例如,當activity接收onPause()方法,在activity中的每個fragment也接收onPause()方法。

Fragment也有一些額外的回呼方法,然而,這些方法用來處理與activity的互動,完成一些動作比如建立或移除fragment的UI,這些額外的回呼方法有:

onAttach()
當fragment與activity聯合的時候呼叫此方法。
onCreateView()
呼叫此方法建立與fragment聯合的視圖階層。 
onActivityCreated()
當activity的onCreate()方法執行完呼叫此方法。
onDestoryView()
當與fragment有關的視圖階層移除時呼叫此方法。
onDetach()
當fragment不'要與activtiy聯合時呼叫此方法。 
fragment的生命週期流程,以及如何受寄主activity影響,都顯示在圖中。


在此圖中,你可以觀察activity的接替的狀態如何決定fragement接收哪一個回呼方法。舉例,當activity已經接收onCreate()方法的回呼,在activity的fragment只有接收onActivityCreated()方法回呼。

一旦activity處於resumed狀態,你可以自由地對activity增加或是移除fragment,因此,只有在activity是resumed狀態,fragment的生命週期才能獨立地改變。

然而,當activity離開resumed狀態,fragment再次由activity推動fragment的生命週期。
 

星期一, 11月 14, 2011

Fragment (九)

Android應用程式學習筆記

Handling the Fragment Lifecycle

管理fragment的生命週期非常像在管理activity的生命週期,如activity,fragment也存在三個狀態:

Resumed:
Fragment在運行中的activity是可見的。
Paused:
其他的activity在前台且擁有焦點,但是在activity的該fragment仍是可見的(前台activity部分是透明的或沒有完全覆蓋屏幕)。
Stopped:
Fragment已不可見,一方面可能是寄主activity已經停止或是fragment已經從activity被移除,但被加到back stack中。被停止的fragment仍是活的(所有的狀態和變數資訊都由系統保存),然而,fragment不再被用戶所見且如果activity被殺掉fragment也會被殺掉。
也像activity一樣,可以利用Bundle保留fragment狀態,假如activity的進程被殺掉了而你需要在activity重新建立時恢復fragment,你可以在fragment的onSaveInstanceState()回調方法中儲存狀態並且在onCreate()方法或是onCreateView()方法中恢復狀態。

activity的生命週期與fragment的生命週期之間最大的差異在於如何儲存在各自的back stack。預設,當activity停止會被置到由系統管理的back stack,然而,fragment只在你確切地要求在fragment移除時呼叫addToBackStack()方法儲存實例時,才會置入由寄主activity管理的back stack。

除此之外,管理fragment生命週期與管理activity生命週期非常類似,所以練習管理activity生命週期也可以用在fragment上,觀察activity生命週期如何影響fragment生命週期。


星期四, 11月 10, 2011

Fragment (八)

Android應用程式學習筆記

Communicating with the Activity

雖然fragment可以做為物件獨立於activity,也可以使用在多個activities,預設的fragment實體是直接與activity綁定。

特別是,fragment透過getActivity()方法可以存取Activity並輕鬆地執行工作比如取得activity布局的view。


View listView = getActivity().findViewById(R.id.list);

同樣地,你的activity也可以呼叫fragment中的方法,透過findFragmentById()方法或是findFragmentByTag()方法。


ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);


Creating event callbacks to the Activity

在一些情況中,你需要一個fragment與activity分享一個事件,在fragment定義一個回呼的介面並要求寄主activity執行是一個分享事件的好方法。當activity從界面接收到回呼,介面就會將資訊分享給在布局中的其他需要的fragment。

舉例,如果新聞應用程式有兩個fragment在activity中-一個顯示文章列表(Fragment A)-其他顯示文章內容(Fragment B),當一個列表選項被選到時,Fragment A必須告訴activity以便activity可以呼叫Fragment B去顯示文章內容,在此例中,OnArticleSelectedListener介面被宣告在Fragment A中。


public static class FragmentA extends ListFragment {
    ...
    // Container Activity must implement this interface
    public interface OnArticleSelectedListener {
        public void onArticleSelected(Uri articleUri);
    }
    ...
}

然後fragment的寄主activity執行OnArticleSelectedListener介面並複寫OnArticleSelected()方法,去通知Fragment B來自Fragment A的事件,為了確認寄主activity實現此介面,fragment A的onAttach()回呼方法將實體化OnArticleSelectedListener的Activity傳入onAttach()方法。


public static class FragmentA extends ListFragment {
    OnArticleSelectedListener mListener;
    ...
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mListener = (OnArticleSelectedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
        }
    }
    ...
}

如果activity沒有執行介面,fragment就會丟出ClassCastException例外,如果成功,mListener持有activity對OnArticleSelectedListener實現的引用,所以Fragment A可以通過呼叫由OnArticleSelectedListener介面定義的方法與Activity分享事件。例如,如果fragment A繼承ListFragment,每次用戶點擊列表項目,系統呼叫fragment中的onListItemClick()方法,接著在呼叫onArticleSelected()方法與Activity分享事件。


public static class FragmentA extends ListFragment {
    OnArticleSelectedListener mListener;
    ...
    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        // Append the clicked item's row ID with the content provider Uri
        Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);
        // Send the event and Uri to the host activity
        mListener.onArticleSelected(noteUri);
    }
    ...
}

傳入onListItemClick()方法中的id參數是被選的項目行ID,id用來從activity的ContentProvider取文章。

星期三, 11月 09, 2011

Fragment (七)

Android應用程式學習筆記

Managing Fragments

管理在你的activity的fragments,需要使用FragmentManager,從activtiy呼叫getFragmentManager()方法取得FragmentManager。

FragmentManager可以完成以下工作:

  • 取得存在於activity的fragment,findFragmentById()方法(有在activity布局中提供UI的fragment)或是findFragmentTag()方法(沒有在activity佈局中提供UI的fragment)。
  • 將fragment從back stack中拿出,popBackStack()方法。
  • 向back stack註冊一個監聽器,監聽改變,addBackStackChangedListener()方法。

Performing Fragment Transactions

在activity中與使用fragment有關的最重要議題就是增加、移除、替換及運用fragment執行其他工作,回應用戶的互動。你託付給activity的每一組fragment變化稱為一個transaction且你可以使用FragmentTransaction的APIs組織一個transaction。你可以將transaction存在back stack由activity管理,允許用戶反轉fragment變化。

你可以從FragmentManager獲得FragmentTransaction實例,像是這樣:

FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
每個transaction都是你想在同意時間執行的變化組合,你可以使用比如add()、remove()、replace()方法組織所有你想要執行的變化為一個transaction,然後將transaction應用在activity上,最後你必須呼叫commit()方法。

在呼叫commit()方法之前,你可能想要呼叫addToBackStack()方法,為了增加transaction到fragment transaction的back stack。Back stack由activity管理且允許用戶按"BACK"按鈕回傳前一個fragment的狀態。

舉例,你如何將fragment由另一個fragment替換,並且將先前的狀態保存在back stack。

// Create new fragment and transaction
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
此例中,newFragment取代了目前在布局容器中ID為R.id.Fragment_container的fragment,藉由呼叫addToBackStack()方法,替換的transaction被存到back stack中,所以用戶可以反轉transaction並且透過"BACK"按鈕返回先前的fragment。

如果增加更多變化到transaction(比如其他add()、remove())並呼叫addToBackStack()方法,在呼叫commit()之前所有變化都會增加到back stack作為一個transaction且"BACK"可以將transaction一起反轉。

將變化加到FragmentTransaction的順序並不重要,除了:
  • 必須在最後時呼叫commit()方法。
  • 如果要增加多個fragment到同一個容器,加入的順序決定了出現在view階層的順序。
如果在執行transaction移除fragment時,沒有呼叫addToBackStack()方法,然後,transaction被commit,fragment被銷毀,用戶就無法在回去,反之,如果有呼叫addToBackStack()方法,fragment被停止,當用戶想回去時,fragment就會再次恢復。

呼叫commit()方法後並不會立即執行transaction,由activity的UI執行續安排。如果需要,可以從UI執行續呼叫executingPandingTransaction()方法立即執行transaction,並不是常常會如此需要,除非transaction牽涉到其他的執行續。