android開發之GestureDetector手勢識別(調節音量、亮度、快進和後退)

yxwkaifa發表於2016-04-22

這裡寫圖片描寫敘述

寫UI佈局:

<?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:fitsSystemWindows="true"
    android:orientation="vertical" >

    <include
        android:id="@+id/root_layout"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        layout="@layout/video_layout" />

</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent" >

    <com.zanelove.gesturedetectordemo.views.MyVideoView
            android:id="@+id/tv_pro_play"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    <LinearLayout
            android:id="@+id/ll_player_controller"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:layout_alignParentBottom="true"
            android:layout_alignParentLeft="true"
            android:background="#c0000000"
            android:gravity="center"
            android:orientation="horizontal"
            android:visibility="gone">

        <ImageView
                android:id="@+id/iv_play_pause"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="15dp"
                android:src="@drawable/btn_pause" />

        <TextView
                android:id="@+id/tv_playing_time"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="15dp"
                android:text="00:00"
                android:textColor="@android:color/darker_gray"
                android:textSize="15sp" />

        <SeekBar
                android:id="@+id/sb_video_progress"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:layout_weight="2" />

        <TextView
                android:id="@+id/tv_total_time"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:text="00:00"
                android:textColor="@android:color/darker_gray"
                android:textSize="15sp" />

        <ImageView
                android:id="@+id/iv_full_screen"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="15dp"
                android:padding="15dp"
                android:src="@drawable/btn_full_screen" />
    </LinearLayout>

    <RelativeLayout
            android:id="@+id/gesture_volume_layout"
            android:layout_width="120dip"
            android:layout_height="100dip"
            android:layout_centerInParent="true"
            android:background="@drawable/souhu_player_gesture_bg"
            android:gravity="center"
            android:visibility="gone" >

        <ImageView
                android:id="@+id/gesture_iv_player_volume"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:src="@drawable/souhu_player_volume" />

        <TextView
                android:id="@+id/geture_tv_volume_percentage"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@id/gesture_iv_player_volume"
                android:layout_centerHorizontal="true"
                android:gravity="right"
                android:text="80%"
                android:textColor="#ffececec" />
    </RelativeLayout>

    <RelativeLayout
            android:id="@+id/gesture_bright_layout"
            android:layout_width="120dip"
            android:layout_height="100dip"
            android:layout_centerInParent="true"
            android:background="@drawable/souhu_player_gesture_bg"
            android:gravity="center"
            android:visibility="gone" >

        <ImageView
                android:id="@+id/gesture_iv_player_bright"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:src="@drawable/souhu_player_bright" />

        <TextView
                android:id="@+id/geture_tv_bright_percentage"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@id/gesture_iv_player_bright"
                android:layout_centerHorizontal="true"
                android:gravity="right"
                android:text="80%"
                android:textColor="#ffececec" />
    </RelativeLayout>

    <RelativeLayout
            android:id="@+id/gesture_progress_layout"
            android:layout_width="120dip"
            android:layout_height="100dip"
            android:layout_centerInParent="true"
            android:background="@drawable/souhu_player_gesture_bg"
            android:gravity="center"
            android:visibility="gone">

        <ImageView
                android:id="@+id/gesture_iv_progress"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:src="@drawable/souhu_player_backward" />

        <TextView
                android:id="@+id/geture_tv_progress_time"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@id/gesture_iv_progress"
                android:layout_centerHorizontal="true"
                android:gravity="right"
                android:text="00:35/24:89"
                android:textColor="#ffececec" />
    </RelativeLayout>

</RelativeLayout>

以上將UI佈局給大夥貼上上來了。大家依照各自的需求改吧改吧…就可以

那麼。就下來我將首先給大家分析分析怎樣通過手勢識別來調節音量、亮度、快鍵和後退需求!

一提到手勢識別。大夥第一反應絕對是Google提供給我們的GestureDetector類,沒錯今天我們就使用使用這個類來給大家完畢以上的需求!

