(Original) Gesture control tools for video players

Recently working on a video player

Now on the market, a relatively complete video player

Probably has the following functions:

Fast forward, fast reverse, sound, brightness control

This time based on these basic functions

Through the system's gesture control class GestureDetector to complete

Made a practical tool class

Only need simple configuration

You can monitor the gestures of the video player controls

Relevant functions are implemented in the tool class, no need for the video player to write by itself

Realizes code decoupling and facilitates reuse

 

Now let's introduce the use of this tool class and the principle of implementation.

Paste the actual code of the tool class first

public class VideoGestureControlUtil {

    private static final String TAG = "VideoGestureControlUtil";
    private Context mContext;
    private GestureDetector mGestureDetector;//手势类
    private MyGestureListener myGestureListener;//手势监听
    private OnVideoControlListener videoControlListener;//视频View回调监听
    private Rect VideoViewRect = null;//视频控件范围

    //状态相关
    private static int SCROLL_FLAG = 0;//记录状态
    private static final int SCROLL_FLAG_RESET = 0;//初始状态,无任何操作
    private static final int SCROLL_FLAG_TOPBOTTOM_LEFT = 1;//左边屏幕上下滑动
    private static final int SCROLL_FLAG_TOPBOTTOM_RIGHT = 2;//右边屏幕上下滑动
    private static final int SCROLL_FLAG_LEFTRIGHT = 3;//左右滑动


    //拖动相关
    protected static final float FLIP_DISTANCE = 50;//确定滑动方向的最小滑动距离
    private int SCROLL_VIDEO_SCROLL_RANGE = 1000;//拖动范围 0~1000
    private float SCROLL_VIDEO_PLAY_RANGE = 0.25f;//视频可拖动部分的范围

    //视频进度相关
    private float SCROLL_VIDEO_PLAY_INDEX = 0f;//拖动的视频进度
    private double videoIndex = 0;//拖动的视频毫秒数
    private double newVideoIndex;//拖动结束后视频的位置,单位毫秒
    private double SCROLL_VIDEO_PLAYING_INDEX = 0;//视频播放位置
    private double SCROLL_VIDEO_PLAYING_INDEX_CATCH = 0;//缓存的视频播放位置(手指按下去的视频播放位置)
    private double SCROLL_VIDEO_LENTH = 0;//视频总长度 单位毫秒

    //声音和亮度相关
    private AudioManager systemService;
    private int SCROLL_VIDEO_VOICE_INDEX = 0;//拖动的视频声音
    private double SCROLL_VIDEO_LIGHT_INDEX = 0;//拖动的视频亮度
    private int SCROLL_VOICE_MAX = 0;//声音总长度
    private int SCROLL_LIGHT_MAX = 255;//亮度总长度
    private int SCREEN_LIGHT = 0;//屏幕亮度
    private int SCREEN_LIGHT_CATCH = 0;//缓存的屏幕亮度
    private int SCREEN_VOICE = 0;//音量大小

    //弹框部分
    private Dialog indexDialog;//弹框
    private View videoDialogView;//弹框View
    private ImageView indeximg;
    private LinearLayout indexll, voicell;
    private TextView indextv;
    private ImageView voiceLightImg;
    private SeekBar voiceLightsb;
    private WindowManager.LayoutParams indexDialogLp;
    private DisplayMetrics displayMetrics;


