美文网首页Android安卓控件Android 干货
Android 列表视频的全屏、自动小窗口优化实践

Android 列表视频的全屏、自动小窗口优化实践

作者: 恋猫月亮 | 来源:发表于2016-11-21 21:17 被阅读11599次
Hello,爱猫的老司机来埋坑啦<( ̄︶ ̄)>,鉴于之前的《Android 实现视屏播放器、边播边缓存功能、外加铲屎(IJKPlayer)》好像还挺多人关注的,文中一些地方因为篇幅(就是懒)问题一笔带过,这篇就拓扑聊一聊其中列表全屏,还有播放中的视频滑出屏幕用小窗口播放的实现,刚好最近有做了一些调整。

上例牌 github>>>>>>>> https://github.com/CarGuo 对,就是这个郭老司机。

本期就不话唠了,周一谁有精力说话呢┑( ̄Д  ̄)┍,程序猿的周末是什么?

列表中播放视频全屏展示

看过小喵上一篇视频相关文章的应该知道小喵手贱的用了两种实现方式,一种是基于懒人的系统层模式;一种是基于单例的UI逻辑播放器的模式的ListVideoUtil。至于为什么是两种呢?因为手贱啊。(ノಠ益ಠ)ノ彡┻━┻,本文如有不明之处可结合前文一起食用:《Android 实现视屏播放器、边播边缓存功能、外加铲屎(IJKPlayer)》

1、系统层实现全屏播放

伟人曾经说过,每一个Activity都有一个自己的默认布局,这里面又包含有了一个com.android.internal.R.id.content,而且是一个FrameLayout(请无视上面的废话),如此看来用来作为我们全屏显示的父布局妥妥的。此处手贱的加入了动画效果的支持,一直觉得5.0的过渡动画挺高大上的,作为一个material design的应用必须有这样的逼格(什么?你说兼容?这里美女太多我听不到····)。

作为一只内向的程序猿,语言组织能力有限,我们还是从代码上来,从代码上去吧,注释满满的,顺序看下去不难理解(前提是你看的下,确实长♂了点)。

1.1 进入全屏
  • 获取到了com.android.internal.R.id.content这个ViewGroup。
  • 清除当前列表播放器L上的TextureView渲染控件,等待全屏播放器F的渲染控件。
  • 新创建一个视频逻辑播放器F,为它设置一个固定id,这样干掉它的时候通过这个id也能快速找到。
  • 保存当前的状态栏、标题栏信息和列表中在屏幕位置的信息,用于恢复到原本的状态。
  • 创建一个黑色背景的FrameLayout,充满屏幕用来承载全屏播放器F,这样全屏播放器F可以在其中执行动画效果。
  • 5.0以下直接加全屏播放器F到ViewGroup居中充满全屏,5.0以上则执行动画。
  • 5.0以上先通过margin让全屏播放器加入到ViewGroup同列表的位置一致,之后通过过渡动画平移到屏幕中间,居中充满全屏。

怎么样,看起来是不是有些混乱?(ノಠ益ಠ)ノ彡┻━┻,我就说程序猿还是看代码好沟通是吧,虽然很长就是。

 
//获得com.android.internal.R.id.content
private ViewGroup getViewGroup() {    
    return (ViewGroup) (CommonUtil.scanForActivity(getContext())).findViewById(Window.ID_ANDROID_CONTENT);
}

···此处省略无数只草泥马

//这两个是TextureView的回调,在这remove和onPause还有add的时候基本会进入
 @Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
    //更新数据到这个surface上渲染
    mSurface = new Surface(surface);
    GSYVideoManager.instance().setDisplay(mSurface);
}

@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
    //告诉数据管理器这个渲染控件放弃了
    GSYVideoManager.instance().setDisplay(null);
    surface.release();
    return true;
}

···此处省略无数只草泥马

这个开始全屏的页面逻辑

//将播放的视频渲染控件移除,进入上面的回调,让新的逻辑播放器可以接入
if (mTextureViewContainer.getChildCount() > 0) {
    mTextureViewContainer.removeAllViews();
}

//保存全屏之前的状态栏和
saveLocationStatus(context, statusBar, actionBar);

