本文主要介绍采用RecyclerView配合PagerSnapHelper实现短视频滑动播放内容。

1. 主页内容构建

主页布局文件定义RecyclerView,为RecyclerView建立对应适配器。

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/rv_little_video"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

适配器条目中添加视频播放容器FrameLayout及封面ImageVIew.

<FrameLayout
    android:id="@+id/fl_content_item"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/black">

<ImageView
    android:id="@+id/iv_thumb_item"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scaleType="centerCrop" />

</FrameLayout>

2.定义RecyclerView滑动管理

PagerSnapHelper 结合 LinearLayoutManager 实现滑动管理,实现监听任务。

PagerSnapHelper介绍

PagerSnapHelper can help achieve a similar behavior to
ViewPager. Set both RecyclerView and the items of the RecyclerView.Adapter to have android.view.ViewGroup.LayoutParams#MATCH_PARENT height and width and then attach PagerSnapHelper to the RecyclerView using #attachToRecyclerView(RecyclerView)}.

翻译:PagerSnapHelper可以帮助实现与以下类似的行为
ViewPager。 将RecyclerView和RecyclerView.Adapter的项目都设置为具有android.view.ViewGroup.LayoutParams#MATCH_PARENT的高度和宽度,然后使用#attachToRecyclerView(RecyclerView)}将PagerSnapHelper附加到RecyclerView。


自定义RecyclerView管理器

RecyclerView管理器为LinearLayoutManager 时,默认为纵向滑动,如果想采用横向滑动,就设置其滑动方向为RecyclerView.HORIZONTAL。同理,我们也可以这样采用setOrientation(RecyclerView.HORIZONTAL) 方法去改变滑动方向。

public class PagerLayoutManager extends LinearLayoutManager implements RecyclerView.OnChildAttachStateChangeListener {

    private OnPageChangedListener mOnPageChangedListener;

    private PagerSnapHelper mSnapHelper;

    /**
     * 移动方向标记
     */
    private int direction;

    public PagerLayoutManager(Context context) {
        super(context);
        mSnapHelper = new PagerSnapHelper();
    }

    /**
     * PagerSnapHelper绑定RecyclerView,同时为监听RecyclerView子布局附着,脱离,进行滑动页面内容控制
     *
     * @param view RecyclerView
     */
    @Override
    public void onAttachedToWindow(RecyclerView view) {
        super.onAttachedToWindow(view);
        mSnapHelper.attachToRecyclerView(view);
        view.addOnChildAttachStateChangeListener(this);
    }

    /**
     * 滑动状态改变监听,滑动完毕后进行播放控制
     *
     * @param state 滑动状态
     */
    @Override
    public void onScrollStateChanged(int state) {
    super.onScrollStateChanged(state);

        if (state == RecyclerView.SCROLL_STATE_IDLE) {
            View view = mSnapHelper.findSnapView(this);
            if (view == null) {
                return;
            }
            int position = getPosition(view);
            if (mOnPageChangedListener != null && getChildCount() == 1) {
                mOnPageChangedListener.onPageSelected(position, position == getItemCount() - 1);
            }
        }

    }

    @Override
    public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
        direction = dx;
        return super.scrollHorizontallyBy(dx, recycler, state);
    }

    @Override
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        direction = dy;
        return super.scrollVerticallyBy(dy, recycler, state);
    }

    @Override
    public void onChildViewAttachedToWindow(@NonNull View view) {
        if (mOnPageChangedListener != null && getChildCount() == 1) {
            mOnPageChangedListener.onPageInitComplete();
        }
    }

    @Override
    public void onChildViewDetachedFromWindow(@NonNull View view) {
        if (mOnPageChangedListener != null) {
            mOnPageChangedListener.onPageRelease(getPosition(view), direction >= 0);
        }
    }

    public void setOnPageChangedListener(OnPageChangedListener mOnPageChangedListener) {
        this.mOnPageChangedListener = mOnPageChangedListener;
    }

    public interface OnPageChangedListener {

        /**
         * 初始化子布局加载完成
         */
        void onPageInitComplete();

        /**
         * 子布局脱离
         *
         * @param position 子布局在RecyclerView位置
         * @param isNext   是否有下一个
         */
        void onPageRelease(int position, boolean isNext);

        /**
         * 子布局附着
         *
         * @param position 子布局在RecyclerView位置
         * @param isLast   是否最后一个
         */
        void onPageSelected(int position, boolean isLast);
    }

}

3. 视频滑动实现

为RecyclerView 设置管理器PagerLayoutManager,设置其Adapter数据内容,进行封面展示,且此时会回调onPageInitComplete()方法,进行首个视频播放。对RecyclerView进行滑动,当页面滑动结束后,会先回调管理器中onPageRelease()方法,此时可对进行中播放器进行停止释放;然后,回调onPageSelected()方法,对选中页面内容进行展示播放。

当滑动后取消时,要进行判断当前位置,避免当前页视频停止或重复播放。