    public VideoGestureControlUtil(Context context, View view) {
        myGestureListener = new MyGestureListener();
        this.mContext = context;
        systemService = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
        SCROLL_VOICE_MAX = systemService.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        mGestureDetector = new GestureDetector(context, myGestureListener);
        mGestureDetector.setOnDoubleTapListener(myGestureListener);

        LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        videoDialogView = layoutInflater.inflate(R.layout.video_gesture_dialog, null);
        indexll = videoDialogView.findViewById(R.id.indexll);
        voicell = videoDialogView.findViewById(R.id.voicell);
        indeximg = videoDialogView.findViewById(R.id.indeximg);
        indextv = videoDialogView.findViewById(R.id.indextv);
        voiceLightImg = videoDialogView.findViewById(R.id.voice_light_img);
        voiceLightsb = videoDialogView.findViewById(R.id.voice_light_sb);


        indexDialog = new Dialog(context);

        /*随意定义个Dialog*/
        Window dialogWindow = indexDialog.getWindow();
        /*实例化Window*/
        indexDialogLp = dialogWindow.getAttributes();
        /*实例化Window操作者*/
        indexDialogLp.x = 0; // 新位置X坐标
        indexDialogLp.y = 0; // 新位置Y坐标
        dialogWindow.setGravity(Gravity.CENTER);
        dialogWindow.getDecorView().setBackground(null);
        dialogWindow.setAttributes(indexDialogLp);

        /*放置属性*/
        indexDialog.setContentView(videoDialogView, new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT));
        dialogWindow.setDimAmount(0);
        dialogWindow.setBackgroundDrawableResource(android.R.color.transparent);