try {
    //生成一个播放器,因为继承关系,会创建一个当前列表item一样的UI逻辑播放器
    //这些逻辑都是写在GSYBaseVideoPlayer这个抽象类下
    Constructor<GSYBaseVideoPlayer> constructor = (Constructor<GSYBaseVideoPlayer>) GSYBaseVideoPlayer.this.getClass().getConstructor(Context.class);
    final GSYBaseVideoPlayer gsyVideoPlayer = constructor.newInstance(getContext());
    //给它一个固定的id,在这样移除的时候就知道在哪里
    gsyVideoPlayer.setId(FULLSCREEN_ID);
    //获取屏幕的高度
    WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
    final int w = wm.getDefaultDisplay().getWidth();
    final int h = wm.getDefaultDisplay().getHeight();
    //创建一个层用于加入都window层中,设置为黑色,用于包含著播放器
    FrameLayout.LayoutParams lpParent = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
    FrameLayout frameLayout = new FrameLayout(context);
    frameLayout.setBackgroundColor(Color.BLACK);
    //如果5.0的机器就执行动画,这里其实可以用VauleAnimaton兼容5.0以下的
    if (mShowFullAnimation && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        //先把播放器的位置设置为在列表中一样位置
        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(getWidth(), getHeight());
        lp.setMargins(mListItemRect[0], mListItemRect[1], 0, 0);
        frameLayout.addView(gsyVideoPlayer, lp);
        vp.addView(frameLayout, lpParent);
        //稍微延时执行动画
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                //开启5.0动画
                TransitionManager.beginDelayedTransition(vp);
                //将播放器跳转为充满居中,系统自动过渡
                resolveFullVideoShow(context, gsyVideoPlayer, h, w);
            }
        }, 300);
    } else {
        //非5.0的直接将播放器的布局加入到布局下
        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(getWidth(), getHeight());
        frameLayout.addView(gsyVideoPlayer, lp);
        vp.addView(frameLayout, lpParent);
        //将播放器跳转为充满居中
        resolveFullVideoShow(context, gsyVideoPlayer, h, w);
    }
    //设置全屏逻辑播放器和当前列表的逻辑状态一致
    gsyVideoPlayer.setUp(mUrl, mCache, mObjects);
    gsyVideoPlayer.setStateAndUi(mCurrentState);
    //添加上渲染控件,通知数据加载管理器是用这个渲染
    gsyVideoPlayer.addTextureView();
    //配置对应UI
    gsyVideoPlayer.getFullscreenButton().setImageResource(R.drawable.video_shrink);
    gsyVideoPlayer.getFullscreenButton().setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            clearFullscreenLayout();
        }
    });

    gsyVideoPlayer.getBackButton().setVisibility(VISIBLE);
    gsyVideoPlayer.getBackButton().setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            clearFullscreenLayout();
        }
    });
    //将数据加载管理器的接口回到配置到全屏播放器里面
    GSYVideoManager.instance().setLastListener(this);
    GSYVideoManager.instance().setListener(gsyVideoPlayer);

} catch (Exception e) {
    e.printStackTrace();
}

···此处省略无数只草泥马

/**
 * 全屏
 */
private void resolveFullVideoShow(Context context, GSYBaseVideoPlayer gsyVideoPlayer) {
    //清除动画的margin
    FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) gsyVideoPlayer.getLayoutParams();
    lp.setMargins(0, 0, 0, 0);
    //居中充满
    lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
    lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
    lp.gravity = Gravity.CENTER;
    gsyVideoPlayer.setLayoutParams(lp);
    gsyVideoPlayer.setIfCurrentIsFullscreen(true);
    //加入旋转工具类
    mOrientationUtils = new OrientationUtils((Activity) context, gsyVideoPlayer);
    mOrientationUtils.setEnable(mRotateViewAuto);
}

1.2 退出全屏

既然都进去了♂,出来还难吗?所以我们只需要反着来就行了,下面直接长代码,有注释。(男人长一点有什么错┑( ̄Д  ̄)┍)

  • 是否横屏,是的话先转为竖屏
  • 恢复状态栏和标题栏
  • 5.0以下直接清除当前列全屏播放器F,恢复视频状态
  • 5.0以上显示让全屏播放器F过渡到原本的位置,再清除恢复视频状态
