imcys.com
遵从中二的召唤,来吧少年!

饺子播放器实现抖音点击布局暂停及双击效果

前言

前面我提到了这个抖音的视频滚动切换实现方案,在文章最后提到了需要对这个饺子播放器进行修改。

饺子播放器自带的一些东西并不能满足我们的需求,那么这个时间我们就需要继承饺子播放器去做一些自定义的操作,来实现模仿抖音的这种效果。

明确需求

我们注意到抖音是可以点击视频页面任何一个部位使得视频暂停,以及切换视图后视频暂停,但是保留视频播放位置,当返回后继续播放,并且抖音还有双击支持,双击后对视频进行点赞,这个后期也需要实现。

  • 点击界面使视频暂停
  • 切换视图保留视频播放位置
  • 保留双击操作

OK,以上是我们需要继承和实现的功能。

具体实现

首先我们需要去做点击视频界面就使得视频暂停,而且不去影响我们的视频进度切换,和暂停点击事件。

那么这个情况就比较有意思了,我们先来看看饺子播放器的基础布局。

我们来看看JzvdStd这个类,我们发现它还继承了一个类,当然这是必然,继承了Jzvd这个抽象类,当然它的父类暂时不是重点,我们翻阅代码,能发现这样的方法——getLayoutId,在Jzvd,这是个抽象方法,这个方法里边return了一个布局,也就是R.layout.jz_layout_std

学习到小细节

这里有个小细节,JzvdStd不是抽象类,但是它有了父类的抽象方法,这个是不行的,所以JzvdStd必须覆盖这个方法。(现学现卖,我对于抽象类的了解甚少。)我很好奇接口类的方法集合都需要被实现,那么这种抽象类的抽象方法又是个什么情况呢?

一篇文章告诉了我->Java中abstract的基本使用与详解,大家有兴趣了可以自己看看,前提是你也不太清楚这个抽象类。写这里也是给自己记录一下,如果以后忘记了就回来看看。

这个布局代码太多了,就不贴了,放个图片吧

简单的观察这个布局,发现如果要点击布局的任何一个部分来让视频暂停,我们只能依赖这个FrameLayout控件,OK,那么我们就对这个东西改一改。

继承饺子播放器

为了去更好的自定义,我们得去继承下JzvdStd,方便我们后期改动,推荐大家去把这个布局搬到自己的项目里,后面可以改布局。记得重写getLayoutId

那么我们想一想,继承了要做什么?当然是要设置下这个铺满全界面FrameLayout的点击事件,但是对于饺子播放器肯定是有自己的实现方案的,因为什么,对于初始化好的视频而言,需要我们点击播放,这个点击是点击哪里都可以的,所以我们得看看饺子播放器的代码。

首当其冲的就是检查下JzvdStd,我们看看,最先被我考虑到的就是onClick,我们来看看这个代码。

    @Override
    public void onClick(View v) {
        super.onClick(v);
        int i = v.getId();
        if (i == R.id.poster) {
            clickPoster();
        } else if (i == R.id.surface_container) {
            clickSurfaceContainer();
            if (clarityPopWindow != null) {
                clarityPopWindow.dismiss();
            }
        } else if (i == R.id.back) {
            clickBack();
        } else if (i == R.id.back_tiny) {
            clickBackTiny();
        } else if (i == R.id.clarity) {
            clickClarity();
        } else if (i == R.id.retry_btn) {
            clickRetryBtn();
        }
    }

选读:并不会影响核心内容

我们仔细阅读一下,发现当视频初始化后,点击任意位置可以播放是因为clickPoster这个方法,出于好奇心,我看了下这个东西为什么可以使得视频播放。

化繁为简,第一个判断看起来是判断这个地址或者说资源是不是为空,第二个判断首先是判断了下当前状态是不是正常的,也就是准备就绪状态,然后里边的判断可能是判断现在是不是WiFi啥的吧,不太清楚,但是它执行了startVideo,这个就是播放视频的方法,再上一篇文档应该已经出现过了。


我们注意到R.id.surface_container,这个正好是,FrameLayout的ID,那么我们仔细看看,从里边的方法来看,clickSurfaceContainer,是现实播放器的这个界面展示的,如果你不点击屏幕,会隐藏一些东西,点击后就显示一会后消失。

那么我们在这里插入,如果当前视频是播放状态就把视频暂停了,好,想法很不错,但是有个问题,我们怎么知道现在的状态哪里来,假设你没有看上面的选读,是不是还得找?我们现在来看看,视频需要点击播放和暂停,这玩意怎么实现?我们是不是可以猜测,播放或者暂停的那里会判断播放状态。有了这个想法我们就去找找。

这里我们可以看到播放按钮的ID,那么就找找看R.id.start,显然JzvdStd搜索无结果,那么我们来看看它继承的类,Jzvd,我们在Jzvd里找到了它。控件变量是startButton,可以发现,它把点击监听器也是设置在了类里。

我们直接来看看代码,发现clickStart方法,去看看,这个大家如果用都有,我就不贴了。

发现里边有state变量在比较,通过代码我们发现STATE_PAUSE就是暂停状态,STATE_PLAYING是播放状态,先不着急,我们发现如果是播放状态点击播放按钮就会暂停视频,同时显示暂停UI。

            mediaInterface.pause();
            onStatePause();

现在判断状态有了,而且暂停视频的方法也有了(我不知道饺子播放器带了暂停方法没有,我只找到了释放全部视频的方法,如果大家找到了请留言一下。)