        try {
            int dividerID = indexDialog.getContext().getResources().getIdentifier("android:id/titleDivider", null, null);
            View divider = indexDialog.findViewById(dividerID);
            if (divider != null) {
                divider.setBackgroundColor(Color.TRANSPARENT);
            }
        } catch (Exception e) {
            //上面的代码,是用来去除Holo主题的蓝色线条
            e.printStackTrace();
        }
        indexDialog.setCanceledOnTouchOutside(false);

    }


    public boolean touch(MotionEvent event) {

        boolean detectedUp = event.getAction() == MotionEvent.ACTION_UP;
        if (!mGestureDetector.onTouchEvent(event) && detectedUp) {
            //手指抬起时触发
            if (SCROLL_FLAG == SCROLL_FLAG_LEFTRIGHT) {
                //设置当前播放进度
                videoControlListener.setScrollVideoPlayingIndex(SCROLL_VIDEO_PLAYING_INDEX);
            }
            SCROLL_FLAG = SCROLL_FLAG_RESET;
            VideoViewRect = null;
            dismissIndexDialog();
        } else if (event.getAction() == MotionEvent.ACTION_CANCEL) {
            //熄屏时会触发
            SCROLL_FLAG = SCROLL_FLAG_RESET;
            VideoViewRect = null;
            dismissIndexDialog();
        }
        return true;

    }

    /*
     * 手势监听类
     */
    class MyGestureListener extends GestureDetector.SimpleOnGestureListener {

        public MyGestureListener() {
            super();
        }

        @Override
        public boolean onDoubleTap(MotionEvent e) {
            Log.e(TAG, "双击");
            videoControlListener.onDoubleTap();
            return true;
        }

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            Log.e(TAG, "单击");
            videoControlListener.onSingleTap();
            return true;
        }

        @Override
        public boolean onDoubleTapEvent(MotionEvent e) {
            Log.e(TAG, "onDoubleTapEvent");
            return true;
        }


        @Override
        public boolean onContextClick(MotionEvent e) {
            Log.e(TAG, "onContextClick");
            return true;
        }

        @Override
        public boolean onDown(MotionEvent e) {
            Log.e(TAG, "onDown");
            return true;
        }

        @Override
        public void onShowPress(MotionEvent e) {
            Log.e(TAG, "onShowPress");
        }

        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            Log.e(TAG, "onSingleTapUp");
            return true;
        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            switch (SCROLL_FLAG) {
                case SCROLL_FLAG_RESET:
                    WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
                    displayMetrics = new DisplayMetrics();
                    windowManager.getDefaultDisplay().getRealMetrics(displayMetrics);


                    if (e1.getX() < FLIP_DISTANCE || e1.getX() > displayMetrics.widthPixels - FLIP_DISTANCE || e1.getY() > displayMetrics.heightPixels - FLIP_DISTANCE) {
                        Log.d(TAG, "onScroll: 无效动作" + e1.getX());
                        //这里主要是处理屏幕边缘滑动的问题,防止和边缘的滑动返回等操作冲突,所以屏蔽了边缘的部分滑动。
                        return true;
                    }

                    if (VideoViewRect == null) {
                        VideoViewRect = videoControlListener.getVideoViewRect();

                        Log.d(TAG, "高度: " + displayMetrics.heightPixels);
                        Log.d(TAG, "宽度: " + displayMetrics.widthPixels);


                        if (VideoViewRect != null) {
                            Log.d(TAG, "left: " + VideoViewRect.left);
                            Log.d(TAG, "right: " + VideoViewRect.right);

                            Log.d(TAG, "top: " + VideoViewRect.top);
                            Log.d(TAG, "bottom: " + VideoViewRect.bottom);


                            indexDialogLp.x = ((VideoViewRect.right - VideoViewRect.left) / 2) - (displayMetrics.widthPixels / 2);
                            indexDialogLp.y = ((VideoViewRect.bottom - VideoViewRect.top) / 2) - (displayMetrics.heightPixels / 2) + VideoViewRect.top;
                        }

                    }
                    //初始化,没有滑动方向
                    if (e1.getX() - e2.getX() > FLIP_DISTANCE || e2.getX() - e1.getX() > FLIP_DISTANCE) {
                        Log.i(TAG, "向左右滑...");
                        SCROLL_FLAG = SCROLL_FLAG_LEFTRIGHT;
                        SCROLL_VIDEO_PLAY_INDEX = 0f;

                        videoIndex = 0;

                        //得到视频长度和播放位置
                        SCROLL_VIDEO_LENTH = videoControlListener.getScrollVideoLenth();
                        SCROLL_VIDEO_PLAYING_INDEX_CATCH = SCROLL_VIDEO_PLAYING_INDEX = videoControlListener.getScrollVideoPlayingIndex();


                        indexll.setVisibility(View.VISIBLE);
                        voicell.setVisibility(View.GONE);


                        showIndexDialog();

                        return true;
                    } else if (e1.getY() - e2.getY() > FLIP_DISTANCE || e2.getY() - e1.getY() > FLIP_DISTANCE) {


                        if (e1.getX() < (displayMetrics.widthPixels / 2)) {
                            //左边上下滑动滑动
                            SCROLL_FLAG = SCROLL_FLAG_TOPBOTTOM_LEFT;
                            Log.i(TAG, "左屏幕向上下滑...");
                            SCROLL_VIDEO_LIGHT_INDEX = 0;
                            voiceLightImg.setImageResource(R.drawable.video_lightimg);
                            try {
                                //当前屏幕亮度
                                SCREEN_LIGHT_CATCH = SCREEN_LIGHT = Settings.System.getInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS);
                                voiceLightsb.setMax(SCROLL_LIGHT_MAX);
                                voiceLightsb.setProgress(SCREEN_LIGHT);
                            } catch (Settings.SettingNotFoundException e) {
                                e.printStackTrace();
                            }
                        } else {
                            //右边上下滑动
                            SCROLL_FLAG = SCROLL_FLAG_TOPBOTTOM_RIGHT;
                            Log.i(TAG, "右屏幕向上下滑...");
                            SCROLL_VIDEO_VOICE_INDEX = 0;
                            //当前声音大小
                            SCREEN_VOICE = systemService.getStreamVolume(AudioManager.STREAM_MUSIC);
                            voiceLightImg.setImageResource(R.drawable.video_voiceimg);
                            voiceLightsb.setMax(SCROLL_VOICE_MAX);
                            voiceLightsb.setProgress(SCREEN_VOICE);
                        }

                        indexll.setVisibility(View.GONE);
                        voicell.setVisibility(View.VISIBLE);
                        showIndexDialog();

                        return true;
                    }
                    break;
                case SCROLL_FLAG_TOPBOTTOM_LEFT:
                    //设置亮度
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        if (!Settings.System.canWrite(mContext)) {
                            Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
                            intent.setData(Uri.parse("package:" + mContext.getPackageName()));
                            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                            mContext.startActivity(intent);
                        } else {
                            //左屏幕上下滑动
                            SCROLL_VIDEO_LIGHT_INDEX += distanceY;
                            Log.e(TAG, "toponScroll:" + SCROLL_VIDEO_LIGHT_INDEX);
                            double lightIndex = (((double) SCROLL_VIDEO_LIGHT_INDEX / (double) SCROLL_VIDEO_SCROLL_RANGE)) * SCROLL_LIGHT_MAX;
//                            lightIndex = -lightIndex;//取反


                            double newLightIndex = lightIndex + SCREEN_LIGHT_CATCH;

                            if (newLightIndex > SCROLL_LIGHT_MAX) {
                                //说明拖动到末尾
                                SCREEN_LIGHT = SCROLL_LIGHT_MAX;

                            } else if (newLightIndex < 0) {
                                //说明拖动到开头
                                SCREEN_LIGHT = 0;
                            } else {
                                SCREEN_LIGHT = (int) newLightIndex;
                            }
                            // 申请权限后做的操作
                            // 设置系统亮度
                            Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, SCREEN_LIGHT);
                            Log.d(TAG, "屏幕亮度: " + SCREEN_LIGHT);
                            voiceLightsb.setProgress(SCREEN_LIGHT);

                        }
                    }


                    return true;