/**
 * 退出系统层播放全屏效果
 */
public void clearFullscreenLayout() {

    //需要判断当前是否横屏,是的话要转为界面之后稍等一会在退回,这样才不会界面抖动
    int delay = mOrientationUtils.backToProtVideo();
    //关闭旋转
    mOrientationUtils.setEnable(false);
    mHandler.postDelayed(new Runnable() {
        @Override
        public void run() {
            backToNormal();
        }
    }, delay);
}

/**
 * 回到正常效果
 */
private void backToNormal() {
    //恢复状态
    showSupportActionBar(mContext, mActionBar, mStatusBar);
    final ViewGroup vp = getViewGroup();
    //拿到content和播放器
    final View oldF = vp.findViewById(FULLSCREEN_ID);
    final GSYVideoPlayer gsyVideoPlayer;
    if (oldF != null) {
        gsyVideoPlayer = (GSYVideoPlayer) oldF;
        if (mShowFullAnimation && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            TransitionManager.beginDelayedTransition(vp);
            //执行动画回到原本的列表中的位置
            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) gsyVideoPlayer.getLayoutParams();
            lp.setMargins(mListItemRect[0], mListItemRect[1], 0, 0);
            lp.width = mListItemSize[0];
            lp.height = mListItemSize[1];
            //注意配置回来,不然动画效果会不对
            lp.gravity = Gravity.NO_GRAVITY;
            gsyVideoPlayer.setLayoutParams(lp);

            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    resolveNormalVideoShow(oldF, vp, gsyVideoPlayer);
                }
            }, 400);
        } else {
            //直接移除
            resolveNormalVideoShow(oldF, vp, gsyVideoPlayer);
        }

    } else {
        //直接移除
        resolveNormalVideoShow(null, vp, null);
    }
}


/**
 * 恢复
 */
private void resolveNormalVideoShow(View oldF, ViewGroup vp, GSYVideoPlayer gsyVideoPlayer) {
    //移除全屏播放器
    if (oldF.getParent() != null) {
        ViewGroup viewGroup = (ViewGroup) oldF.getParent();
        vp.removeView(viewGroup);
    }
    //拿回状态
    mCurrentState = GSYVideoManager.instance().getLastState();
    if (gsyVideoPlayer != null) {
        mCurrentState = gsyVideoPlayer.getCurrentState();
    }
    //重新设置回调
    GSYVideoManager.instance().setListener(GSYVideoManager.instance().lastListener());
    GSYVideoManager.instance().setLastListener(null);
    //播放器恢复
    setStateAndUi(mCurrentState);
    //通知数据加载播放器用回列表的渲染
    addTextureView();
    CLICK_QUIT_FULLSCREEN_TIME = System.currentTimeMillis();
}

2、ListVideoUtil实现全屏播放

总体上逻辑和上文是一致的,只是这种实现在列表中是不包含逻辑播放器,逻辑播放器和全屏逻辑播放器都是一个单例,需要你手动在list列表的最外层加多一个布局做全屏播放,在每个item那里预留一个位置用于包容列表的播放器,还有一个播放按钮用于播放。

感觉很麻烦是吧,耦合度又高,但是它可以在视频滑出界面的时候不被释放,一直保持在原来的位置。

2.1 全屏

和上面的逻辑基本一致,就不废话了(可以偷懒了),只需要注意用的时候操作方式不一样,总结起来就是有些麻烦。


//配置好全屏布局
listVideoUtil.setFullViewContainer(videoFullContainer);
listVideoUtil.setHideStatusBar(true);

···此处省略无数只草泥马

//增加封面
ImageView imageView = new ImageView(context);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setImageResource(R.mipmap.xxx1);
//将列表的位置,封面,列表的TAG,列表是的父布局,播放按键传入进去
listVideoUtil.addVideoPlayer(position, imageView, TAG, holder.videoContainer, holder.playerBtn);

