天天看點

ViewFlipper實作圖檔動态切換、GestureDetector實作手勢捕捉

ViewFlipper是Android系統提供的用于切換圖檔的UI控件,而GestureDetector是Android提供的用于判斷手勢并執行相應操作的類,該類中手勢判斷的方法與傳統的手勢判斷的方法(View類中的setOnTouchListener方法)相比,操作更加友善,提供的參數更多,可判斷的手勢更加多樣。下面将淺析一個demo:使用GestureDetector的手勢判斷方法,動态實作切換ViewFlipper中的圖檔。

demo的實作效果

demo的實作效果如下所示——左右滑動螢幕上的圖檔,實作圖檔的輪詢切換。

ViewFlipper實作圖檔動态切換、GestureDetector實作手勢捕捉
ViewFlipper實作圖檔動态切換、GestureDetector實作手勢捕捉

注:由于切圖速度比較快,故截圖顯示不夠明了,下面将在代碼中列印Log并說明手勢方法的調用。

XML布局

XML布局使用了include标簽用于包含相似的内容:

<ViewFlipper xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/view_flipper"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <include
        android:id="@+id/screen1"
        layout="@layout/item1" />

    <include
        android:id="@+id/screen2"
        layout="@layout/item2" />

    <include
        android:id="@+id/screen3"
        layout="@layout/item3" />

    <include
        android:id="@+id/screen4"
        layout="@layout/item4" />

    <include
        android:id="@+id/screen5"
        layout="@layout/item5" />

</ViewFlipper>
           

item1的layout布局如下(item2-item5與item1相仿,此處不再列出):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/android1"
    android:orientation="vertical" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="view1" />

</LinearLayout>
           

activity邏輯實作

public class MainActivity extends Activity {

    public static final String TAG = "GESTURE_DETECTOR";
    private ViewFlipper mViewFlipper;

    private GestureDetector mGestureDetector;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mViewFlipper = (ViewFlipper) findViewById(R.id.view_flipper);
        // 初始化GestureDetector類
        mGestureDetector = new GestureDetector(this,
                new CustomSimpleOnGestureListener());

        // 依然需要實作setOnTouchListener方法
        mViewFlipper.setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // TODO Auto-generated method stub
                // 使用GestureDetector接管手勢操作
                mGestureDetector.onTouchEvent(event);

                return true;
            }
        });

    }

    // 重寫GestureDetector類中的靜态子類SimpleOnGestureListener,
    // 并根據不同手勢操作,有選擇地重寫該靜态子類中的方法
    public class CustomSimpleOnGestureListener extends
            GestureDetector.SimpleOnGestureListener {

        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            // TODO Auto-generated method stub
            Log.i(TAG, "--onSingleTapUp--");
            return super.onSingleTapUp(e);
        }

        @Override
        public void onLongPress(MotionEvent e) {
            // TODO Auto-generated method stub
            Log.i(TAG, "--onLongPress--");
            super.onLongPress(e);
        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2,
                float distanceX, float distanceY) {
            // TODO Auto-generated method stub
            Log.i(TAG, "--onScroll--");
            return super.onScroll(e1, e2, distanceX, distanceY);
        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
                float velocityY) {
            // TODO Auto-generated method stub
            Log.i(TAG, "--onFling--");
            // 從左往右劃 劃到上一張
            if (e2.getX() - e1.getX() > ) {
                mViewFlipper.setInAnimation(AnimationUtils.loadAnimation(
                        MainActivity.this, R.anim.show_previous_in));
                mViewFlipper.setOutAnimation(AnimationUtils.loadAnimation(
                        MainActivity.this, R.anim.show_previous_out));
                mViewFlipper.showPrevious();
            }
            // 從右往左劃 劃到下一張
            else if (e1.getX() - e2.getX() > ) {
                mViewFlipper.setInAnimation(AnimationUtils.loadAnimation(
                        MainActivity.this, R.anim.show_next_in));
                mViewFlipper.setOutAnimation(AnimationUtils.loadAnimation(
                        MainActivity.this, R.anim.show_next_out));
                mViewFlipper.showNext();
            }

            return true;
        }

        @Override
        public void onShowPress(MotionEvent e) {
            // TODO Auto-generated method stub
            Log.i(TAG, "--onShowPress--");
            super.onShowPress(e);
        }

        @Override
        public boolean onDown(MotionEvent e) {
            // TODO Auto-generated method stub
            Log.i(TAG, "--onDown--");
            return super.onDown(e);
        }

        @Override
        public boolean onDoubleTap(MotionEvent e) {
            // TODO Auto-generated method stub
            Log.i(TAG, "--onDoubleTap--");
            return super.onDoubleTap(e);
        }

        @Override
        public boolean onDoubleTapEvent(MotionEvent e) {
            // TODO Auto-generated method stub
            Log.i(TAG, "--onDoubleTapEvent--");
            return super.onDoubleTapEvent(e);
        }

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            // TODO Auto-generated method stub
            Log.i(TAG, "--onSingleTapConfirmed--");
            return super.onSingleTapConfirmed(e);
        }

        @Override
        public boolean onContextClick(MotionEvent e) {
            // TODO Auto-generated method stub
            Log.i(TAG, "--onContextClick--");
            return super.onContextClick(e);
        }

    }
}
           

