手把手教你撸最新Youtube视频 拖拽动画效果

前言

又到了金三银四的季节了,忙的人特别忙,面试啊,加班啊,闲的人也是特别闲吧,就比如我,天天划水,闲的写文章,做动画,同时呢各种新技术在不断的涌进,推动者软件行业的发展,不要焦虑,不要着急,学好本分,再扩展技能。不多说了,给大家看看做的效果吧。

github链接根据大家的一致意见开源了

youtube2.gif

这个是原生的效果

youtube1.gif

这个是做出来的效果

动画分解

动画分解其实很重要,如果说想模仿一个App的动画及交互效果,一定要仔细的观察他在交互细到放慢每一个操作,然后在根据这个动态来写代码。不然最后实现的效果不一样,那岂不是白费了。废话不多说,上代码看步骤。

第一步视频原始状态分析

可以看到,视频最初始的状态是由一个VideoView(可以是surfaceview都行),加上一个list吧,上下布局,但是VideoView呢,他是根据视频尺寸大小动态的调整宽高的,而不是固定的,我们来分析这个过程

  1. 第一种下拉的时候如果视频的高度没有达到原生高度,这时候下面的listview 不能消费touch事件,由videoview来消费这个事件,根据当前的下滑Y值进行调整视频宽高比
  2. 第二种情况是如果listview已经滑动了一部分这时候进行下拉,但是viewvideo还没有到达真是高度,这时候touch事件还是由listview消费,当他滑动到顶部的时候进行拦截touch 然后传递给videoview
  3. 第三中情况,当listview 向下快速滑动的时候会有个惯性的过程,即使手离开手机了 listview还是在滑动,我们称为惯性滑动,到惯性滑动到顶部的时候,如果视频高度没有达到原生高度,这时候要根据这个惯性值来调整调整视频高度。

第二步 视频下滑过程分析

由上图可以看到当我们在下拉视频的时候,视频的顶部 左右边距 机listview 都发生改变,同时videoview高度也发生了变化。

  1. 下拉的时候margin的 left right top bottom值都在发生改变,videoview 的高度也在发送改变,同时listview 渐渐消失,变成白色的背景,listview的高度在逐渐变小,同时可以看到在Z轴也发生了改变。可能图片不清晰,看不出Z轴的变换
  2. 下拉到listview消失的时候,这时候videoview的宽度发生改变,同时控制器出现。这时候videoview宽度还是在一直的缩小。
  3. 当达到了最小视频悬浮层的时候,下拉整个视图越来越透明,同时整个布局在根据手势在下滑。

第三步 视频上滑过程分析

上滑过程其实就是把下滑过程反过来而已,这里我就不再过多追溯了。

撸代码

主要的逻辑在上面都有,下面我就简单的对上面的逻辑进行分析一下,整体代码想要的老铁私信我留言都行

如何对listview和videoview进行touch的分发

这里我使用的是一个LinearLayout作为父布局进行组装这两个子view,然后根据viewgroup的dispatchTouchEvent方法里面加判断进行事件的分发。网上这些代码很多我就不详细赘述了,其实我这里还是有很多可以优化的地方,这只是个demo。

  • 和上面动画分解的逻辑一致,一看videoview 是否达到最大值,listview是否在顶部。
  • listview(我这边用的时候recyclerview都一样)是否在顶部可以通过调用addOnScrollListener()来判断是否是第一个可见的item
    *判断videoview是否达到最大值呢,根据视频的宽高来。可以通过Mediaplayer来获取,
//分发recyclerview和videoview事件
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        y = ev.getY();
        int pointerId = ev.getPointerId(0);
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mDownY = ev.getY();
                Log.i(TAG, "dispatchTouchEvent: ACTION_DOWN " + mDownY);
                if (mVelocityTracker == null) {
                    mVelocityTracker = VelocityTracker.obtain();
                } else {
                    mVelocityTracker.clear();
                }
                mVelocityTracker.addMovement(ev);
                break;
            case MotionEvent.ACTION_MOVE:

                float dDownY = y - mDownY;

                Log.i(TAG, "dispatchTouchEvent: " + mDownY + " " + y);
                mVelocityTracker.addMovement(ev);
                mVelocityTracker.computeCurrentVelocity(1000);


                if ((mDownY >= (layoutPVideo.getHeight() + layoutPVideo.getMarginTop())) && dDownY > 0 && layoutPVideo.getHeight() < originalHeight + 600 && (isList2Top)) {
                    //判断点击的范围,及当前视频尺寸大小。listview是否已经滑到顶部

                    layoutPVideo.setHeight((int) (layoutPVideo.getHeight() + dDownY));
                    Log.i(TAG, "dispatchTouchEvent: xia " + dDownY);
                    mDownY = y;
                    return true;
                } else if ((mDownY >= (layoutPVideo.getHeight() + layoutPVideo.getMarginTop())) && dDownY <= 0 && layoutPVideo.getHeight() >= originalHeight) {
                    //调整视频view 高度
                    layoutPVideo.setHeight((int) (layoutPVideo.getHeight() + dDownY));
                    //可以加个弹性动画显得更流畅
                    Log.i(TAG, "dispatchTouchEvent: shang " + dDownY);
                    mDownY = y;
                    return true;
                }
                break;
            case MotionEvent.ACTION_UP:
                float yVelocity = VelocityTrackerCompat.getYVelocity(mVelocityTracker,
                        pointerId);
                Log.i("VelocityTrackerCompat", "Y velocity: " +
                        yVelocity);
                
                if (yVelocity >= 5685 && layoutPVideo.getHeight() < originalHeight + 600) {
                    //判断惯性加速度根据惯性加速度进行引导视频大小到底部
                    headMoveToMax();
                } else {
                    Log.i(TAG, "headMoveToMax: not come in" + " " + (layoutPVideo.getHeight() < originalHeight + 600));
                }
                break;
            case MotionEvent.ACTION_CANCEL:
                mVelocityTracker.recycle();
                break;
        }


        return super.dispatchTouchEvent(ev);
    }

