在开发中需要实现图文视频信息流的功能,可以参考下面的动图:
上面的动图来自小红书,我们可以发现:整个列表中有图片(多张)以及视频,视频是可以自动播放的。根据需求,我们来需要实现图片标签的功能,这一块我们是参考Instagram的效果。见下图:
很抱歉,gif中没有很好的体现需要实现的效果,那么我就用文字描述一下吧。如果图片上有标签,那么当滑动到图片时,需要显示一个标签的图标,提示用户这张图片上有标签,可以点击图片查看,如果用户不点击图片,4秒后标签自动消失。
看了以上两种效果,现在我们总结一下需要实现的效果:
下面我们来一个一个分析,由于是讲实现的思路,所以不会涉及到太多代码。
根据效果图我们可以总结出来:列表总共有两种类型:图片和视频。但是它们之间有相同的部分,比如用户信息的展示,文本内容的展示以及评论等。所以自然我们会想到可以创建一个BaseViewHolder来实现共同的部分。
然后我们在创建展示图片和视频的ViewHolder,这两个ViewHolder主要是用来设置各自的信息。
通过以上的操作,我们就可以正常显示列表的信息了,下面我们再探讨一下滑动时的处理操作。
2.滑动控制
一看到滑动控制,肯定会想到RecyclerView的滑动监听:
里面主要有两个方法:onScrollStateChanged()和onScrolled()方法。
onScrollStateChanged()方法会在RecyclerView滑动状态改变时回调。
RecyclerView有哪些滑动状态呢?
SCROLL_STATE_IDLE : RecyclerView没有滑动;
SCROLL_STATE_DRAGGING : RecyclerView被外部因素控制而被拖动,例如触摸事件;
SCROLL_STATE_SETTLING : RecyclerView在无外力控制的情况下找寻最终的位置(可以理解为因为惯性而滑动,因为惯性的存在需要滑动一段距离);
onScrolled():当RecyclerView在滑动过程中会被回调。如果可见的Item的范围因为一个布局而改变时,这个方法也会被回调,在这种情况下dx = 0 , dy = 0;
如果我们想控制视频在滑动过程中的播放控制以及标签的展示,我们需要在这两个回调中进行处理。
除了要知道在哪儿处理,我们还需要知道怎么获取到需要控制的item,也就是代码中的ViewHolder(可能是ImageViewHolder,也可能是VideoViewHolder),这个时候我们就需要借助另外一个工具类:LayoutManager。根据需求,我们需要使用特定的线性布局:LinearLayoutManager。
它能干嘛?
它的作用可大了,比如我们常见的上拉加载更多就需要借助它来计算有没有到达底部。怎么做到的呢?在这里向大家推荐一篇关于判断RecyclerView到达底部的方法的文章。
总结和分析几种判断RecyclerView到达底部的方法 : https://www.jianshu.com/p/c138055af5d2
LinearLayoutManager除了可以判断RecyclerView是否到达底部,它还可以获取当前屏幕内(准确的说是RecyclerView可见范围内)显示的ViewHolder的position。
通过第一个(完全)可见和最后一个(完全)可见的position,我们可以循环获取当前可见的所有的Item的position;
获取到了ViewHolder的position,那我们就要想办法获取到当前position的ViewHolder。
我们可以调用:RecyclerView的findViewHolderForAdapterPosition()方法。
这个方法会返回ViewHolder在数据集合里面的位置,注释里面也提到了另外一个方法findViewHolderForLayoutPosition()。这两个方法有什么区别呢?
第一个方法考虑到了潜在的adapter更新但是没有和布局关联的情况,也就是说,adapter更新了,但是布局还没有计算布局好,那么这个方法会返回null。
第二个方法如果adapter更新了,那么它返回的值可能和adapter里面的不匹配。
在开发中我们使用的是第一个方法。通过调用findViewHolderForAdapterPosition后会返回ViewHolder,这个时候我们就可以判断它是图片类型还是视频类型的ViewHolder,然后我们就可以调用相应的方法了,比如展示图片标签的方法,视频播放的方法等等。
到目前为主,我们知道了如何获取当前显示的ViewHolder然后调用相应的方法,那现在还有一个问题需要解决:如何处理滑动状态?
这个就和你的需求相关了,但一般来说,大多是这样:
通过以上的操作,滑动控制视频的播放以及图片标签的显示隐藏就可以实现了,但是在实际的开发中还有很多需要注意的东西,下面我会列举一些我在开发中遇到的一些问题。
当视频类型的ViewHolder显示到一定的大小时在控制播放?
这个会涉及到计算View的可见区域显示比例问题。我在之前的文章里面讲过,这里就不在重述了。参考链接: https://blog.csdn.net/ChrisSen/article/details/84065243
图片标签功能怎么实现?
这一块的逻辑在开发中并不是由我来写,但是根据显示的效果可以猜测的是需要自定义控件,当然如果你能在GitHub上找到现成的最好,如果没有就需要自己实现了。自定义控件的话,需要两个部分:一个是显示图片,另外一个显示标签,并且可以手动控制。实现代码我就不贴了,这里涉及到公司代码,为了安全起见,大家自行脑补一下
视频部分策略选择?
需要根据不同的需求来选择不同的实现策略。如果是短视频的话,我建议一个adapter对应一个播放器;如果是需要保存播放进度的,可以使用一个ViewHolder对应一个播放器。
视频类型复用?
在开发的过程中,需要特别注意播放器的复用。这里你需要把播放器和数据进行绑定,以确保没有被复用。
局部刷新?
由于列表里面存在点赞,关注等需要刷新布局的操作,所以你要考虑到局部刷新。
实现局部刷新需要两个步骤:
第一步:更新
需要调用含有两个参数的方法,第二个参数传入一个更新操作的标志。比如你是点赞,你就可以写“praise”;
Fragment之间切换控制视频的播放暂停?
如果你用到了ViewPager,并且可以切换页面的,这个时候你需要在setUserVisibleHint方法中处理播放器的播放与暂停。
App进入后台,停止视频播放?
这个时候需要监听播放器的回调。需要特别提醒的是:对于某些播放器,它的startPlay()方法与stopPlay()方法对应,onPause()和onResume()对应,也就是说,一个视频资源如果没有startPlay()而你调用了onPause()是不起作用的。这个时候,如果app被切换到后台了,那么会出现播放器仍然在后台播放的情况。那么我们应该如何解决这种问题呢?我们需要监听播放器播放进度回调,在回调中进行一些条件判断,比如App是否进入后台,当前的页面是否已经被stop等等,然后在进行播放器播放暂停的处理。
可能有的读者会问:“如果我在准备资源的过程中就进行处理不是更好吗?”,逻辑上肯定是这种最好,但是在实际的开发过程中,我们会发现:如果我们在资源准备的过程中处理播放器的暂停,那么这个时候所做的前台判断以及页面是否停止判断都是不符合预期的,也就是说,此时App可能仍然处于前台。所以如果你的视频是循环播放的,那么第一次你是无法处理的,当视频进行第二次循环播放的时候才会生效,我在测试的过程中发现了这个情况。所以,相比来说,在播放进度回调中处理后台播放的逻辑是更优的解决办法。
实现图文短视频信息流,需要综合使用很多知识,需要对RecyclerView非常的了解、视频的播放控制这一块也需要有所接触,还需要自定义实现图片标签的功能等等。
本文只是讲述了开发的实现思路,对于代码实现以及细节处理部分还需要开发者多多注意。