第一:將主邏輯程式碼類繼承FragmentActivity類並實現OnGestureListener監聽和OnTouchListener監聽,同一時候在onCreate方法中建立GestureDetector物件,這時須要傳遞兩個物件,這也是為啥我要繼承和實現了!

GestureDetector gestureDetector = new GestureDetector(this, this);

第二:獲取UI佈局中定義的控制元件

這些你全然能夠使用xUtils第三方工具的註解來完畢下面操作:

@ViewInject(R.id.gesture_bright_layout)
RelativeLayout gesture_bright_layout;

ViewUtils.inject(this);

也能夠通過findViewById方法:

// 視訊播放控制元件
tv_pro_play = (MyVideoView) findViewById(R.id.tv_pro_play);

/*
iv_full_screen = (ImageView) findViewById(R.id.iv_full_screen);
iv_play_pause = (ImageView) findViewById(R.id.iv_play_pause);

ll_player_controller = (LinearLayout) findViewById(R.id.ll_player_controller);

iv_play_pause = (ImageView) findViewById(R.id.iv_play_pause);
sb_video_progress = (SeekBar) findViewById(R.id.sb_video_progress);
iv_full_screen = (ImageView) findViewById(R.id.iv_full_screen);
tv_playing_time = (TextView) findViewById(R.id.tv_playing_time);
tv_total_time = (TextView) findViewById(R.id.tv_total_time);*/

// ****************音量/進度/亮度*********************

root_layout = (RelativeLayout) findViewById(R.id.root_layout);
gesture_volume_layout = (RelativeLayout) findViewById(R.id.gesture_volume_layout);
gesture_bright_layout = (RelativeLayout) findViewById(R.id.gesture_bright_layout);
gesture_progress_layout = (RelativeLayout) findViewById(R.id.gesture_progress_layout);
geture_tv_progress_time = (TextView) findViewById(R.id.geture_tv_progress_time);
geture_tv_volume_percentage = (TextView) findViewById(R.id.geture_tv_volume_percentage);
geture_tv_bright_percentage = (TextView) findViewById(R.id.geture_tv_bright_percentage);
gesture_iv_progress = (ImageView) findViewById(R.id.gesture_iv_progress);
gesture_iv_player_volume = (ImageView) findViewById(R.id.gesture_iv_player_volume);
gesture_iv_player_bright = (ImageView) findViewById(R.id.gesture_iv_player_bright);
gestureDetector = new GestureDetector(this, this); //須要實現OnGestureListener監聽
root_layout.setLongClickable(true);
gestureDetector.setIsLongpressEnabled(true);
root_layout.setOnTouchListener(this);//須要實現OnTouchListener監聽
audiomanager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
maxVolume = audiomanager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); // 獲取系統最大音量
currentVolume = audiomanager.getStreamVolume(AudioManager.STREAM_MUSIC); // 獲取當前值

第三:獲取視訊播放窗體的尺寸,推斷觸發的視訊播放窗體位置來識別不同的操作和需求:

/** 獲取視訊播放窗體的尺寸 */
ViewTreeObserver viewObserver = root_layout.getViewTreeObserver();
viewObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        root_layout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
        playerWidth = root_layout.getWidth();
        playerHeight = root_layout.getHeight();
    }
});

第四:當你實現OnGestureListener監聽和OnTouchListener監聽時是須要您實現其方法的。各自是:

當你實現了OnTouchListener監聽須要覆寫其方法:
@Override
public boolean onTouch(View v, MotionEvent event) {
    return false;
}

當你實現了OnGestureListener監聽須要覆寫一下方法:
// 使用者輕觸觸控式螢幕,由1個MotionEvent ACTION_DOWN觸發  
@Override
public boolean onDown(MotionEvent e) {
    return false;
}

// 使用者按下觸控式螢幕,並拖動,由1個MotionEvent ACTION_DOWN, 多個ACTION_MOVE觸發
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
    return false;
}

