抖音视频滚动切换实现方案:继承饺子播放器进行自定义操作

[复制链接]
查看202 | 回复0 | 2024-8-25 01:03:07 | 显示全部楼层 |阅读模式
序言

后面我提及了这个抖音的视频滚动切换实现方案,在文章最后提及了须要对这个汤圆播放器进行更改。

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

明晰需求

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

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

具体实现

首先我们须要去做点击视频界面就促使视频暂停,但是不去影响我们的视频进度切换,和暂停点击风波。

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

我们来瞧瞧JzvdStd这个类,我们发觉它还承继了一个类,其实这是必然,承继了Jzvd这个具象类,其实它的父类暂时不是重点,我们翻阅代码,能发觉这样的方式——getLayoutId,在Jzvd,这是个具象方式,这个方式上面return了一个布局,也就是R.layout.jz_layout_std。

学习到小细节

这儿有个小细节,JzvdStd不是具象类,并且它有了父类的具象方式,这个是不行的,所以JzvdStd必须覆盖这个技巧。(现学现卖,我对于具象类的了解很少。)我很好奇插口类的方式集合都须要被实现,这么这些具象类的具象方式又是个哪些情况呢?

一篇文章告诉了我->Java中abstract的基本使用与解读,你们有兴趣了可以自己瞧瞧,前提是你也不太清楚这个具象类。写这儿也是给自己记录一下,假如之后忘掉了就回去瞧瞧。

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

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

承继包子播放器

为了去更好的自定义,我们得去承继下JzvdStd,便捷我们后期改动,推荐你们去把这个布局迁往自己的项目里,旁边可以改布局。记得重画getLayoutId。



这么我们想一想,承继了要做哪些?其实是要设置下这个布满全界面FrameLayout的点击风波,然而对于包子播放器肯定是有自己的实现方案的,由于哪些,对于初始化好的视频而言,须要我们点击播放,这个点击是点击那里都可以的,所以我们得瞧瞧包子播放器的代码。

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

<p><pre class="EnlighterJSRAW" data-enlighter-language="java" data-enlighter-theme data-enlighter-highlight data-enlighter-linenumbers data-enlighter-lineoffset data-enlighter-title data-enlighter-group>        @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();
        }
    }
</pre></p>
选读:并不会影响核心内容

我们仔细阅读一下,发觉当视频初始化后,点击任意位置可以播放是由于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。

<p><pre class="EnlighterJSRAW" data-enlighter-language="java" data-enlighter-theme data-enlighter-highlight data-enlighter-linenumbers data-enlighter-lineoffset data-enlighter-title data-enlighter-group>                mediaInterface.pause();
            onStatePause();</pre></p>
如今判定状态有了,但是暂停视频的方式也有了(我不晓得包子播放器带了暂停方式没有,我只找到了释放全部视频的方式,假如你们找到了请留言一下。)

如今我们须要创建一个类来承继JzvdStd,我们把onClick覆写进来。

<p><pre class="EnlighterJSRAW" data-enlighter-language="java" data-enlighter-theme data-enlighter-highlight data-enlighter-linenumbers data-enlighter-lineoffset data-enlighter-title data-enlighter-group>        @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();
        }
    }
</pre></p>


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

<p><pre class="wp-block-code">    <code>public class JZPlay extends JzvdStd</code></pre></p>
这个方式是在里面这样的类里的,它须要实现一些插口,你按快捷键实现就可以。

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

如今我们运行,并且有个新问题了,我尝试双击,发觉有卡顿了,我忽然意识到包子播放器默认双击是会暂停,我当时就麻了,而且不能被这个问题打败,其实假如你不须要双击风波问题不大,而且我们这儿是须要实现的,现今来瞧瞧接出来如何解决。

单双击窃听

通过不断检测,发觉JzvdStd里有一个手势窃听器。onDoubleTap双击窃听,下边一个单击窃听。

如今好了,我们只须要把里面的代码办出来放这儿就行了,当我正想去我们承继的类里覆盖这个变量时发觉出大问题,protected修饰后不能在不同包的泛型里承继,这就难堪了呀,当我正打算换其他方案时我发觉下边有一个风波onTouch,这个里面有一行代码。

<p><pre class="wp-block-code">    <code>gestureDetector.onTouchEvent(event);</code></pre></p>
我发觉了个新办法,这个手势窃听器被保护了难以覆写,而且我们可以覆写onTouch,onTouchEvent,这个是安卓风波分发机制里的一个东西,我之前看过一点,然而没怎样读懂,这儿就不探讨了。

这么我们来瞧瞧新的代码

<p><pre class="EnlighterJSRAW" data-enlighter-language="java" data-enlighter-theme data-enlighter-highlight data-enlighter-linenumbers data-enlighter-lineoffset data-enlighter-title data-enlighter-group>     /**
     * 双击
     * 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;
    }
</pre></p>
然而我们没有仿效,首先我们把上面的代码换到onSingleTapConfirmed里,这样双击风波也能够执行了。

同时我们的return直接返回了flase,而不是super.onTouch(v,event);,由于现今它的父类也有onTouch,照搬会出现一个问题,那就是父类虽然执行了同样的代码,这样就有问题了,然而如今这样改似乎促使点击风波触控都正常了,可是这样可能造成Jzvd收不到,引起一些滑动操作挂了,这个我没有测试,不敢断定,你们可以自行测试,这样单击和双击功能就兼容了。

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

后期实现

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

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则