现在我们需要创建一个类来继承JzvdStd,我们把onClick覆写进来。

    @Override
    public void onClick(View v) {
        super.onClick(v);
        int i = v.getId();
        if (i == R.id.poster) {
            clickPoster();
        } else if (i == R.id.surface_container) {

            if (state == STATE_PLAYING) {
                //暂停视频
                mediaInterface.pause();
                //更新状态
                state = STATE_PAUSE;
            }

            clickSurfaceContainer();
            if (clarityPopWindow != null) {
                clarityPopWindow.dismiss();
            }


        } else if (i == R.id.back) {
            clickBack();
        } else if (i == R.id.back_tiny) {
            clickBackTiny();
        } else if (i == R.id.clarity) {
            clickClarity();
        } else if (i == R.id.retry_btn) {
            clickRetryBtn();
        }

    }


如果你和我一样也是初学者,肯定好奇,为什么只贴了这一个,我之前去网上查资料也就困惑住了

public class JZPlay extends JzvdStd

这个方法是在上面这样的类里的,它需要实现一些接口,你按快捷键实现就可以。

那么我们来看看上面代码,我们加入了一段判断,首先确定用户是不是点击了FrameLayout,如果是就判断播放状态,如果正在播放,就执行暂停,这样就实现了点击任意位置暂停视频。而且不影响其他UI功能。

现在我们运行,但是有个新问题了,我尝试双击,发现有卡顿了,我突然意识到饺子播放器默认双击是会暂停,我当时就麻了,但是不能被这个问题打倒,当然如果你不需要双击事件问题不大,但是我们这里是需要实现的,现在来看看接下来怎么解决。

单双击监听

通过不断检查,发现JzvdStd里有一个手势监听器。onDoubleTap双击监听,下面一个单击监听。

现在好了,我们只需要把上面的代码办下来放这里就行了,当我正想去我们继承的类里覆盖这个变量时发现出大问题,protected修饰后不能在不同包的子类里继承,这就尴尬了呀,当我正准备换其他方案时我发现下面有一个事件onTouch,这个里边有一行代码。

gestureDetector.onTouchEvent(event);

我发现了个新办法,这个手势监听器被保护了无法覆写,但是我们可以覆写onTouch,onTouchEvent,这个是安卓事件分发机制里的一个东西,我之前看过一点,但是没怎么弄懂,这里就不阐述了。

那么我们来看看新的代码

 /**
     * 双击
     * TODO 这里搬了过来,它的父类把这个变量保护了起来,导致无法去自定义点击事件,但是我吗需要自定义,就必须把他搬下来
     */
    private GestureDetector myGestureDetector = new GestureDetector(getContext().getApplicationContext(), new GestureDetector.SimpleOnGestureListener() {
        @Override
        public boolean onDoubleTap(MotionEvent e) {
            if (state == STATE_PLAYING || state == STATE_PAUSE) {
                Log.d(TAG, "doublClick [" + this.hashCode() + "] ");
                startButton.performClick();

            }
            return super.onDoubleTap(e);
        }

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            if (!mChangePosition && !mChangeVolume) {
                //这个东西不能放在FrameLayout点击开机
                if (state == STATE_PLAYING) {
                    //暂停视频
                    mediaInterface.pause();
                    //更新状态
                    state = STATE_PAUSE;
                }
                onClickUiToggle();
            }
            return super.onSingleTapConfirmed(e);
        }

        @Override
        public void onLongPress(MotionEvent e) {
            super.onLongPress(e);
        }
    });



    //同理,因为被保护,这里得改下触发器的变量名使其使用我们自定义的
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        int id = v.getId();

        if (id == R.id.surface_container) {

            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                case MotionEvent.ACTION_MOVE:
                    break;
                case MotionEvent.ACTION_UP:
                    startDismissControlViewTimer();
                    if (mChangePosition) {
                        long duration = getDuration();
                        int progress = (int) (mSeekTimePosition * 100 / (duration == 0 ? 1 : duration));
                        bottomProgressBar.setProgress(progress);
                    }
                    break;
            }
            myGestureDetector.onTouchEvent(event);
        } else if (id == R.id.bottom_seek_progress) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    cancelDismissControlViewTimer();
                    break;
                case MotionEvent.ACTION_UP:
                    startDismissControlViewTimer();
                    break;
            }
        }
        //TODO 将触摸消息拦截下来 不向下传递了,如果不这样做父类会执行它的点击事件,这样就冲突了
        return false;
    }

但是我们没有照搬,首先我们把前面的代码换到onSingleTapConfirmed里,这样双击事件也就能执行了。

同时我们的return直接返回了flase,而不是super.onTouch(v, event);,因为现在它的父类也有onTouch,照抄会出现一个问题,那就是父类似乎执行了同样的代码,这样就有问题了,但是现在这样改虽然使得点击事件触控都正常了,但是这样可能导致Jzvd收不到,导致一些滑动操作挂了,这个我没有测试,不敢断言,大家可以自行测试,这样单击和双击功能就兼容了。

我现在还没有测试这个问题,下来我会使用手机单独打包测试下,填这个坑。

后期实现

现在仅仅是实现兼容了单机和双击行为,后期还得解决一下切换视图暂停视频,切换回来继续播放的功能,这个下次讲吧很晚了。

萌新杰少

文章作者

I im CYS,一个热爱二次元的高中生开发者

发表评论

textsms
account_circle
email

Captcha Code

萌新杰少の秘密基地

饺子播放器实现抖音点击布局暂停及双击效果
前言 前面我提到了这个抖音的视频滚动切换实现方案,在文章最后提到了需要对这个饺子播放器进行修改。 饺子播放器自带的一些东西并不能满足我们的需求,那么这个时间我们就需要继承…
扫描二维码继续阅读
2022-07-26