mPagerLayoutManager = new PagerLayoutManager(this);
mPagerLayoutManager.setOnPageChangedListener(new PagerLayoutManager.OnPageChangedListener() {
@Override
public void onPageInitComplete() {
    int position = mPagerLayoutManager.findFirstVisibleItemPosition();
    if (position != -1) {
        mCurrentPosition = position;
    }
    startPlay(mCurrentPosition);
}

@Override
public void onPageRelease(int position, boolean isNext) {
    if (mCurrentPosition == position) {
        stopPlay();
    BaseViewHolder viewHolder = (BaseViewHolder) mRvLittleVideo.findViewHolderForLayoutPosition(mCurrentPosition);
    if (viewHolder != null) {
        ImageView mVideoThumb = viewHolder.getView(R.id.iv_thumb_item);
    if (mVideoThumb != null) {
        mVideoThumb.setVisibility(View.VISIBLE);
    }
    }
    }
}

@Override
public void onPageSelected(int position, boolean isLast) {
    if (mCurrentPosition == position) {
        return;
    }
    startPlay(position);
    mCurrentPosition = position;
    }
});

mRvLittleVideo.setLayoutManager(mPagerLayoutManager);

mLittleVideoAdapter = new LittleVideoAdapter();
mRvLittleVideo.setAdapter(mLittleVideoAdapter);

3. 播放器初始化及停止、播放

初始化播放器内容

private void initVideo() {

    mVideoView = new LittleVideoView(this);
    GSYVideoType.setShowType(GSYVideoType.SCREEN_TYPE_FULL);
    mGsySmallVideoHelperBuilder = new GSYVideoOptionBuilder();
    mGsySmallVideoHelperBuilder
            .setLooping(true)
            .setCacheWithPlay(true)
            .setIsTouchWiget(false)
            .setVideoAllCallBack(new GSYSampleCallBack() {
                @Override
                public void onPrepared(String url, Object... objects) {
                    super.onPrepared(url, objects);

                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            BaseViewHolder viewHolder = (BaseViewHolder) mRvLittleVideo.findViewHolderForLayoutPosition(mCurrentPosition);
                            if (viewHolder != null) {
                                ImageView mVideoThumb = viewHolder.getView(R.id.iv_thumb_item);
                                if (mVideoThumb != null) {
                                    mVideoThumb.setVisibility(View.INVISIBLE);
                                }
                            }
                        }
                    }, 100);
                }
            });
}

开始播放视频内容,进行播放器视图加载

private void startPlay(int position) {
    if (position < 0 || position >= mLittleVideoAdapter.getData().size()) {
        return;
    }
    BaseViewHolder holder = (BaseViewHolder) mRvLittleVideo.findViewHolderForLayoutPosition(position);
    ViewParent parent = mVideoView.getParent();
    if (parent instanceof FrameLayout) {
        ((ViewGroup) parent).removeView(mVideoView);
    }
    if (holder != null) {
        FrameLayout mVideoContent = holder.getView(R.id.fl_content_item);
        mVideoContent.addView(mVideoView, 0);
        mGsySmallVideoHelperBuilder.setUrl(mLittleVideoAdapter.getData().get(position).getUrl());
        mGsySmallVideoHelperBuilder.build(mVideoView);
        mVideoView.startPlayLogic();
    }
}

停止播放,移除视图

private void stopPlay() {
    mVideoView.release();
    ViewParent parent = mVideoView.getParent();
    if (parent instanceof FrameLayout) {
        ((FrameLayout) parent).removeView(mVideoView);
    }
}

3. 播放器内容

例子中采用了自定义空布局的播放器继承自GSY开源播放器,单纯进行视频播放,当然也可以采用其它的播放器饺子或者IjkPlayer等。

public class LittleVideoView extends StandardGSYVideoPlayer {

    public LittleVideoView(Context context, Boolean fullFlag) {
        super(context, fullFlag);
    }

    public LittleVideoView(Context context) {
        super(context);
    }

    public LittleVideoView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public int getLayoutId() {
        return R.layout.empty_control_video;
    }
}

布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/black">

    <FrameLayout
    android:id="@+id/surface_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center">

    </FrameLayout>

</RelativeLayout>

简单的滑动播放这些就完成了,例子也只是仅仅提供了实现的方法和思路,供大家进行学习参考,实际使用中可以对其进一步地进行封装及处理,接下来也会补充一些滑动播放适配器的数据加载处理以及多布局内容展示等内容。

Android短视频滑动播放(二)

关注公众号:几圈年轮,查看更多有趣的技术、工具、闲言、资源。