如何动态的跳转左右边距及视频宽高

这里我用了一个包装类拿到当前view的layoutParams,然后通过touchEvent 拿到滑动的值来动态的修改当前view 的宽高和margin值,这个代码我就补贴了,就是set get方法。

  • 第一步下滑调整margin的左右上下,及videoview 的高度。
  • 第二步继续下滑调整videoview的宽度
  • 第三步下滑隐藏整体view
 private void updateVideoView(int m, int originY) {

        canHide = false;
        if (mDetailView.getHeight() == 0) {
            if (layoutPVideoView.getMarginRight() <= videoWidthPx && 0 < originY) {


                int value = layoutPVideoView.getMarginRight() + originY * 9;//加速缩小
                if (value > videoWidthPx) {
                    value = (int) videoWidthPx;
                }
                float percent = (videoWidthPx - value) / videoWidthPx;
                if (0 > percent) {
                    percent = 0.f;
                }
                int videoHeight = (int) (videoMinHeightPx * (1 - percent));
                int videoMTop = (int) (allMinScrollY * (1 - percent));
                layoutPVideo.setMarginTop((int) (allScrollY + videoMTop));
                layoutPVideo.setHeight((int) (videoHeightPx - videoHeight));
                layoutPVideoView.setMarginRight(value);



                canHide = true;
                Log.i(TAG, "updateVideoView: "+isBottomMax);
                if (layoutPVideoView.getMarginRight() >= videoWidthPx) {
                    if (isBottomMax) {

                        layoutPVideo.setMarginTop(m);
                        float v = m - (allScrollY + allMinScrollY);
                        mVideoView.setAlpha(1.0f - v / swipePx2Dismiss);

                        if (v >= swipePx2Dismiss) {
                            setVisibility(INVISIBLE);
                            mVideoView.setAlpha(1f);
                        }
                    }
                }

                return;
            }//缩小视频右边距


            if (layoutPVideoView.getMarginRight() >= 0 && 0 > originY) {
                int value = layoutPVideoView.getMarginRight() + originY * 9;
//                if (0 > value) {
//                    value = 0;
//                }
                float percent = (videoWidthPx - value) / videoWidthPx;
                if (0 > percent) {
                    percent = 0.f;
                }
                int videoHeight = (int) (videoMinHeightPx * (1 - percent));
                int videoMTop = (int) (allMinScrollY * (1 - percent));
                layoutPVideo.setMarginTop((int) (allScrollY + videoMTop));
                layoutPVideo.setHeight((int) (videoHeightPx - videoHeight));
                layoutPVideoView.setMarginRight(value);
                return;
            }//放大视频右边距

            if (layoutPVideoView.getMarginRight() >= 0 && originY > 0) {

                return;
            }//最小化阶段


        }


        if (layoutPVideo.getMarginTop() <= 0 && originY < 0) {
            m = 0;
        }//最大化阶段

        float percent = (allScrollY - m) / allScrollY;
        if (0 > percent) {
            percent = 0;
            return;

        }

        int videoHeight = (int) (originalHeight - (originalHeight - videoHeightPx) * (1 - percent));
        int listHeight = (int) ((originListHeight) * (percent));
        layoutPVideo.setMarginTop(m);
        layoutPVideo.setHeight(videoHeight);
        layoutPList.setMarginBottom((int) (marginBottomPx * (1 - percent)));
        layoutPList.setHeight(listHeight);
        layoutPCoverView.getView().setAlpha((1 - percent));
        layoutPContainer.setMarginRight((int) (marginRLPx * (1 - percent)));
        layoutPContainer.setMarginLeft((int) (marginRLPx * (1 - percent)));
        int mr = (int) ((1f - percent) * marginPx); //VideoView右边和详情View 上方的margin
        layoutPVideo.setZ(mr / 2);//这个是Z轴的值,悬浮效果

    }

ending

花了个把小时写的东西,希望给老铁们带来的是知识的储备而不是时间的浪费。不早了不早了下班了,想要代码的老铁可以私信 留言都行

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,252评论 6 516
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,886评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,814评论 0 361
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,869评论 1 299
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,888评论 6 398
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,475评论 1 312
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,010评论 3 422
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,924评论 0 277
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,469评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,552评论 3 342
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,680评论 1 353
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,362评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,037评论 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,519评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,621评论 1 274
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,099评论 3 378
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,691评论 2 361

推荐阅读更多精彩内容