holder.playerBtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        //更新其他item
        notifyDataSetChanged();
        //设置播放器的标志位,防止错位
        listVideoUtil.setPlayPositionAndTag(position, TAG);
        //url开始播放
        final String url = "http://baobab.wdjcdn.com/14564977406580.mp4";
        listVideoUtil.startPlay(url);
    }
});

列表中播放视频小窗口播放

有时候我们会想要视频滑出屏幕的时候有个小窗口在右下角,最好还是可以关闭和拖动的(看视频的时候可以快速最小化收起来,不停止,避免尴尬对吧)。逻辑和实现全屏一样,用系统的content层来承载,不同的是利用margin让视频出现在右下角,这样我们拖动的时候只要改变视频的margin,就可以让视频小窗体在它的父布局内移动啦。

小窗口
/**
 * 显示小窗口
 */
public void showSmallVideo(Point size, final boolean actionBar, final boolean statusBar) {
    //利用content实现,和全屏一样,只是大小和背景色不一样
    final ViewGroup vp = getViewGroup();

    removeVideo(vp, SMALL_ID);

    if (mTextureViewContainer.getChildCount() > 0) {
        mTextureViewContainer.removeAllViews();
    }

    try {
        Constructor<GSYBaseVideoPlayer> constructor = (Constructor<GSYBaseVideoPlayer>) GSYBaseVideoPlayer.this.getClass().getConstructor(Context.class);
        GSYBaseVideoPlayer gsyVideoPlayer = constructor.newInstance(getContext());
        gsyVideoPlayer.setId(SMALL_ID);

        FrameLayout.LayoutParams lpParent = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        FrameLayout frameLayout = new FrameLayout(mContext);

        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(size.x, size.y);
        int marginLeft = CommonUtil.getScreenWidth(mContext) - size.x;
        int marginTop = CommonUtil.getScreenHeight(mContext) - size.y;

        if (actionBar) {
            marginTop = marginTop - getActionBarHeight((Activity) mContext);
        }

        if (statusBar) {
            marginTop = marginTop - getStatusBarHeight(mContext);
        }
        //利用margin让视频出现在右下角,这样我们拖动的时候只要改变margin就好啦
        lp.setMargins(marginLeft, marginTop, 0, 0);
        frameLayout.addView(gsyVideoPlayer, lp);

        vp.addView(frameLayout, lpParent);
        //继续播放
        gsyVideoPlayer.setUp(mUrl, mCache, mObjects);
        gsyVideoPlayer.setStateAndUi(mCurrentState);
        gsyVideoPlayer.addTextureView();
        gsyVideoPlayer.onClickUiToggle();
        gsyVideoPlayer.setSmallVideoTextureView(new SmallVideoTouch(gsyVideoPlayer, marginLeft, marginTop));

        GSYVideoManager.instance().setLastListener(this);
        GSYVideoManager.instance().setListener(gsyVideoPlayer);

    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    }

}

/**
 * 隐藏小窗口
 */
public void hideSmallVideo() {
    final ViewGroup vp = getViewGroup();
    GSYVideoPlayer gsyVideoPlayer = (GSYVideoPlayer) vp.findViewById(SMALL_ID);
    removeVideo(vp, SMALL_ID);
    mCurrentState = GSYVideoManager.instance().getLastState();
    if (gsyVideoPlayer != null) {
        mCurrentState = gsyVideoPlayer.getCurrentState();
    }
    GSYVideoManager.instance().setListener(GSYVideoManager.instance().lastListener());
    GSYVideoManager.instance().setLastListener(null);
    setStateAndUi(mCurrentState);
    addTextureView();
    CLICK_QUIT_FULLSCREEN_TIME = System.currentTimeMillis();
}

这是触摸逻辑,这拖动的视频窗体的时候,通过改变margin来实现窗体的移动,注意不要跑飞了就要,加个阈值。多说无益,看代码(又省下了好多字):

public class SmallVideoTouch implements View.OnTouchListener {

    private int mDownX, mDownY;
    private int mMarginLeft, mMarginTop;
    private int _xDelta, _yDelta;
    private GSYBaseVideoPlayer mGsyBaseVideoPlayer;