Android短视频滑动播放(一)的更多相关文章

  1. 如何实现 Android 短视频跨页面的流畅续播?

    在一切皆可视频化的今天,短视频内容作为移动端产品新的促活点,受到了越来越多的重视与投入,同时短视频也是增加用户粘性.增加用户停留时长的一把利器.那么如何快速实现移动端短视频功能呢?前两篇我们介绍了盒马 ...

  2. Android短视频中如何实现720P磨皮美颜录制?

    视频中磨皮.美颜功能已成为刚需,那么如何在Android短视频中实现720P磨皮美颜录制?本篇文章中,网易云信资深开发工程师将向大家介绍具体的操作方法. 相关阅读推荐 <短视频技术详解:Andr ...

  3. 揭秘盒马鲜生 Android 短视频秒播优化方案

    短视频作为内容重要的承载方式,是吸引用户的重点,短视频的内容与体验直接关系到用户是否愿意长时停留.因此,体验的优化就显得尤为重要.上一篇我们分享了 iOS 短视频秒播优化,这篇我们来聊聊 Androi ...

  4. Android短视频SDK转码实践

    一. 前言 一些涉及的基本概念: 转码:一般指多媒体文件格式的转换,比如分辨率.码率.封装格式等: 解复用(demux):从某种封装中分离出视频track和音频track,然后交给后续模块进行处理: ...

  5. 项目源码--Android聚合视频类播放器

    下载源码 技术要点:  1.高效支持主流的视音频格式 2.本地视频的播放与管理 3.聚合电视在线直播 4.聚合优酷.搜狐.乐视.爱奇艺等多种在线视频 5.优质播放,包含播放.暂停,声音.亮度调整等功能 ...

  6. uni-app仿抖音APP短视频+直播+聊天实例|uniapp全屏滑动小视频+直播

    基于uniapp+uView-ui跨端H5+小程序+APP短视频|直播项目uni-ttLive. uni-ttLive一款全新基于uni-app技术开发的仿制抖音/快手短视频直播项目.支持全屏丝滑般上 ...

  7. EasyVideoRecorder短视频拍摄、短视频录制SDK支持IOS版本

    在前面的博客<EasyDarwin开发出类似于美拍.秒拍的短视频拍摄SDK:EasyVideoRecorder>和<美拍.秒拍中安卓.IOS短视频拍摄的一些关键技术>中我们简单 ...

  8. 短视频 SDK 架构设计实践

    作者简介 孔维乐,七牛云客户端团队 Android 平台高级开发工程师,专注音视频,图形图像领域.OpenGL 专家,先后参与直播推流及连麦 SDK 的开发,主导短视频 SDK 的架构设计与实现, 对 ...

  9. 短视频服务大PK,阿里云、腾讯云、又拍云、七牛云、金山云5强横向对比

    继直播后,短视频又再次爆发,在这个风口,国内的各大云厂商也都相继推出了自己的一站式短视频解决方案.上周由于公司短视频功能开发的需要,对比了阿里云.腾讯云.又拍云.七牛云.金山云5家的短视频服务. 先介 ...

随机推荐

  1. LOJ 10239 有趣的数列

    LOJ 10239 有趣的数列 首先可以将奇数视作入栈,偶数视作出栈,那么它是卡特兰数,其实打表也能看出来,而且好像可以用dp? 不过这道题的难点不在这里,p不是素数,所以不能用求逆元来做,不过前50 ...

  2. Android 设置ImageView宽度固定,其高度按比例缩放适应

    今天和项目经理对喷了一下,他说在应用的列表数据中的图片应该宽度固定,高度按比例缩放自适应,我说,那岂不是很丑!直接让运营那边把图片处理成固定宽高比不就好了,省的我客户端麻烦了. 这家伙不同意,为毛呢, ...

  3. 【BestCoder Round #93 1002】MG loves apple

    [题目链接]:http://acm.hdu.edu.cn/showproblem.php?pid=6020 [题意] 给你一个长度为n的数字,然后让你删掉k个数字,问你有没有删数方案使得剩下的N-K个 ...

  4. 深入Java线程管理(四):线程通讯

    线程间的相互作用 线程间的相互作用:线程之间需要一些协调通信,来共同完成一件任务. Object类中相关的方法有两个notify方法和三个wait方法: http://docs.oracle.com/ ...

  5. 达观数据CTO纪达麒:小标注数据量下自然语言处理实战经验

    自然语言处理在文本信息抽取.自动审校.智能问答.情感分析等场景下都有非常多的实际应用需求,在人工智能领域里有极为广泛的应用场景.然而在实际工程应用中,最经常面临的挑战是我们往往很难有大量高质量的标注语 ...

  6. 【知识小结】Git 个人学习笔记及心得

    https://mp.weixin.qq.com/s/D96dXYfu3XAA4ac456qo0g git架构 工作区:就是你在电脑里能看到的目录. 版本库:工作区有一个隐藏目录.git,,而是Git ...

  7. Element节点输出到System.out

    protected void writeElementToFile(Element valrespEle) { try { TransformerFactory transformerFactory ...

  8. MySQL高级配置

    参考文章:http://www.jb51.net/article/47419.htm https://blog.csdn.net/waneto2008/article/details/52502208 ...

  9. pycharm解析器找不到

    pycharm每个项目都需要设定解释器位置吧,把自己解释器位置添加到项目设置就好了 先装python,再装pycharm,不然找不到解析器的,解析器其实就是python.exe.

  10. Vue 父组件往子组件传递方法

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...