//                    break;
                case SCROLL_FLAG_TOPBOTTOM_RIGHT:
                    //右屏幕上下滑动 设置声音
                    SCROLL_VIDEO_VOICE_INDEX += distanceY;
//                    SCROLL_VIDEO_VOICE_INDEX = (int) distanceY;
                    double voiceIndex = (((double) SCROLL_VIDEO_VOICE_INDEX / (double) SCROLL_VIDEO_SCROLL_RANGE)) * SCROLL_VOICE_MAX;
//                            lightIndex = -lightIndex;//取反

                    if (voiceIndex > 1) {
                        voiceIndex = 1;
                        SCROLL_VIDEO_VOICE_INDEX = 0;

                    } else if (voiceIndex < -1) {
                        voiceIndex = -1;
                        SCROLL_VIDEO_VOICE_INDEX = 0;

                    } else {
                        voiceIndex = 0;
                    }
                    Log.d(TAG, "voiceIndex: " + voiceIndex);

                    double newVoiceIndex = voiceIndex + SCREEN_VOICE;


                    if (newVoiceIndex > SCROLL_VOICE_MAX) {
                        //说明拖动到末尾
                        SCREEN_VOICE = SCROLL_VOICE_MAX;

                    } else if (newVoiceIndex < 0) {
                        //说明拖动到开头
                        SCREEN_VOICE = 0;
                    } else {
                        SCREEN_VOICE = (int) newVoiceIndex;
                    }


                    Log.d(TAG, "结束声音大小: " + SCREEN_VOICE);
                    systemService.setStreamVolume(AudioManager.STREAM_MUSIC, SCREEN_VOICE, AudioManager.FLAG_PLAY_SOUND);
                    voiceLightsb.setProgress(SCREEN_VOICE);

                    return true;
//                    break;
                case SCROLL_FLAG_LEFTRIGHT:
                    //左右滑动
                    if (Math.abs(distanceY) > 1) {
                        break;
                    }
                    SCROLL_VIDEO_PLAY_INDEX += distanceX;

                    //得到当前视频进度
                    videoIndex = (((double) SCROLL_VIDEO_PLAY_INDEX / (double) SCROLL_VIDEO_SCROLL_RANGE)) * (SCROLL_VIDEO_LENTH * SCROLL_VIDEO_PLAY_RANGE);


                    //说明是进度滑动
                    videoIndex = -videoIndex;//取反

                    newVideoIndex = videoIndex + SCROLL_VIDEO_PLAYING_INDEX_CATCH;