代碼分析:

  • 根據代碼可知,要通過GestureDetector實作手勢操作,仍然要先實作View類中的setOnTouchListener方法并傳入OnTouchListener接口對象,隻是需要在該接口中的onTouch方法中使用GestureDetector的onTouchEvent方法接管手勢操作而已。
  • 初始化GestureDetector類時,該類的構造方法中的第二個參數可以是以下4個對象之一:
    • 接口OnGestureListener,該接口中有6個未實作方法:

      —— onDown(MotionEvent e);

      —— onShowPress(MotionEvent e);

      —— onSingleTapUp(MotionEvent e);

      —— onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);

      —— onLongPress(MotionEvent e);

      —— onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)

    • 接口OnDoubleTapListener,該接口中有3個未實作方法:

      —— onSingleTapConfirmed(MotionEvent e);

      —— onDoubleTap(MotionEvent e);

      —— onDoubleTapEvent(MotionEvent e);

    • 接口OnContextClickListener ,該接口中有1個未實作方法:

      —— onContextClick(MotionEvent e);

    • 靜态子類SimpleOnGestureListener

      —— 該子類實作了上述3個接口的全部10個方法,隻是都是空實作而已,并沒有進行具體的邏輯覆寫。

  • 推薦使用靜态子類SimpleOnGestureListener對象作為構造GestureDetector類的第二個參數,這樣可以有選擇地覆寫所需要的方法,而不是需要實作所有未實作的方法。
  • 可以為圖檔切換添加動畫效果,切入時調用ViewFlipper的setInAnimation方法,切出時調用ViewFlipper的setOutAnimation方法,并在XML中自定義動畫效果,代碼如下:
<!-- 向左滑動時,切入的動畫定義 -->
<set xmlns:android="http://schemas.android.com/apk/res/android" >

    <translate
        android:duration="200"
        android:fromXDelta="100%p"
        android:toXDelta="0" >
    </translate>

    <alpha
        android:duration="200"
        android:fromAlpha="0.1"
        android:toAlpha="1.0" >
    </alpha>

</set>
           
<!-- 向左滑動時,切出的動畫定義 -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >

    <translate
        android:duration="200"
        android:fromXDelta="0"
        android:toXDelta="-100%p" />

    <alpha
        android:duration="200"
        android:fromAlpha="1.0"
        android:toAlpha="0.1" >
    </alpha>

</set>
           
<!-- 向右滑動時,切入的動畫定義 -->
<set xmlns:android="http://schemas.android.com/apk/res/android" >

    <translate
        android:duration="200"
        android:fromXDelta="-100%p"
        android:toXDelta="0"
        android:zAdjustment="bottom" >
    </translate>

    <alpha
        android:duration="200"
        android:fromAlpha="0.1"
        android:toAlpha="1.0" >
    </alpha>

</set>
           
<!-- 向右滑動時,切出的動畫定義 -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >

    <translate
        android:duration="200"
        android:fromXDelta="0"
        android:toXDelta="100%p" />

    <alpha
        android:duration="200"
        android:fromAlpha="1.0"
        android:toAlpha="0.1" >
    </alpha>

</set>
           

需要注意:

  • translate标簽用于設定切換起點與終點的相對位移,機關為“%p”;
  • alpha标簽用于設定起始位置和終止位置的透明度,範圍是0.0-1.0,其中0.0代表完全透明,1.0代表完全不透明。

SimpleOnGestureListener回調方法淺析

在靜态子類SimpleOnGestureListener中,一共有10個空實作的回調方法,這些方法會根據不同手勢操作而适時回調,開發者也可以為實作某個特定手勢而重寫特定的方法,而不用将所有方法重寫。

在demo中,列印Log,淺析不同手勢對應的回調方法:

ViewFlipper實作圖檔動态切換、GestureDetector實作手勢捕捉
  • 在螢幕上快速滑動,首先調用onDown,接着是一系列的onScroll,最後是onFling;
ViewFlipper實作圖檔動态切換、GestureDetector實作手勢捕捉
  • 單擊螢幕,先後調用onDown,onSingleTapUp,onSingleTapConfirmed;
ViewFlipper實作圖檔動态切換、GestureDetector實作手勢捕捉
  • 雙指(及雙指以上)單擊螢幕,僅調用onDown;
ViewFlipper實作圖檔動态切換、GestureDetector實作手勢捕捉
  • 在螢幕上慢速滑動,首先調用onDown,然後是一系列的onScroll,并不調用onFling方法;
ViewFlipper實作圖檔動态切換、GestureDetector實作手勢捕捉
  • 用力單擊螢幕,依次調用onDown,onShowPress,onSingleTapUp,OnSingleTapConfirmed;
ViewFlipper實作圖檔動态切換、GestureDetector實作手勢捕捉
  • 長按螢幕,依次調用onDown,onShowPress,onLongPress;
ViewFlipper實作圖檔動态切換、GestureDetector實作手勢捕捉
  • 輕按兩下螢幕,依次調用onDown,onSingleTapUp,onDoubleTap,onDoubleTapEvent,onDown,onDoubleTapEvent。

至此,除onContextClick未回調以外(該方法涉及到事件分發機制),其餘9個方法均在不同手勢的操作下被回調,開發者可以在不同手勢所回調的方法中編寫相應邏輯進而實作手勢的捕捉繼而觸發事件邏輯。

繼續閱讀