// 使用者(輕觸觸控式螢幕後)鬆開,由1個MotionEvent ACTION_UP觸發
@Override
public boolean onSingleTapUp(MotionEvent e) {
    return false;
}

// 使用者按下觸控式螢幕、高速移動後鬆開,由1個MotionEvent ACTION_DOWN, 多個ACTION_MOVE, 1個ACTION_UP觸發
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
    return false;
}

// 使用者長按觸控式螢幕,由多個MotionEvent ACTION_DOWN觸發
@Override
public void onLongPress(MotionEvent e) {}

// 使用者輕觸觸控式螢幕,尚未鬆開或拖動,由1個MotionEvent ACTION_DOWN觸發, 注意和onDown()的差別,強調的是沒有鬆開或者拖動的狀態
@Override
public void onShowPress(MotionEvent e) {}

能夠看到OnTouchListener僅僅能監聽到三種觸控事件,即按下。移動,鬆開,假設想要監聽到雙擊、滑動、長按等複雜的手勢操作,這個時候就必須得用到OnGestureListener了。

因此在onTouch()方法中,我們要進行例如以下的處理:

@Override
public boolean onTouch(View v, MotionEvent event) {
    // 手勢裡除了singleTapUp,沒有其它檢測up的方法
    if (event.getAction() == MotionEvent.ACTION_UP) {
        GESTURE_FLAG = 0;// 手指離開螢幕後,重置調節音量或進度的標誌
        gesture_volume_layout.setVisibility(View.GONE);
        gesture_bright_layout.setVisibility(View.GONE);
        gesture_progress_layout.setVisibility(View.GONE);
    }
    return gestureDetector.onTouchEvent(event);//假設想要監聽到雙擊、滑動、長按等複雜的手勢操作,這個時候就必須得用到OnGestureListener了
}

同一時候點選螢幕時觸發的onDown()方法中:

@Override
public boolean onDown(MotionEvent e) {
    firstScroll = true;// 設定是觸控螢幕後第一次scroll的標誌
    return false;
}