    public SmallVideoTouch(GSYBaseVideoPlayer gsyBaseVideoPlayer, int marginLeft,  int marginTop) {
        super();
        mMarginLeft = marginLeft;
        mMarginTop = marginTop;
        mGsyBaseVideoPlayer = gsyBaseVideoPlayer;
    }

    @Override
    public boolean onTouch(View view, MotionEvent event) {
        final int X = (int) event.getRawX();
        final int Y = (int) event.getRawY();
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                mDownX = X;
                mDownY = Y;

                FrameLayout.LayoutParams lParams = (FrameLayout.LayoutParams) mGsyBaseVideoPlayer
                        .getLayoutParams();
                _xDelta = X - lParams.leftMargin;
                _yDelta = Y - lParams.topMargin;

                break;
            case MotionEvent.ACTION_UP:
                if (Math.abs(mDownY - Y) < 5 && Math.abs(mDownX - X) < 5) {
                    return false;
                } else {
                    return true;
                }
            case MotionEvent.ACTION_MOVE:
                FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mGsyBaseVideoPlayer
                        .getLayoutParams();

                layoutParams.leftMargin = X - _xDelta;
                layoutParams.topMargin = Y - _yDelta;
                
                //不能超过屏幕上下左右的位置
                if (layoutParams.leftMargin >= mMarginLeft) {
                    layoutParams.leftMargin = mMarginLeft;
                }

                if (layoutParams.topMargin >= mMarginTop) {
                    layoutParams.topMargin = mMarginTop;
                }

                if (layoutParams.leftMargin <= 0) {
                    layoutParams.leftMargin = 0;
                }

                if (layoutParams.topMargin <= 0) {
                    layoutParams.topMargin = 0;
                }

                mGsyBaseVideoPlayer.setLayoutParams(layoutParams);

        }
        return false;
    }

}
最后

如果你看到这里,恭喜你看完了<( ̄︶ ̄)>!那么,下面还有沙发,请问您要坐一坐吗?d=====( ̄▽ ̄*)b不坐也没关系,还有github可以去呢:https://github.com/CarGuo

来一下嘛

相关文章