//                    Log.d("print", "videoIndex: " + videoIndex + "-----newVideoIndex:" + newVideoIndex);
                    if (newVideoIndex > SCROLL_VIDEO_LENTH) {
                        //说明拖动到末尾
                        SCROLL_VIDEO_PLAYING_INDEX = SCROLL_VIDEO_LENTH;

                    } else if (newVideoIndex < 0) {
                        //说明拖动到开头
                        SCROLL_VIDEO_PLAYING_INDEX = 0;
                    } else {
                        SCROLL_VIDEO_PLAYING_INDEX = newVideoIndex;
                    }
                    if ((videoIndex / 1000) > 0f) {
                        //快进
                        indeximg.setImageResource(R.drawable.indeximg_left);
                    } else {
                        //快退
                        indeximg.setImageResource(R.drawable.indeximg_right);
                    }
//                    Log.d("print", "滑动结束,拖动进度为" + videoIndex / 1000 + "秒");
//                    Log.d("print", "滑动结束,当前进度为" + SCROLL_VIDEO_PLAYING_INDEX / 1000 + "秒");
                    indextv.setText(parse2TimeStr(SCROLL_VIDEO_PLAYING_INDEX) + "/" + parse2TimeStr(SCROLL_VIDEO_LENTH));
                    return true;
//                    break;
            }

            return false;
        }

        @Override
        public void onLongPress(MotionEvent e) {
            Log.e(TAG, "onLongPress");
        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            Log.e(TAG, "onFling");
            return false;
        }
    }

    private void showIndexDialog() {

        indexDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
        indexDialog.show();
    }

    private void dismissIndexDialog() {

        if (indexDialog != null) {
            indexDialog.dismiss();
        }
    }


    public interface OnVideoControlListener {

        //得到视频总长度
        double getScrollVideoLenth();

        //得到视频当前播放位置
        double getScrollVideoPlayingIndex();

        //设置视频当前播放位置
        void setScrollVideoPlayingIndex(double playIndex);

        //双击屏幕
        void onDoubleTap();

        //单击屏幕
        void onSingleTap();

        //得到视频控件的大小范围
        Rect getVideoViewRect();

    }

    public void setOnVideoControlListener(OnVideoControlListener videoControlListener) {
        this.videoControlListener = videoControlListener;
    }

    private String parse2TimeStr(double timeStr) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss");
        simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT+00:00"));
        if (timeStr / 1000 > 3600) {
            //说明超过一小时
            simpleDateFormat.applyPattern("HH:mm:ss");
        } else {
            //说明没超过一小时
            simpleDateFormat.applyPattern("mm:ss");
        }
        return simpleDateFormat.format(timeStr);
    }


}

It is actually very simple to use, we have customized a control MyVideoView here

Suppose this MyVideoView is our video control

So how to make this control use our tool class

So as to realize the function of gesture control playback progress, sound and brightness?

Look at the code below

public class MyVideoView extends View {

    private VideoGestureControlUtil videoGestureControlUtil;
    private double playIndex = 0;//视频当前播放位置


    public MyVideoView(Context context) {
        this(context, null);
    }

    public MyVideoView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyVideoView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context) {
        videoGestureControlUtil = new VideoGestureControlUtil(context, this);
        videoGestureControlUtil.setOnVideoControlListener(new VideoGestureControlUtil.OnVideoControlListener() {
            @Override
            public double getScrollVideoLenth() {
                return 36 * 60 * 1000;//36分钟的视频
            }

            @Override
            public double getScrollVideoPlayingIndex() {
                return playIndex;
            }

            @Override
            public void setScrollVideoPlayingIndex(double playIndex) {
                MyVideoView.this.playIndex = playIndex;
            }

            @Override
            public void onDoubleTap() {
                Log.d("print", "onSingleTap: 双击");
            }

            @Override
            public void onSingleTap() {
                Log.d("print", "onSingleTap: 单击");
            }

            @Override
            public Rect getVideoViewRect() {
                Rect rect = new Rect();
                boolean localVisibleRect = getGlobalVisibleRect(rect);
                if (localVisibleRect) {
                    return rect;
                }
                return null;
            }
        });


    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return videoGestureControlUtil.touch(event);
    }
}