通過在onTouch()方法中呼叫gestureDetector.onTouchEvent(event)方法時,它會去呼叫onScroll()方法,這樣在該方法中通過手勢識別來完畢調節音量、亮度、快鍵和後退操作:

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
    float mOldX = e1.getX(), mOldY = e1.getY();
    int y = (int) e2.getRawY();
    if (firstScroll) {// 以觸控螢幕後第一次滑動為標準,避免在螢幕上操作切換混亂
        // 橫向的距離變化大則調整進度,縱向的變化大則調整音量
        if (Math.abs(distanceX) >= Math.abs(distanceY)) {
            gesture_progress_layout.setVisibility(View.VISIBLE);
            gesture_volume_layout.setVisibility(View.GONE);
            gesture_bright_layout.setVisibility(View.GONE);
            GESTURE_FLAG = GESTURE_MODIFY_PROGRESS;
        } else {
            if (mOldX > playerWidth * 3.0 / 5) {// 音量
                gesture_volume_layout.setVisibility(View.VISIBLE);
                gesture_bright_layout.setVisibility(View.GONE);
                gesture_progress_layout.setVisibility(View.GONE);
                GESTURE_FLAG = GESTURE_MODIFY_VOLUME;
            } else if (mOldX < playerWidth * 2.0 / 5) {// 亮度
                gesture_bright_layout.setVisibility(View.VISIBLE);
                gesture_volume_layout.setVisibility(View.GONE);
                gesture_progress_layout.setVisibility(View.GONE);
                GESTURE_FLAG = GESTURE_MODIFY_BRIGHT;
            }
        }
    }

    // 假設每次觸控螢幕後第一次scroll是調節進度。那之後的scroll事件都處理音量進度,直到離開螢幕執行下一次操作
    if (GESTURE_FLAG == GESTURE_MODIFY_PROGRESS) {
        // distanceX=lastScrollPositionX-currentScrollPositionX,因此為正時是快進
        if (Math.abs(distanceX) > Math.abs(distanceY)) {// 橫向移動大於縱向移動
            if (distanceX >= DensityUtil.dip2px(this, STEP_PROGRESS)) {// 快退,用步長控制改變速度。可微調
                gesture_iv_progress.setImageResource(R.drawable.souhu_player_backward);
                if (playingTime > 3) {// 避免為負
                    playingTime -= 3;// scroll方法執行一次快退3秒
                } else {
                    playingTime = 0;
                }
            } else if (distanceX <= -DensityUtil.dip2px(this, STEP_PROGRESS)) {// 快進
                gesture_iv_progress.setImageResource(R.drawable.souhu_player_forward);
                if (playingTime < videoTotalTime - 16) {// 避免超過總時長
                    playingTime += 3;// scroll執行一次快進3秒
                } else {
                    playingTime = videoTotalTime - 10;
                }
            }
            if (playingTime < 0) {
                playingTime = 0;
            }
            tv_pro_play.seekTo(playingTime);
            geture_tv_progress_time.setText(DateTools.getTimeStr(playingTime) + "/" + DateTools.getTimeStr(videoTotalTime));
        }
    }

    // 假設每次觸控螢幕後第一次scroll是調節音量,那之後的scroll事件都處理音量調節。直到離開螢幕執行下一次操作
    else if (GESTURE_FLAG == GESTURE_MODIFY_VOLUME) {
        currentVolume = audiomanager.getStreamVolume(AudioManager.STREAM_MUSIC); // 獲取當前值
        if (Math.abs(distanceY) > Math.abs(distanceX)) {// 縱向移動大於橫向移動
            if (distanceY >= DensityUtil.dip2px(this, STEP_VOLUME)) {// 音量調大,注意橫屏時的座標體系,雖然左上角是原點,但橫向向上滑動時distanceY為正
                if (currentVolume < maxVolume) {// 為避免調節過快,distanceY應大於一個設定值
                    currentVolume++;
                }
                gesture_iv_player_volume.setImageResource(R.drawable.souhu_player_volume);
            } else if (distanceY <= -DensityUtil.dip2px(this, STEP_VOLUME)) {// 音量調小
                if (currentVolume > 0) {
                    currentVolume--;
                    if (currentVolume == 0) {// 靜音,設定靜音獨有的圖片
                        gesture_iv_player_volume.setImageResource(R.drawable.souhu_player_silence);
                    }
                }
            }
            int percentage = (currentVolume * 100) / maxVolume;
            geture_tv_volume_percentage.setText(percentage + "%");
            audiomanager.setStreamVolume(AudioManager.STREAM_MUSIC,currentVolume, 0);
        }
    }

    // 假設每次觸控螢幕後第一次scroll是調節亮度,那之後的scroll事件都處理亮度調節。直到離開螢幕執行下一次操作
    else if (GESTURE_FLAG == GESTURE_MODIFY_BRIGHT) {
        gesture_iv_player_bright.setImageResource(R.drawable.souhu_player_bright);
        if (mBrightness < 0) {
            mBrightness = getWindow().getAttributes().screenBrightness;
            if (mBrightness <= 0.00f)
                mBrightness = 0.50f;
            if (mBrightness < 0.01f)
                mBrightness = 0.01f;
        }
        WindowManager.LayoutParams lpa = getWindow().getAttributes();
        lpa.screenBrightness = mBrightness + (mOldY - y) / playerHeight;
        if (lpa.screenBrightness > 1.0f)
            lpa.screenBrightness = 1.0f;
        else if (lpa.screenBrightness < 0.01f)
            lpa.screenBrightness = 0.01f;
        getWindow().setAttributes(lpa);
        geture_tv_bright_percentage.setText((int) (lpa.screenBrightness * 100) + "%");
    }

    firstScroll = false;// 第一次scroll執行完畢,改動標誌
    return false;
}

這種話,我個人覺得完美了…不完美之處還望各位大牛多多提醒,本人隨時隨地進行接納…哈哈,謝謝咯

演示樣例程式碼

相關文章