抖音怎么拍滑动视频,抖音视频左右滑动的视频怎么拍
0、效果图
自定义LayoutManager,并且继承LinearLayoutManager,这样就得到一个可以水平排向或者竖向排向的布局策略。
如果你接触过SnapHelper应该了解一下LinearSnapHelper和PagerSnapHelper这两个子类类,LinearSnapHelper可以实现让列表的Item居中显示的效果,PagerSnapHelper就可以做到一次滚动一个item显示的效果。
重写onChildViewAttachedToWindow方法,在RecyclerView中,当Item添加进来了调用这个方法。这个方法相当于是把view添加到window时候调用的,也就是说它比draw方法先执行,可以做一些初始化相关的操作。
/** * 该方法必须调用 * @param recyclerView */@Overridepublic void onAttachedToWindow(RecyclerView recyclerView) { if (recyclerView == null) { throw new IllegalArgumentException("The attach RecycleView must not null!!"); } super.onAttachedToWindow(recyclerView); this.mRecyclerView = recyclerView; if (mPagerSnapHelper==null){ init(); } mPagerSnapHelper.attachToRecyclerView(mRecyclerView); mRecyclerView.addOnChildAttachStateChangeListener(mChildAttachStateChangeListener);}
4.2 添加滑动监听
涉及到一次滑动一页视频,那么肯定会有视频初始化和释放的功能。那么思考一下哪里来开始播放视频和在哪里释放视频?
不要着急,要监听滑动到哪页,需要我们重写onScrollStateChanged()函数,这里面有三种状态:SCROLL_STATE_IDLE(空闲),SCROLL_STATE_DRAGGING(拖动),SCROLL_STATE_SETTLING(要移动到最后位置时)。
我们需要的就是RecyclerView停止时的状态,我们就可以拿到这个View的Position,注意这里还有一个问题,当你通过这个position去拿Item会报错,这里涉及到RecyclerView的缓存机制,自己去脑补~~。
打印Log,你会发现RecyclerView.getChildCount()一直为1或者会出现为2的情况。来实现一个接口然后通过接口把状态传递出去。
自定义监听listener事件
public interface OnPagerListener { /** * 初始化完成 */ void onInitComplete(); /** * 释放的监听 * @param isNext 是否下一个 * @param position 索引 */ void onPageRelease(boolean isNext,int position); /*** * 选中的监听以及判断是否滑动到底部 * @param position 索引 * @param isBottom 是否到了底部 */ void onPageSelected(int position,boolean isBottom);}
获取到RecyclerView空闲时选中的Item,重写LinearLayoutManager的onScrollStateChanged方法:
/** * 滑动状态的改变 * 缓慢拖拽-> SCROLL_STATE_DRAGGING * 快速滚动-> SCROLL_STATE_SETTLING * 空闲状态-> SCROLL_STATE_IDLE * @param state 状态 */@Overridepublic void onScrollStateChanged(int state) { switch (state) { case RecyclerView.SCROLL_STATE_IDLE: View viewIdle = mPagerSnapHelper.findSnapView(this); int positionIdle = 0; if (viewIdle != null) { positionIdle = getPosition(viewIdle); } if (mOnViewPagerListener != null && getChildCount() == 1) { mOnViewPagerListener.onPageSelected(positionIdle, positionIdle == getItemCount() - 1); } break; case RecyclerView.SCROLL_STATE_DRAGGING: View viewDrag = mPagerSnapHelper.findSnapView(this); if (viewDrag != null) { int positionDrag = getPosition(viewDrag); } break; case RecyclerView.SCROLL_STATE_SETTLING: View viewSettling = mPagerSnapHelper.findSnapView(this); if (viewSettling != null) { int positionSettling = getPosition(viewSettling); } break; default: break; }}
4.3 监听页面是否滚动
这里有两个方法scrollHorizontallyBy()和scrollVerticallyBy()可以拿到滑动偏移量,可以判断滑动方向。
/** * 监听竖直方向的相对偏移量 * @param dy y轴滚动值 * @param recycler recycler * @param state state滚动状态 * @return int值 */@Overridepublic int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { if (getChildCount() == 0 || dy == 0) { return 0; } this.mDrift = dy; return super.scrollVerticallyBy(dy, recycler, state);}/** * 监听水平方向的相对偏移量 * @param dx x轴滚动值 * @param recycler recycler * @param state state滚动状态 * @return int值 */@Overridepublic int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) { if (getChildCount() == 0 || dx == 0) { return 0; } this.mDrift = dx; return super.scrollHorizontallyBy(dx, recycler, state);}
4.4 attach和Detached
列表的选中监听好了,我们就看看什么时候释放视频的资源,第二步中的三种状态,去打印getChildCount()的日志,你会发现getChildCount()在SCROLL_STATE_DRAGGING会为
1. SCROLL_STATE_SETTLING为2,SCROLL_STATE_IDLE有时为1,有时为
2. 还是RecyclerView的缓存机制O(∩∩)O,这里不会去赘述缓存机制,要做的是要知道在什么时候去做释放视频的操作,还要分清是释放上一页还是下一页。
private RecyclerView.OnChildAttachStateChangeListener mChildAttachStateChangeListener = new RecyclerView.OnChildAttachStateChangeListener() { /** * 第一次进入界面的监听,可以做初始化方面的操作 * @param view view */ @Override public void onChildViewAttachedToWindow(@NonNull View view) { if (mOnViewPagerListener != null && getChildCount() == 1) { mOnViewPagerListener.onInitComplete(); } } /** * 页面销毁的时候调用该方法,可以做销毁方面的操作 * @param view view */ @Override public void onChildViewDetachedFromWindow(@NonNull View view) { if (mDrift >= 0){ if (mOnViewPagerListener != null) { mOnViewPagerListener.onPageRelease(true , getPosition(view)); } }else { if (mOnViewPagerListener != null) { mOnViewPagerListener.onPageRelease(false , getPosition(view)); } } }};
哪里添加该listener监听事件,如下所示。这里注意需要在页面销毁的时候移除listener监听事件。
/** * attach到window窗口时,该方法必须调用 * @param recyclerView recyclerView */@Overridepublic void onAttachedToWindow(RecyclerView recyclerView) { //这里省略部分代码 mRecyclerView.addOnChildAttachStateChangeListener(mChildAttachStateChangeListener);}/** * 销毁的时候调用该方法,需要移除监听事件 * @param view view * @param recycler recycler */@Overridepublic void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) { super.onDetachedFromWindow(view, recycler); if (mRecyclerView!=null){ mRecyclerView.removeOnChildAttachStateChangeListener(mChildAttachStateChangeListener); }}
5、优化点详谈
5.1 ViewPager改变滑动速率
可以通过反射修改属性,注意,使用反射的时候,建议手动try-catch,避免异常导致崩溃。代码如下所示:
/** * 修改滑动灵敏度 * @param flingDistance 滑动惯性,默认是75 * @param minimumVelocity 最小滑动值,默认是1200 */public void setScrollFling(int flingDistance , int minimumVelocity){ try { Field mFlingDistance = ViewPager.class.getDeclaredField("mFlingDistance"); mFlingDistance.setAccessible(true); Object o = mFlingDistance.get(this); Log.d("setScrollFling",o.toString()); //默认值75 mFlingDistance.set(this, flingDistance); Field mMinimumVelocity = ViewPager.class.getDeclaredField("mMinimumVelocity"); mMinimumVelocity.setAccessible(true); Object o1 = mMinimumVelocity.get(this); Log.d("setScrollFling",o1.toString()); //默认值1200 mMinimumVelocity.set(this,minimumVelocity); } catch (Exception e){ e.printStackTrace(); }}
5.2 PagerSnapHelper注意点
好多时候会抛出一个异常"illegalstateexception an instance of onflinglistener already set".
看SnapHelper源码attachToRecyclerView(xxx)方法时,可以看到如果recyclerView不为null,则先destoryCallback(),它作用在于取消之前的RecyclerView的监听接口。
然后通过setupCallbacks()设置监听器,如果当前RecyclerView已经设置了OnFlingListener,会抛出一个状态异常。那么这个如何复现了,很容易,你初始化多次就可以看到这个bug。
建议手动捕获一下该异常,代码设置如下所示。源码中判断了,如果onFlingListener已经存在的话,再次设置就直接抛出异常,那么这里可以增强一下逻辑判断,ok,那么问题便解决呢!
try { //attachToRecyclerView源码上的方法可能会抛出IllegalStateException异常,这里手动捕获一下 RecyclerView.OnFlingListener onFlingListener = mRecyclerView.getOnFlingListener(); //源码中判断了,如果onFlingListener已经存在的话,再次设置就直接抛出异常,那么这里可以判断一下 if (onFlingListener==null){ mPagerSnapHelper.attachToRecyclerView(mRecyclerView); }} catch (IllegalStateException e){ e.printStackTrace();}
5.3 自定义LayoutManager注意点
网上有人已经写了一篇自定义LayoutManager实现抖音的效果的博客,我自己也很仔细看了这篇文章。不过我觉得有几个注意要点,因为要用到线上app,则一定要尽可能减少崩溃率……
通过SnapHelper调用findSnapView方法,得到的view,一定要增加非空判断逻辑,否则很容易造成崩溃。
在监听滚动位移scrollVerticallyBy的时候,注意要增加判断,就是getChildCount()如果为0时,则需要返回0。
在onDetachedFromWindow调用的时候,可以把listener监听事件给remove掉。
5.4 视频播放逻辑优化
从前台切到后台,当视频正在播放或者正在缓冲时,调用方法可以设置暂停视频。销毁页面,释放,内部的播放器被释放掉,同时如果在全屏、小窗口模式下都会退出。
从后台切换到前台,当视频暂停时或者缓冲暂停时,调用该方法重新开启视频播放。具体视频播放代码设置如下,具体更加详细内容可以看我封装的视频播放器lib:
https://github.com/yangchong211/YCVideoPlayer
@Overrideprotected void onStop() { super.onStop(); //从前台切到后台,当视频正在播放或者正在缓冲时,调用该方法暂停视频 VideoPlayerManager.instance().suspendVideoPlayer();}@Overrideprotected void onDestroy() { super.onDestroy(); //销毁页面,释放,内部的播放器被释放掉,同时如果在全屏、小窗口模式下都会退出 VideoPlayerManager.instance().releaseVideoPlayer();}@Overridepublic void onBackPressed() { //处理返回键逻辑;如果是全屏,则退出全屏;如果是小窗口,则退出小窗口 if (VideoPlayerManager.instance().onBackPressed()){ return; }else { //销毁页面 VideoPlayerManager.instance().releaseVideoPlayer(); } super.onBackPressed();}@Overrideprotected void onRestart() { super.onRestart(); //从后台切换到前台,当视频暂停时或者缓冲暂停时,调用该方法重新开启视频播放 VideoPlayerManager.instance().resumeVideoPlayer();}
5.5 视频逻辑充分解藕
实际开发中,翻页肯定会涉及到视频的初始化和销毁的逻辑。首先要保证视频只有唯一一个播放,滑动到分页一半,总不可能让两个页面都播放视频吧,所以需要保证视频VideoPlayer是一个单利对象,这样就可以保证唯一性呢!
接着,不管是在recyclerView还是ViewPager中,当页面处于不可见被销毁或者view被回收的阶段,这个时候需要把视频资源销毁,尽量视频播放功能封装起来,然后在页面不同状态调用方法即可。
当然,实际app中,视频播放页面,还有一些***,评论,分享,查看作者等等很多其他功能。那么这些都是要请求接口的,还有滑动分页的功能,当滑动到最后某一页时候拉取下一个视频集合数据等业务逻辑。
视频播放功能这块,因为功能比较复杂,因此封装一下比较好。尽量做到视频功能解藕!关于视频封装库,可以看我之前写的一个库,视频播放器。
https://github.com/yangchong211/YCVideoPlayer
5.6 翻页卡顿优化分析
如果是使用recyclerView实现滑动翻页效果,那么为了提高使用体验效果。则可以注意:
1.在onBindViewHolder中不要做耗时操作;
2.视频滑动翻页的布局固定高度,避免重复计算高度RecyclerView.setHasFixedSize(true);
3.关于分页拉取数据注意,建议一次拉下10条数据(这个也可以和服务端协定自定义数量),而不要滑动一页加载下一页的数据。
5.7 上拉很快翻页黑屏
因为设置视频的背景颜色为黑色,我看了好多播放器初始化的时候,都是这样的。因为最简单的解决办法,就是给它加个封面,设置封面的背景即可。
以上内容来自网络,目的只是为了学习参考和传递资讯。
其版权归原创作者所有,如不慎侵犯了你的权益,请联系我们【qq123456】告知,我们将做删除处理!