You can see that the implementation is actually very simple

The core is in onTouchEvent

Feedback the touch monitoring of the control to this tool class

Need to implement five interfaces at the same time

These five interfaces

Need to return the length of the video, the current playback progress

After dragging at the same time, it will return to the current progress

Single click and double click monitoring have also been implemented

Here we need to focus on the getVideoViewRect method

This method is used to return the position of the control on the screen

For details, please refer to this blog

(Reprinted) Android: 6 ways to get the coordinate position of View efficiently & correctly

Why do you need a control to pass the position?

That’s because in our tool class

The playback progress and brightness when dragging with the finger, and the pop-up window when the sound changes have been realized

Need to adjust the position of this pop-up window according to the user's position

Of course, you can also remove this function by commenting out the dialog code of the tool class.

For how to set the position of the dialog pop-up window, please refer to this blog or Baidu search.

(Reproduced) Android settings on the display position of the dialog

In this way, this video player realizes the function of controlling playback progress, brightness, sound, and single click and double click through gestures

 

Finished talking about basic use

Let me talk about the principle

The first is the monitoring of gestures

You can refer to this blog

(Reproduced) Detailed Android GestureDetector

Next, analyze the specific implementation in our tool class

 

The parse2TimeStr method is mainly to format the time

When the pop-up window is convenient, the text display of the playback progress is consistent

 

The touch method is mainly used to monitor the finger up (MotionEvent.ACTION_UP)

Or when the finger is pressed and the screen is stopped (MotionEvent.ACTION_CANCEL)

At this time, you need to close the pop-up window and go to the callback method according to the drag distance

Tell the video player the playback progress of the current drag

 

The core listener MyGestureListener class

This is the place to deal with double-click, click, and drag

Note that the drag is divided into different states here

Respectively are

SCROLL_FLAG_RESET initial state, which is the state where the finger is not pressed

SCROLL_FLAG_TOPBOTTOM_LEFT Slide up and down on the left screen to control the brightness

SCROLL_FLAG_TOPBOTTOM_RIGHT Slide up and down on the right screen to control the sound

SCROLL_FLAG_LEFTRIGHT slide left and right to control the playback progress

The state will be judged when the finger is dragged, and the state will be restored after lifting

Also note this code

if (e1.getX() < FLIP_DISTANCE || e1.getX() > displayMetrics.widthPixels - FLIP_DISTANCE || e1.getY() > displayMetrics.heightPixels - FLIP_DISTANCE) {
                        Log.d(TAG, "onScroll: 无效动作" + e1.getX());
                        //这里主要是处理屏幕边缘滑动的问题,防止和边缘的滑动返回等操作冲突,所以屏蔽了边缘的部分滑动。
                        return true;
                    }

Mainly to prevent dragging at the edge of the screen (such as the bottom or both sides), you can comment out when you don’t need it

The location setting of dialog is mainly the following code, which can be modified according to actual needs

indexDialogLp.x = ((VideoViewRect.right - VideoViewRect.left) / 2) - (displayMetrics.widthPixels / 2);
indexDialogLp.y = ((VideoViewRect.bottom - VideoViewRect.top) / 2) - (displayMetrics.heightPixels / 2) + VideoViewRect.top;

 

Finally, because the pop-up window needs some pictures and so on, they are also uploaded here

Download it in the network disk

link:

https://pan.baidu.com/s/1_E8IeJhXz2jTwdigLGgkaA 
Extraction code:

kkzu

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Guess you like

Origin blog.csdn.net/Android_xiong_st/article/details/113116891