网友评论

  • 3ce2620b05f3:我继承了GSYVideoPlayer这个类。按照StandardGSYVideoPlayer方式写好了自定义UI的类。但是视频播放不了。getFullWindowPlayer这个返回的是null:innocent:
  • 背包者:@恋猫月亮 楼主,如果UI逻辑是Activity>fragment>fragment,想在最里面进行列表视频播放,要注意什么??
  • 7ecf07e3455b:当看到小窗口的时候 我感觉 可以完成我那个 竖屏 横拍的功能 已打赏~
    恋猫月亮:@阿里赤壁 多谢!
  • 波波一起飞:如果有用的是Toolbar不是Actionbar,没法全屏!解决思路怎么样?
    恋猫月亮: @波波一起飞 toolbar在activity也是设置到support的actionbar上啊
  • 背包者:楼主,在子fragment中使用视频列表,只有声音没有画面,可以帮一下吗
    恋猫月亮: @背包者 可能和硬件加速有关吧
    背包者:@恋猫月亮 demo正常,就是添加在我的fragment中就不行了
    恋猫月亮: @背包者 demo中会吗
  • b5df1d12c3ae:大神,在给状态栏设置统一背景后,然后全屏,虽然隐藏了状态栏,但是状态栏的背景还在怎么解决?
  • 7b3a41b7334e:有试过小米的机子么?
    恋猫月亮: @傲娇的小bug 有
  • fe36f7a9e41a:打开了硬解码横竖屏切换的时候会黑屏一下,这个怎么解决?
    恋猫月亮: @執孓之掱 真机?可以看问题详解
    fe36f7a9e41a:@恋猫月亮 软解的话不同步有什么解决方案吗
    恋猫月亮: @執孓之掱 因为硬解码成像慢
  • Mg明明就是你:fragmengt无法全屏播放是什么鬼‘?而且只有声音没有画面,这又是什么鬼?
    Mg明明就是你:@恋猫月亮 GIF图放README.md吧,项目太大了下载项目下载了2天 时不时自己断了 好不容易下载下来还没看:smiley: 真心累啊
    Mg明明就是你:@恋猫月亮 一横屏就重新加载数据是什么鬼?重新请求数据了
    恋猫月亮: @Mg明明就是你 fragment播放demo中有,在fragment和Activity没区别。然后第二个问题,在github首页有说明,请查看。
  • bdf0379f96ed:楼主万分火急,为什么当嵌套ScrollView里当有监听onScrollChanged时上下一滑动就会界面就会显示暂停状态,但是有声音,去掉onScrollChanged的监听就没有问题,上下滑动还是会继续播放,实在找不出原因
  • 不弃先森丶:楼主您好 ,请问小窗口点击如何进入详情页 继续播放?
  • 812f3a1a9a68:楼主好人
  • 疯斗的何先生:一边学习一边吃狗粮:relieved:
  • 阿_希爸:首先感谢楼主,之前也是一直才尝试封装一个播放器,在楼主的播放器中得到了很多启示,封装的过程中遇到的最大的问题还是全屏和List列表播放的问题,于是我走了不同的路线,并没有用反射的方式重新构造一个播放器,而是将现有的播放器移出容器在添加到新的容器中。这是我写的播放器项目的地址:https://github.com/tough1985/XibaVideoPlayer。希望能够有一些帮助
    开心就好_zhu:进度条小窗口预览的功能在哪啊?
    阿_希爸:@恋猫月亮 话说咱能不能把项目里的图用连接的方式扔到README里面啊,每次下项目都好累啊
    恋猫月亮: @阿_希爸 话说,我的项目中的listvideoutils应该和你的类似,不过这个类如今少维护了
  • Rc在努力:楼主你好,你写的文章很好,而且比用IJKPlayer简单很多了,我想知道安装你在github上的步骤引入项目后,如何在Gradle设置支持的SO库架构,gradle不是很熟:cold_sweat:
    恋猫月亮: @Rc在努力 http://www.jianshu.com/p/86e4b336c17d
  • 林锐波:楼主 把功能嵌套在fragment中会有问题,在旋转查看全屏的时候会卡屏有这个bug,
    还有一个问题是当使用小窗口查看视频时,此时再切换下一个视频listVideoUtil.startPlay(url) 不会做更新,还是小视频的url.
    e5f2ae8529cb:还有一个问题是当使用小窗口查看视频时,此时再切换下一个视频listVideoUtil.startPlay(url) 不会做更新,还是小视频的url. 这个问题你是怎么处理的
  • i无恙i:怎么在视频列表中自动播放呢,就像微博和朋友圈一样??
    恋猫月亮:@路人甲er 你看看今天github的最新的最新版本是否满足你的需求,ListViewActivity,demo里面没有画出来自动播放,不过github上的qq群里有人做了这个需求,你可以进去问问
    i无恙i:@恋猫月亮 现在很需要这个功能。这个大概多久能完善呢, 感谢楼主
    恋猫月亮:@路人甲er 本身有startPlayLogic可以然你自动播放,但是静音播放的接口还没有加上,目前正在完善这一块
  • e2f8c36142de:如何定制UI呢,有相关的接口么
  • 8ff6bbf32ad6:突然发现猫是一个神奇的物种
    恋猫月亮:很神奇😏😏😏
  • 04ba21f94b17:如果不是listview。只是一个scrollview 最上方是VideoPlayer 下方是webview。。要实现小窗的功能 直接用ListVideoUtil 也是可以的吧? = =。
  • 06peng:要不要加上左右滑动,关闭视频播放啊。
    恋猫月亮:@06peng 左右滑动关闭视屏??你可以在左右滑动去pageonselect之类的加上releaseAllVideo哟
  • 贰yang:赞
    恋猫月亮:@PATH_洋 😁😁😁😁
  • 恋猫月亮:1.2.3版本已经更新
  • 2fd8867901ec:好长…
  • 74f2385a62c0:收藏 学习😊
    恋猫月亮: @蜂蜜110 欢迎欢迎🤗
  • 恋猫月亮:这篇的demo和上一篇是同一个哟! https://github.com/CarGuo/GSYVideoPlayer

本文标题:Android 列表视频的全屏、自动小窗口优化实践

本文链接:https://www.haomeiwen.com/subject/xqrupttx.html