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

1. 主页内容构建

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

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

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

  1. <FrameLayout
  2. android:id="@+id/fl_content_item"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:background="@android:color/black">
  6. <ImageView
  7. android:id="@+id/iv_thumb_item"
  8. android:layout_width="match_parent"
  9. android:layout_height="match_parent"
  10. android:scaleType="centerCrop" />
  11. </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) 方法去改变滑动方向。

  1. public class PagerLayoutManager extends LinearLayoutManager implements RecyclerView.OnChildAttachStateChangeListener {
  2. private OnPageChangedListener mOnPageChangedListener;
  3. private PagerSnapHelper mSnapHelper;
  4. /**
  5. * 移动方向标记
  6. */
  7. private int direction;
  8. public PagerLayoutManager(Context context) {
  9. super(context);
  10. mSnapHelper = new PagerSnapHelper();
  11. }
  12. /**
  13. * PagerSnapHelper绑定RecyclerView,同时为监听RecyclerView子布局附着,脱离,进行滑动页面内容控制
  14. *
  15. * @param view RecyclerView
  16. */
  17. @Override
  18. public void onAttachedToWindow(RecyclerView view) {
  19. super.onAttachedToWindow(view);
  20. mSnapHelper.attachToRecyclerView(view);
  21. view.addOnChildAttachStateChangeListener(this);
  22. }
  23. /**
  24. * 滑动状态改变监听,滑动完毕后进行播放控制
  25. *
  26. * @param state 滑动状态
  27. */
  28. @Override
  29. public void onScrollStateChanged(int state) {
  30. super.onScrollStateChanged(state);
  31. if (state == RecyclerView.SCROLL_STATE_IDLE) {
  32. View view = mSnapHelper.findSnapView(this);
  33. if (view == null) {
  34. return;
  35. }
  36. int position = getPosition(view);
  37. if (mOnPageChangedListener != null && getChildCount() == 1) {
  38. mOnPageChangedListener.onPageSelected(position, position == getItemCount() - 1);
  39. }
  40. }
  41. }
  42. @Override
  43. public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
  44. direction = dx;
  45. return super.scrollHorizontallyBy(dx, recycler, state);
  46. }
  47. @Override
  48. public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
  49. direction = dy;
  50. return super.scrollVerticallyBy(dy, recycler, state);
  51. }
  52. @Override
  53. public void onChildViewAttachedToWindow(@NonNull View view) {
  54. if (mOnPageChangedListener != null && getChildCount() == 1) {
  55. mOnPageChangedListener.onPageInitComplete();
  56. }
  57. }
  58. @Override
  59. public void onChildViewDetachedFromWindow(@NonNull View view) {
  60. if (mOnPageChangedListener != null) {
  61. mOnPageChangedListener.onPageRelease(getPosition(view), direction >= 0);
  62. }
  63. }
  64. public void setOnPageChangedListener(OnPageChangedListener mOnPageChangedListener) {
  65. this.mOnPageChangedListener = mOnPageChangedListener;
  66. }
  67. public interface OnPageChangedListener {
  68. /**
  69. * 初始化子布局加载完成
  70. */
  71. void onPageInitComplete();
  72. /**
  73. * 子布局脱离
  74. *
  75. * @param position 子布局在RecyclerView位置
  76. * @param isNext 是否有下一个
  77. */
  78. void onPageRelease(int position, boolean isNext);
  79. /**
  80. * 子布局附着
  81. *
  82. * @param position 子布局在RecyclerView位置
  83. * @param isLast 是否最后一个
  84. */
  85. void onPageSelected(int position, boolean isLast);
  86. }
  87. }

3. 视频滑动实现

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

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

  1. mPagerLayoutManager = new PagerLayoutManager(this);
  2. mPagerLayoutManager.setOnPageChangedListener(new PagerLayoutManager.OnPageChangedListener() {
  3. @Override
  4. public void onPageInitComplete() {
  5. int position = mPagerLayoutManager.findFirstVisibleItemPosition();
  6. if (position != -1) {
  7. mCurrentPosition = position;
  8. }
  9. startPlay(mCurrentPosition);
  10. }
  11. @Override
  12. public void onPageRelease(int position, boolean isNext) {
  13. if (mCurrentPosition == position) {
  14. stopPlay();
  15. BaseViewHolder viewHolder = (BaseViewHolder) mRvLittleVideo.findViewHolderForLayoutPosition(mCurrentPosition);
  16. if (viewHolder != null) {
  17. ImageView mVideoThumb = viewHolder.getView(R.id.iv_thumb_item);
  18. if (mVideoThumb != null) {
  19. mVideoThumb.setVisibility(View.VISIBLE);
  20. }
  21. }
  22. }
  23. }
  24. @Override
  25. public void onPageSelected(int position, boolean isLast) {
  26. if (mCurrentPosition == position) {
  27. return;
  28. }
  29. startPlay(position);
  30. mCurrentPosition = position;
  31. }
  32. });
  33. mRvLittleVideo.setLayoutManager(mPagerLayoutManager);
  34. mLittleVideoAdapter = new LittleVideoAdapter();
  35. mRvLittleVideo.setAdapter(mLittleVideoAdapter);

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

