屏幕切換指的是在同一個Activity內屏幕見的切換,最長見的情況就是在一個FrameLayout內有多個頁面,比如一個系統設置頁面;一個個性化設置頁面。
通過查看OPhone API文檔可以發現,有個android.widget.ViewAnimator類繼承至FrameLayout,ViewAnimator類的作用是爲FrameLayout裏面的View切換提供動畫效果。該類有如下幾個和動畫相關的函數:
l setInAnimation:設置View進入屏幕時候使用的動畫,該函數有兩個版本,一個接受單個參數,類型爲android.view.animation.Animation;一個接受兩個參數,類型爲Context和int,分別爲Context對象和定義Animation的resourceID。
- setOutAnimation: 設置View退出屏幕時候使用的動畫,參數setInAnimation函數一樣。
- showNext: 調用該函數來顯示FrameLayout裏面的下一個View。
- showPrevious: 調用該函數來顯示FrameLayout裏面的上一個View。
一般不直接使用ViewAnimator而是使用它的兩個子類ViewFlipper和ViewSwitcher。ViewFlipper可以用來指定FrameLayout內多個View之間的切換效果,可以一次指定也可以每次切換的時候都指定單獨的效果。該類額外提供瞭如下幾個函數:
- isFlipping: 用來判斷View切換是否正在進行
- setFilpInterval:設置View之間切換的時間間隔
- startFlipping:使用上面設置的時間間隔來開始切換所有的View,切換會循環進行
- stopFlipping: 停止View切換
ViewSwitcher 顧名思義Switcher特指在兩個View之間切換。可以通過該類指定一個ViewSwitcher.ViewFactory 工程類來創建這兩個View。該類也具有兩個子類ImageSwitcher、TextSwitcher分別用於圖片和文本切換。
在教程中通過示例介紹ViewFlipper 的使用,其他的使用方式是類似的。詳細信息可以參考文檔:
ViewFlipper示例
記住,ViewFlipper是繼承至FrameLayout的,所以它是一個Layout裏面可以放置多個View。在示例中定義一個ViewFlipper,裏面包含三個ViewGroup作爲示例的三個屏幕,每個ViewGroup中包含一個按鈕和一張圖片,點擊按鈕則顯示下一個屏幕。代碼如下(res\layout\main.xml):
-
<?xml
version= "1.0"encoding= "utf-8"?> -
<LinearLayout
-
xmlns:android= -
android:orientation= -
android:layout_width= -
android:layout_height= -
<ViewFlipper android:id= -
android:layout_width= -
android:layout_height= -
android:persistentDrawingCache= -
android:flipInterval= -
android:inAnimation= -
android:outAnimation="@anim/push_left_out"
-
>
-
<LinearLayout -
android:orientation= -
android:layout_width= -
android:layout_height= -
<Button -
android:text= -
android:id= -
android:layout_width= -
android:layout_height= -
</Button> -
<ImageView -
android:id= -
android:src= -
android:layout_width= -
android:layout_height= -
</ImageView> -
</LinearLayout> -
-
<LinearLayout -
android:orientation= -
android:layout_width= -
android:layout_height= -
<Button -
android:text= -
android:id= -
android:layout_width= -
android:layout_height= -
</Button> -
<ImageView -
android:id= -
android:src= -
android:layout_width= -
android:layout_height= -
</ImageView> -
</LinearLayout> -
-
<LinearLayout -
android:orientation= -
android:layout_width= -
android:layout_height= -
<Button -
android:text= -
android:id= -
android:layout_width= -
android:layout_height= -
</Button> -
<ImageView -
android:id= -
android:src= -
android:layout_width= -
android:layout_height= -
</ImageView> -
</LinearLayout> -
-
</ViewFlipper> -
-
</LinearLayout>
很簡單,在Layout定義中指定動畫的相關屬性就可以了,通過persistentDrawingCache指定緩存策略;flipInterval指定每個View動畫之間的時間間隔;inAnimation和outAnimation分別指定View進出使用的動畫效果。動畫效果定義如下:
-
res\anim\push_left_in.xml
-
<?xml
version= "1.0"encoding= "utf-8"?> -
<set
xmlns:android= "http://schemas.android.com/apk/res/android"> -
<translate -
android:fromXDelta= -
android:toXDelta= -
android:duration= -
<alpha -
android:fromAlpha= -
android:toAlpha= -
android:duration= /> -
</set>
-
res\anim\push_left_out.xml
-
<?xml
version= "1.0"encoding= "utf-8"?> -
<set
xmlns:android= "http://schemas.android.com/apk/res/android"> -
<translate -
android:fromXDelta= -
android:toXDelta= -
android:duration= -
<alpha -
android:fromAlpha= -
android:toAlpha= -
android:duration= /> -
</set>
Activity代碼如下(src\cc\c\TestActivity.java):
-
public
class TestActivity extendsActivity { -
ViewFlipper mViewFlipper; -
-
void onCreate(Bundle savedInstanceState) { -
-
setContentView(R.layout.main); -
-
Button buttonNext1 = (Button) findViewById(R.id.Button_next1); -
mViewFlipper = (ViewFlipper) findViewById(R.id.flipper); -
buttonNext1.setOnClickListener( View.OnClickListener() { -
void onClick(View view) { -
-
//
mViewFlipper.setInAnimation(getApplicationContext(), R.anim.push_left_in); -
//
mViewFlipper.setOutAnimation(getApplicationContext(), R.anim.push_left_out); -
//
mViewFlipper.setPersistentDrawingCach e(ViewGroup.PERSISTENT_ALL_CACHES); -
//
mViewFlipper.setFlipInterval(1000); -
mViewFlipper.showNext(); -
-
//
mViewFlipper.startFlipping(); -
} -
}); -
-
Button buttonNext2 = (Button) findViewById(R.id.Button_next2); -
buttonNext2.setOnClickListener( View.OnClickListener() { -
void onClick(View view) { -
mViewFlipper.showNext(); -
} -
-
}); -
Button buttonNext3 = (Button) findViewById(R.id.Button_next3); -
buttonNext3.setOnClickListener( View.OnClickListener() { -
void onClick(View view) { -
mViewFlipper.showNext(); -
} -
-
}); -
-
} -
}
通過手勢移動屏幕
上面是通過屏幕上的按鈕來在屏幕間切換的,這看起來多少有點不符合OPhone的風格,如果要是能通過手勢的左右滑動來實現屏幕的切換就比較優雅了。
通過android.view.GestureDetector類可以檢測各種手勢事件,該類有兩個回調接口分別用來通知具體的事件:
GestureDetector.OnDoubleTapListener:用來通知DoubleTap事件,類似於鼠標的雙擊事件,該接口有如下三個回調函數:
1. onDoubleTap(MotionEvent e):通知DoubleTap手勢,
2. onDoubleTapEvent(MotionEvent e):通知DoubleTap手勢中的事件,包含down、up和move事件(這裏指的是在雙擊之間發生的事件,例如在同一個地方雙擊會產生DoubleTap手勢,而在DoubleTap手勢裏面還會發生down和up事件,這兩個事件由該函數通知);
3. onSingleTapConfirmed(MotionEvent e):用來判定該次點擊是SingleTap而不是DoubleTap,如果連續點擊兩次就是DoubleTap手勢,如果只點擊一次,OPhone系統等待一段時間後沒有收到第二次點擊則判定該次點擊爲SingleTap而不是DoubleTap,然後觸發SingleTapConfirmed事件。
GestureDetector.OnGestureListener:用來通知普通的手勢事件,該接口有如下六個回調函數:
1. onDown(MotionEvent e):down事件;
2. onSingleTapUp(MotionEvent e):一次點擊up事件;
3. onShowPress(MotionEvent e):down事件發生而move或則up還沒發生前觸發該事件;
4. onLongPress(MotionEvent e):長按事件;
5. onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY):滑動手勢事件;
6. onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY):在屏幕上拖動事件。
這些事件有些定義的不太容易理解,在示例項目中實現了所有的回調函數,在每個函數中輸出相關的日誌,對這些事件不理解的可以運行項目,通過不同的操作來觸發事件,然後觀看logcat輸出日誌可有助於對這些事件的理解。
在上述事件中,如果在程序中處理的該事件就返回true否則返回false,在GestureDetector中也定義了一個SimpleOnGestureListener類,這是個助手類,實現了上述的所有函數並且都返回false。如果在項目中只需要監聽某個事件繼承這個類可以少些幾個空回調函數。
要走上面的程序中添加滑動手勢來實現屏幕切換的話,首先需要定義一個GestureDetector:
private GestureDetector mGestureDetector;
並在onCreate函數中初始化:
mGestureDetector = new GestureDetector(this);
參數是OnGestureListener,然後讓TestActivity實現 OnGestureListener 和OnDoubleTapListener接口:
-
class
TestActivity extendsActivity implementsOnGestureListener , OnDoubleTapListener
然後在onFling函數中實現切換屏幕的功能:
-
public
boolean onFling(MotionEvent floate1, MotionEvent e2, velocityX, -
velocityY) { -
Log.d(tag, -
> //movee2.getX()) { to left -
mViewFlipper.showNext(); -
} if(e1.getX() < e2.getX()) { -
mViewFlipper.setInAnimation(getApplicationContext(), R.anim.push_right_in); -
mViewFlipper.setOutAnimation(getApplicationContext(), R.anim.push_right_out); -
mViewFlipper.showPrevious(); -
mViewFlipper.setInAnimation(getApplicationContext(), R.anim.push_left_in); -
mViewFlipper.setOutAnimation(getApplicationContext(), R.anim.push_left_out); -
} { -
false; -
} -
true; -
}
這裏實現的功能是從右往左滑動則切換到上一個View,從左往右滑動則切換到下一個View,並且使用不同的in、out 動畫使切換效果看起來統一一些。
然後在onDoubleTap中實現雙擊自動切換的效果,再次雙擊則停止:
-
public
boolean onDoubleTap(MotionEvent e) { -
Log.d(tag, -
{ -
mViewFlipper.stopFlipping(); -
} { -
mViewFlipper.startFlipping(); -
} -
true; -
}
到這裏手勢代碼就完成了,現在可以通過左右滑動切換View並且雙擊可以自動切換View。細心的讀者這裏可能會發現一個問題,上面在創建mGestureDetector 的時候使用的是如下代碼:
mGestureDetector = new GestureDetector(this);
這裏的參數爲OnGestureListener,而且GestureDetector有個函數setOnDoubleTapListener來設置OnDoubleTapListener,在上面的代碼中並沒有設置OnDoubleTapListener,那麼onDoubleTap事件是如何調用的呢?這裏的玄機就要去探探 GestureDetector(OnGestureListener l)這個構造函數的源代碼了:
-
GestureDetector(OnGestureListener listener) { -
listener, null); -
}
調用了另外一個構造函數:
-
public
GestureDetector(Context context, OnGestureListener listener, Handler handler) { -
(handler null)!= { -
mHandler = GestureHandler(handler); -
} { -
mHandler = GestureHandler(); -
} -
mListener = listener; -
(listener instanceofOnDoubleTapListener) { -
setOnDoubleTapListener((OnDoubleTapListener) listener); -
} -
init(context);