初始化播放器内容

  1. private void initVideo() {
  2. mVideoView = new LittleVideoView(this);
  3. GSYVideoType.setShowType(GSYVideoType.SCREEN_TYPE_FULL);
  4. mGsySmallVideoHelperBuilder = new GSYVideoOptionBuilder();
  5. mGsySmallVideoHelperBuilder
  6. .setLooping(true)
  7. .setCacheWithPlay(true)
  8. .setIsTouchWiget(false)
  9. .setVideoAllCallBack(new GSYSampleCallBack() {
  10. @Override
  11. public void onPrepared(String url, Object... objects) {
  12. super.onPrepared(url, objects);
  13. new Handler().postDelayed(new Runnable() {
  14. @Override
  15. public void run() {
  16. BaseViewHolder viewHolder = (BaseViewHolder) mRvLittleVideo.findViewHolderForLayoutPosition(mCurrentPosition);
  17. if (viewHolder != null) {
  18. ImageView mVideoThumb = viewHolder.getView(R.id.iv_thumb_item);
  19. if (mVideoThumb != null) {
  20. mVideoThumb.setVisibility(View.INVISIBLE);
  21. }
  22. }
  23. }
  24. }, 100);
  25. }
  26. });
  27. }

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

  1. private void startPlay(int position) {
  2. if (position < 0 || position >= mLittleVideoAdapter.getData().size()) {
  3. return;
  4. }
  5. BaseViewHolder holder = (BaseViewHolder) mRvLittleVideo.findViewHolderForLayoutPosition(position);
  6. ViewParent parent = mVideoView.getParent();
  7. if (parent instanceof FrameLayout) {
  8. ((ViewGroup) parent).removeView(mVideoView);
  9. }
  10. if (holder != null) {
  11. FrameLayout mVideoContent = holder.getView(R.id.fl_content_item);
  12. mVideoContent.addView(mVideoView, 0);
  13. mGsySmallVideoHelperBuilder.setUrl(mLittleVideoAdapter.getData().get(position).getUrl());
  14. mGsySmallVideoHelperBuilder.build(mVideoView);
  15. mVideoView.startPlayLogic();
  16. }
  17. }

停止播放,移除视图

  1. private void stopPlay() {
  2. mVideoView.release();
  3. ViewParent parent = mVideoView.getParent();
  4. if (parent instanceof FrameLayout) {
  5. ((FrameLayout) parent).removeView(mVideoView);
  6. }
  7. }

3. 播放器内容

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

  1. public class LittleVideoView extends StandardGSYVideoPlayer {
  2. public LittleVideoView(Context context, Boolean fullFlag) {
  3. super(context, fullFlag);
  4. }
  5. public LittleVideoView(Context context) {
  6. super(context);
  7. }
  8. public LittleVideoView(Context context, AttributeSet attrs) {
  9. super(context, attrs);
  10. }
  11. @Override
  12. public int getLayoutId() {
  13. return R.layout.empty_control_video;
  14. }
  15. }

布局文件

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:background="@android:color/black">
  6. <FrameLayout
  7. android:id="@+id/surface_container"
  8. android:layout_width="match_parent"
  9. android:layout_height="match_parent"
  10. android:gravity="center">
  11. </FrameLayout>
  12. </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. @topcoder - SRM577D1L3@ XorAndSum

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 给出 N 个数,每次操作可以任意选择两个数,将其中一个替换为两个 ...

  2. Fiddler快速入门

    Fiddler是一个免费.强大.跨平台的HTTP抓包工具.Wireshark也是一个强大的抓包工具,不过Wireshark是一个通用的抓包工具,主要精力放在各种协议上了,针对HTTP的特定功能较少.所 ...

  3. Project Euler Problem 5-Smallest multiple

    对每个数字分解素因子,最后对每个素因子去其最大的指数,然后把不同素因子的最大指数次幂相乘,得到的就是最小公倍数 python不熟练,代码比较挫 mp = {} def process(n): i = ...

  4. php中 instanceof有什么作用

    php中 instanceof有什么作用 作用:(1)判断一个对象是否是某个类的实例,(2)判断一个对象是否实现了某个接口.

  5. input 的 pattern 验证表单

    pattern 用于定义验证输入正则表达式 pattern 属性适用于以下 <input> 类型:text, search, url, telephone, email 以及 passwo ...

  6. 2007年NOIP普及组复赛题解

    题目涉及算法: 奖学金:结构体排序: 纪念品分组:贪心: 守望者的逃离:动态规划: Hanoi 双塔问题:递推. 奖学金 题目链接:https://www.luogu.org/problem/P109 ...

  7. Python--day28--面试题

  8. tensorflow学习笔记(二十五):ConfigProto&GPU

    tensorflow ConfigPrototf.ConfigProto一般用在创建session的时候.用来对session进行参数配置 with tf.Session(config = tf.Co ...

  9. 2019-10-31-Resharper-去掉注释拼写

    title author date CreateTime categories Resharper 去掉注释拼写 lindexi 2019-10-31 9:8:5 +0800 2018-09-04 1 ...

  10. 后缀数组 + LCP加速多模式匹配算法 O(m+logn)

    #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> ...