转自

目录介绍

01.先来看一下需求

02.有几种实现方式

2.1 使用ViewPager

2.2 使用RecyclerView

03.用ViewPager实现

3.1 自定义ViewPager

3.2 ViewPager和Fragment

3.3 修改滑动距离翻页

3.4 修改滑动速度

04.用RecyclerView实现

4.1 自定义LayoutManager

4.2 添加滑动监听

4.3 监听页面是否滚动

4.4 attach和Detached

05.优化点详谈

5.1 ViewPager改变滑动速率

5.2 PagerSnapHelper注意点

5.3 自定义LayoutManager注意点

5.4 视频播放逻辑优化

5.5 视频逻辑充分解藕

5.6 翻页卡顿优化分析

5.7 上拉很快翻页黑屏

00.先看一下效果图

01.先来看一下需求

项目中的视频播放,要求实现抖音那种竖直方向一次滑动一页的效果。滑动要流畅不卡顿,并且手动触摸滑动超过1/2的时候松开可以滑动下一页,没有超过1/2返回原页。

手指拖动页面滑动,只要没有切换到其他的页面,视频都是在播放的。切换了页面,上一个视频销毁,该页面则开始初始化播放。

切换页面的时候过渡效果要自然,避免出现闪屏。具体的滑动效果,可以直接参考抖音……

02.有几种实现方式

2.1 使用ViewPager

使用ViewPager实现竖直方法上下切换视频分析

1.最近项目需求中有用到需要在ViewPager中播放视频,就是竖直方法上下滑动切换视频,视频是网络视频,最开始的实现思路是ViewPager中根据当前item位置去初始化SurfaceView,同时销毁时根据item的位置移除SurfaceView。

2.上面那种方式确实是可以实现的,但是存在2个问题,第一,MediaPlayer的生命周期不容易控制并且存在内存泄漏问题。第二,连续三个item都是视频时,来回滑动的过程中发现会出现上个视频的最后一帧画面的bug。

3.未提升用户体验,视频播放器初始化完成前上面会覆盖有该视频的第一帧图片,但是发现存在第一帧图片与视频第一帧信息不符的情况,后面会通过代码给出解决方案。

大概的实现思路是这样

1.需要自定义一个竖直方向滑动的ViewPager,注意这个特别重要。

2.一次滑动一页,建议采用ViewPager+FragmentStatePagerAdapter+Fragment方式来做,后面会详细说。

3.在fragment中处理视频的初始化,播放和销毁逻辑等逻辑。

4.由于一个页面需要创建一个fragment,注意性能和滑动流畅度这块需要分析和探讨。

不太建议使用ViewPager

1.ViewPager 自带的滑动效果完全满足场景,而且支持Fragment和View等UI绑定,只要对布局和触摸事件部分作一些修改,就可以把横向的 ViewPager 改成竖向。

2.但是没有复用是个最致命的问题。在onLayout方法中,所有子View会实例化并一字排开在布局上。当Item数量很大时,将会是很大的性能浪费。

3.其次是可见性判断的问题。很多人会以为 Fragment 在 onResume 的时候就是可见的,而 ViewPager 中的 Fragment 就是个反例,尤其是多个 ViewPager 嵌套时,会同时有多个父 Fragment 多个子 Fragment 处于 onResume 的状态,却只有其中一个是可见的。除非放弃 ViewPager 的预加载机制。在页面内容曝光等重要的数据上报时,就需要判断很多条件:onResumed 、 setUserVisibleHint 、 setOnPageChangeListener 等。

2.2 使用RecyclerView

使用RecyclerView实现树枝方向上下切换视频分析

1.首先RecyclerView它设置竖直方向滑动是十分简单的,同时关于item的四级缓存也做好了处理,而且滑动的效果相比ViewPager要好一些。

2.滑动事件处理比viewPager好,即使你外层嵌套了下拉刷新上拉加载的布局,也不影响后期事件冲突处理,详细可以看demo案例。

大概的实现思路是这样

1.自定义一个LinearLayoutManager,重写onScrollStateChanged方法,注意是拿到滑动状态。

2.一次滑动切换一个页面,可以使用PagerSnapHelper来实现,十分方便简单。

3.在recyclerView对应的adapter中,在onCreateViewHolder初始化视频操作,同时当onViewRecycled时,销毁视频资源。

4.添加自定义回调接口,在滚动页面和attch,detach的时候,定义初始化,页面销毁等方法,暴露给开发者。

03.用ViewPager实现

3.1 自定义ViewPager

代码如下所示,这里省略了不少的代码,具体可以看项目中的代码。

/**

*

*    @author 杨充

*    blog  : https://github.com/yangchong211

*    time  : 2019/6/20

*    desc  : 自定义ViewPager,主要是处理边界极端情况

*    revise:

*

*/publicclassVerticalViewPagerextendsViewPager{privatebooleanisVertical=false;privatelongmRecentTouchTime;publicVerticalViewPager(@NonNullContextcontext){super(context);}publicVerticalViewPager(@NonNullContextcontext,@NullableAttributeSetattrs){super(context,attrs);}privatevoidinit(){setPageTransformer(true,newHorizontalVerticalPageTransformer());setOverScrollMode(OVER_SCROLL_NEVER);}publicbooleanisVertical(){returnisVertical;}publicvoidsetVertical(booleanvertical){isVertical=vertical;init();}privateclassHorizontalVerticalPageTransformerimplementsPageTransformer{privatestaticfinalfloatMIN_SCALE=0.25f;@OverridepublicvoidtransformPage(@NonNullViewpage,floatposition){if(isVertical){if(position

* 交换x轴和y轴的移动距离

* @param event 获取事件类型的封装类MotionEvent

*/privateMotionEventswapXY(MotionEventevent){//获取宽高floatwidth=getWidth();floatheight=getHeight();//将Y轴的移动距离转变成X轴的移动距离floatswappedX=(event.getY()/height)*width;//将X轴的移动距离转变成Y轴的移动距离floatswappedY=(event.getX()/width)*height;//重设event的位置event.setLocation(swappedX,swappedY);returnevent;}@OverridepublicbooleanonInterceptTouchEvent(MotionEventev){mRecentTouchTime=System.currentTimeMillis();if(getCurrentItem()==0&&getChildCount()==0){returnfalse;}if(isVertical){booleanintercepted=super.onInterceptTouchEvent(swapXY(ev));swapXY(ev);// return touch coordinates to original reference frame for any child viewsreturnintercepted;}else{returnsuper.onInterceptTouchEvent(ev);}}@OverridepublicbooleanonTouchEvent(MotionEventev){if(getCurrentItem()==0&&getChildCount()==0){returnfalse;}if(isVertical){returnsuper.onTouchEvent(swapXY(ev));}else{returnsuper.onTouchEvent(ev);}}}

3.2 ViewPager和Fragment

采用了ViewPager+FragmentStatePagerAdapter+Fragment来处理。为何选择使用FragmentStatePagerAdapter,主要是因为使用 FragmentStatePagerAdapter更省内存,但是销毁后新建也是需要时间的。一般情况下,如果你是用于ViewPager展示数量特别多的条目时,那么建议使用FragmentStatePagerAdapter。关于PagerAdapter的深度解析,可以我这篇文章:PagerAdapter深度解析和实践优化

在activity中的代码如下所示

privatevoidinitViewPager(){Listlist=newArrayList<>();ArrayListfragments=newArrayList<>();for(inta=0;alist;publicMyPagerAdapter(ArrayListlist,FragmentManager fm){super(fm);this.list=list;}@OverridepublicFragmentgetItem(inti){returnlist.get(i);}@OverridepublicintgetCount(){returnlist!=null?list.size():0;}}

那么在fragment中如何处理呢?关于视频播放器,这里可以看我封装的库,视频lib

publicclassVideoFragmentextendsFragment{publicVideoPlayer videoPlayer;privateString url;privateint index;@OverridepublicvoidonStop(){super.onStop();VideoPlayerManager.instance().releaseVideoPlayer();}publicstaticFragmentnewInstant(String url){VideoFragment videoFragment=newVideoFragment();Bundle bundle=newBundle();bundle.putString("url",url);videoFragment.setArguments(bundle);returnvideoFragment;}@OverridepublicvoidonCreate(@Nullable Bundle savedInstanceState){super.onCreate(savedInstanceState);Bundle arguments=getArguments();if(arguments!=null){url=arguments.getString("url");}}@Nullable    @OverridepublicViewonCreateView(@NonNull LayoutInflater inflater,@Nullable ViewGroup container,@Nullable Bundle savedInstanceState){View view=inflater.inflate(R.layout.fragment_video,container,false);returnview;}@OverridepublicvoidonViewCreated(@NonNull View view,@Nullable Bundle savedInstanceState){super.onViewCreated(view,savedInstanceState);videoPlayer=view.findViewById(R.id.video_player);}@OverridepublicvoidonActivityCreated(@Nullable Bundle savedInstanceState){super.onActivityCreated(savedInstanceState);Log.d("初始化操作","------"+index++);VideoPlayerController controller=newVideoPlayerController(getActivity());videoPlayer.setUp(url,null);videoPlayer.setPlayerType(ConstantKeys.IjkPlayerType.TYPE_IJK);videoPlayer.setController(controller);ImageUtils.loadImgByPicasso(getActivity(),"",R.drawable.image_default,controller.imageView());}}

3.3 修改滑动距离翻页

需求要求必须手动触摸滑动超过1/2的时候松开可以滑动下一页,没有超过1/2返回原页,首先肯定是重写viewpager,只能从源码下手。经过分析,源码滑动的逻辑处理在此处,truncator的属性代表判断的比例值!

这个方法会在切页的时候重定向Page,比如从第一个页面滑动,结果没有滑动到第二个页面,而是又返回到第一个页面,那个这个page会有重定向的功能

privateintdetermineTargetPage(intcurrentPage,floatpageOffset,intvelocity,intdeltaX){inttargetPage;if(Math.abs(deltaX)>this.mFlingDistance&&Math.abs(velocity)>this.mMinimumVelocity){targetPage=velocity>0?currentPage:currentPage+1;}else{floattruncator=currentPage>=this.mCurItem?0.4F:0.6F;targetPage=currentPage+(int)(pageOffset+truncator);}if(this.mItems.size()>0){ViewPager.ItemInfo firstItem=(ViewPager.ItemInfo)this.mItems.get(0);ViewPager.ItemInfo lastItem=(ViewPager.ItemInfo)this.mItems.get(this.mItems.size()-1);targetPage=Math.max(firstItem.position,Math.min(targetPage,lastItem.position));}returntargetPage;}

determineTargetPage这个方法就是计算接下来要滑到哪一页。这个方法调用是在MotionEvent.ACTION_UP这个事件下,先说下参数意思:

currentPage:当前ViewPager显示的页面

pageOffset:用户滑动的页面偏移量

velocity: 滑动速率

deltaX: X方向移动的距离

进行debug调试之后,发现问题就在0.4f和0.6f这个参数上。分析得出:0.6f表示用户滑动能够翻页的偏移量,所以不难理解,为啥要滑动半屏或者以上了。

也可以修改Touch事件

控制ViewPager的Touch事件,这个基本是万能的,毕竟是从根源上入手的。你可以在onTouchEvent和onInterceptTouchEvent中做逻辑的判断。但是比较麻烦。

3.4 修改滑动速度

使用viewPager进行滑动时,如果通过手指滑动来进行的话,可以根据手指滑动的距离来实现,但是如果通过setCurrentItem函数来实现的话,则会发现直接闪过去的,会出现一下刷屏。想要通过使用setCurrentItem函数来进行viewpager的滑动,并且需要有过度滑动的动画,那么,该如何做呢?

具体可以分析setCurrentItem源码的逻辑,然后会看到scrollToItem方法,这个特别重要,主要是处理滚动过程中的逻辑。最主要关心的也是smoothScrollTo函数,这个函数中,可以看到具体执行滑动的其实就一句话,就是mScroller.startScroll(sx,sy,dx,dy,duration),则可以看到,是mScroller这个对象进行滑动的。那么想要改变它的属性,则可以通过反射来实现。

代码如下所示,如果是手指触摸滑动,则可以加快一点滑动速率,当然滑动持续时间你可以自己设置。通过自己自定义滑动的时间,就可以控制滑动的速度。

@TargetApi(Build.VERSION_CODES.KITKAT)publicvoidsetAnimationDuration(finalintduring){try{// viewPager平移动画事件Field mField=ViewPager.class.getDeclaredField("mScroller");mField.setAccessible(true);// 动画效果与ViewPager的一致Interpolator interpolator=newInterpolator(){@OverridepublicfloatgetInterpolation(floatt){t-=1.0f;returnt*t*t*t*t+1.0f;}};Scroller mScroller=newScroller(getContext(),interpolator){finalinttime=2000;@OverridepublicvoidstartScroll(intx,inty,intdx,intdy,intduration){// 如果手工滚动,则加速滚动if(System.currentTimeMillis()-mRecentTouchTime>time){duration=during;}else{duration/=2;}super.startScroll(x,y,dx,dy,duration);}@OverridepublicvoidstartScroll(intx,inty,intdx,intdy){super.startScroll(x,y,dx,dy,during);}};mField.set(this,mScroller);}catch(NoSuchFieldException|IllegalAccessException|IllegalArgumentException e){e.printStackTrace();}}

04.用RecyclerView实现

4.1 自定义LayoutManager

自定义LayoutManager,并且继承LinearLayoutManager,这样就得到一个可以水平排向或者竖向排向的布局策略。如果你接触过SnapHelper应该了解一下LinearSnapHelper和PagerSnapHelper这两个子类类,LinearSnapHelper可以实现让列表的Item居中显示的效果,PagerSnapHelper就可以做到一次滚动一个item显示的效果。

重写onChildViewAttachedToWindow方法,在RecyclerView中,当Item添加进来了调用这个方法。这个方法相当于是把view添加到window时候调用的,也就是说它比draw方法先执行,可以做一些初始化相关的操作。

/**

* 该方法必须调用

* @param recyclerView                          recyclerView

*/@OverridepublicvoidonAttachedToWindow(RecyclerViewrecyclerView){if(recyclerView==null){thrownewIllegalArgumentException("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事件

publicinterfaceOnPagerListener{/**

* 初始化完成

*/voidonInitComplete();/**

* 释放的监听

* @param isNext                    是否下一个

* @param position                  索引

*/voidonPageRelease(booleanisNext,intposition);/***

* 选中的监听以及判断是否滑动到底部

* @param position                  索引

* @param isBottom                  是否到了底部

*/voidonPageSelected(intposition,booleanisBottom);}

获取到RecyclerView空闲时选中的Item,重写LinearLayoutManager的onScrollStateChanged方法

/**

* 滑动状态的改变

* 缓慢拖拽-> SCROLL_STATE_DRAGGING

* 快速滚动-> SCROLL_STATE_SETTLING

* 空闲状态-> SCROLL_STATE_IDLE

* @param state                        状态

*/@OverridepublicvoidonScrollStateChanged(int state){switch(state){caseRecyclerView.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;caseRecyclerView.SCROLL_STATE_DRAGGING:View viewDrag=mPagerSnapHelper.findSnapView(this);if(viewDrag!=null){int positionDrag=getPosition(viewDrag);}break;caseRecyclerView.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值

*/@OverridepublicintscrollVerticallyBy(intdy,RecyclerView.Recyclerrecycler,RecyclerView.Statestate){if(getChildCount()==0||dy==0){return0;}this.mDrift=dy;returnsuper.scrollVerticallyBy(dy,recycler,state);}/**

* 监听水平方向的相对偏移量

* @param dx                                x轴滚动值

* @param recycler                          recycler

* @param state                            state滚动状态

* @return                                  int值

*/@OverridepublicintscrollHorizontallyBy(intdx,RecyclerView.Recyclerrecycler,RecyclerView.Statestate){if(getChildCount()==0||dx==0){return0;}this.mDrift=dx;returnsuper.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,这里不会去赘述缓存机制,要做的是要知道在什么时候去做释放视频的操作,还要分清是释放上一页还是下一页。

privateRecyclerView.OnChildAttachStateChangeListenermChildAttachStateChangeListener=newRecyclerView.OnChildAttachStateChangeListener(){/**

* 第一次进入界面的监听,可以做初始化方面的操作

* @param view                      view

*/@OverridepublicvoidonChildViewAttachedToWindow(@NonNullViewview){if(mOnViewPagerListener!=null&&getChildCount()==1){mOnViewPagerListener.onInitComplete();}}/**

* 页面销毁的时候调用该方法,可以做销毁方面的操作

* @param view                      view

*/@OverridepublicvoidonChildViewDetachedFromWindow(@NonNullViewview){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

*/@OverridepublicvoidonAttachedToWindow(RecyclerViewrecyclerView){//这里省略部分代码mRecyclerView.addOnChildAttachStateChangeListener(mChildAttachStateChangeListener);}/**

* 销毁的时候调用该方法,需要移除监听事件

* @param view                                  view

* @param recycler                              recycler

*/@OverridepublicvoidonDetachedFromWindow(RecyclerViewview,RecyclerView.Recyclerrecycler){super.onDetachedFromWindow(view,recycler);if(mRecyclerView!=null){mRecyclerView.removeOnChildAttachStateChangeListener(mChildAttachStateChangeListener);}}

05.优化点详谈

5.1 ViewPager改变滑动速率

可以通过反射修改属性,注意,使用反射的时候,建议手动try-catch,避免异常导致崩溃。代码如下所示:

/**

* 修改滑动灵敏度

* @param flingDistance                    滑动惯性,默认是75

* @param minimumVelocity                  最小滑动值,默认是1200

*/publicvoidsetScrollFling(int flingDistance,int minimumVelocity){try{Field mFlingDistance=ViewPager.class.getDeclaredField("mFlingDistance");mFlingDistance.setAccessible(true);Object o=mFlingDistance.get(this);Log.d("setScrollFling",o.toString());//默认值75mFlingDistance.set(this,flingDistance);Field mMinimumVelocity=ViewPager.class.getDeclaredField("mMinimumVelocity");mMinimumVelocity.setAccessible(true);Object o1=mMinimumVelocity.get(this);Log.d("setScrollFling",o1.toString());//默认值1200mMinimumVelocity.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.OnFlingListeneronFlingListener=mRecyclerView.getOnFlingListener();//源码中判断了,如果onFlingListener已经存在的话,再次设置就直接抛出异常,那么这里可以判断一下if(onFlingListener==null){mPagerSnapHelper.attachToRecyclerView(mRecyclerView);}}catch(IllegalStateExceptione){e.printStackTrace();}

5.3 自定义LayoutManager注意点

网上有人已经写了一篇自定义LayoutManager实现抖音的效果的博客,我自己也很仔细看了这篇文章。不过我觉得有几个注意要点,因为要用到线上app,则一定要尽可能减少崩溃率……

通过SnapHelper调用findSnapView方法,得到的view,一定要增加非空判断逻辑,否则很容易造成崩溃。

在监听滚动位移scrollVerticallyBy的时候,注意要增加判断,就是getChildCount()如果为0时,则需要返回0。

在onDetachedFromWindow调用的时候,可以把listener监听事件给remove掉。

5.4 视频播放逻辑优化

从前台切到后台,当视频正在播放或者正在缓冲时,调用方法可以设置暂停视频。销毁页面,释放,内部的播放器被释放掉,同时如果在全屏、小窗口模式下都会退出。从后台切换到前台,当视频暂停时或者缓冲暂停时,调用该方法重新开启视频播放。具体视频播放代码设置如下,具体更加详细内容可以看我封装的视频播放器lib:

@OverrideprotectedvoidonStop(){super.onStop();//从前台切到后台,当视频正在播放或者正在缓冲时,调用该方法暂停视频VideoPlayerManager.instance().suspendVideoPlayer();}@OverrideprotectedvoidonDestroy(){super.onDestroy();//销毁页面,释放,内部的播放器被释放掉,同时如果在全屏、小窗口模式下都会退出VideoPlayerManager.instance().releaseVideoPlayer();}@OverridepublicvoidonBackPressed(){//处理返回键逻辑;如果是全屏,则退出全屏;如果是小窗口,则退出小窗口if(VideoPlayerManager.instance().onBackPressed()){return;}else{//销毁页面VideoPlayerManager.instance().releaseVideoPlayer();}super.onBackPressed();}@OverrideprotectedvoidonRestart(){super.onRestart();//从后台切换到前台,当视频暂停时或者缓冲暂停时,调用该方法重新开启视频播放VideoPlayerManager.instance().resumeVideoPlayer();}

5.5 视频逻辑充分解藕

实际开发中,翻页肯定会涉及到视频的初始化和销毁的逻辑。首先要保证视频只有唯一一个播放,滑动到分页一半,总不可能让两个页面都播放视频吧,所以需要保证视频VideoPlayer是一个单利对象,这样就可以保证唯一性呢!接着,不管是在recyclerView还是ViewPager中,当页面处于不可见被销毁或者view被回收的阶段,这个时候需要把视频资源销毁,尽量视频播放功能封装起来,然后在页面不同状态调用方法即可。

当然,实际app中,视频播放页面,还有一些点赞,评论,分享,查看作者等等很多其他功能。那么这些都是要请求接口的,还有滑动分页的功能,当滑动到最后某一页时候拉取下一个视频集合数据等业务逻辑。视频播放功能这块,因为功能比较复杂,因此封装一下比较好。尽量做到视频功能解藕!关于视频封装库,可以看我之前写的一个库,视频播放器。

5.6 翻页卡顿优化分析

如果是使用recyclerView实现滑动翻页效果,那么为了提高使用体验效果。则可以注意:1.在onBindViewHolder中不要做耗时操作,2.视频滑动翻页的布局固定高度,避免重复计算高度RecyclerView.setHasFixedSize(true),3.关于分页拉取数据注意,建议一次拉下10条数据(这个也可以和服务端协定自定义数量),而不要滑动一页加载下一页的数据。

5.7 上拉很快翻页黑屏

因为设置视频的背景颜色为黑色,我看了好多播放器初始化的时候,都是这样的。因为最简单的解决办法,就是给它加个封面,设置封面的背景即可。

作者:飞鱼_9d08

链接:https://www.jianshu.com/p/53e4a1c0bd62

来源:简书

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

抖音只能上下滑动吗_仿抖音上下滑动分页视频相关推荐

  1. 抖音只能上下滑动吗_仿抖音上下滑动播放视频

    太多朋友对短视频,上下滚动播放视频效果比较比较研究,今天看看这个案例. 讲下大概思路,使用Recycleview配合自定义LinearLayoutManager来实现这个功能,这里着重说下自定义Lin ...

  2. 仿抖你妹原版主题源码/仿抖音模式套图WordPress图片主题模板

    仿抖你妹原版主题源码/仿抖音模式套图WordPress图片主题模板 ☑️ 编号:ym430 ☑️ 品牌:WordPress ☑️ 语言:php ☑️ 大小:5.4MB ☑️ 类型:仿抖音模式套图 ☑️ ...

  3. 抖你妹原版主题源码 仿抖音模式套图SEO源码 全开源无授权 WordPress图片主题

    靓图库模板抖你妹原版主题源码,仿抖音模式套图SEO源码,wordpress图片主题不送数据!此源码为wordpress模板,需先安装wordpress后才能使用! 注意:PHP版本越高越好,PHP必须 ...

  4. 抖音只能上下滑动吗_iOS仿抖音—上下滑动播放视频

    iOS仿抖音短视频 图做的不太好,将就看吧 2018.12.12 添加了tabbar,数据写死到了本地(接口过一段时间就会访问没数据) dy_tabbar.gif 前言 上一篇文章仿写了抖音的左右滑动 ...

  5. 抖音高贵气质的签名_这些抖音直播注意事项,不注意很可能被封号!

    3月16日开始,抖音直播推出"百万开麦,抖音主播扶持计划 ",想推动直播成为短视频达人的标配. 不管是从整个互联网大环境还是从各平台的政策来看,直播都是2020年平台.企业和个人必 ...

  6. 罗马音平假字复制_平假名|罗马音平假名和片假名大全ID,罗马音平假名和片假名大全复制中文[多图]_234游戏网...

    罗马音平假名和片假名大全ID可复制粘贴版,是一款能够帮助用户生成各种趣味名字的软件,软件功能强大,用户在线一键即可生成任何你喜欢的网名,并且全部都能直接复制实用,全部都是免费分享,还支持中英文转换哦. ...

  7. java抖音获取用户信息失败_为什么抖音用id搜不到用户?抖音用id搜不到用户的原因与解决方法...

    在抖音短视频上,网友可以通过抖音id来搜索指定的抖音用户,并添加为好友,不过,这几天,不断有网友反映一个问题,那就是:抖音用id搜不到用户,那么,为什么抖音用id搜不到用户?今天,小编就为大家介绍一下 ...

  8. 响应式滑动菜单_如何创建响应式滑动菜单

    响应式滑动菜单 by Prashant Yadav 通过Prashant Yadav 如何创建响应式滑动菜单 (How to create a responsive sliding menu) I r ...

  9. android微信列表滑动删除,Android仿微信对话列表滑动删除效果

    微信对话列表滑动删除效果很不错的,借鉴了github上SwipeListView(项目地址:https://github.com/likebamboo/SwipeListView),在其上进行了一些重 ...

  10. 罗马音平假字复制_五十音图的那些平假字和片假字有罗马音吗?

    你好!每一个平假名都有它相对应的罗马音. 日语假名及其发音一览平假 片假 罗马音平假 片假 罗马音平假 片假 罗马音平假 片假 罗马音平假 片假 罗马音 あ ア a い イ i う ウ u え エ e ...

最新文章

  1. 支付宝账单出来后,除了总消费,你看到你的学习支出了吗?
  2. [JLOI2011]飞行路线
  3. 完美解决distinct中使用多个字段的方法
  4. magento 的一些关于addFieldToFilter的查询
  5. 为啥你写的代码总是这么复杂?
  6. grub开机选项中没有linux,安装 Ubuntu 后 Grub 开机选项中的 Fedora 不见了
  7. java异常中return_求大神!!!!java 异常处理中return的意思
  8. DataGridView下拉框动态赋值以及事件处理
  9. 求1000的阶乘c语言编程,用C语言编写程序,求出1000的阶乘?
  10. 梯度下降的线性回归用python_一元线性回归和梯度下降的python代码实现
  11. 【职业经验】测试转研发的一年总结
  12. idea 设置内存_IntelliJ IDEA修改内存使得变得流畅
  13. 【题解】洛谷P2914[USACO08OCT]断电Power Failure
  14. 词嵌入生成词向量_使用词嵌入创建诗生成器
  15. Linux tcpdump命令详解
  16. win系统服务器白名单,win10系统如何添加白名单 windows10下添加白名单的方法
  17. 跟着Leo机器学习:sklearn之Nearest Neighbors
  18. 使用 Amazon Personalize 快速搭建推荐服务
  19. [置顶]定向爬虫 - Python模拟新浪微博登录
  20. 从从协方差的误差椭圆到PCA

热门文章

  1. Generative Adversarial Networks overview(1)
  2. C++实训easyx打地鼠(动画版)
  3. 企业间数据竞争规则研究
  4. 通过JavaScript脚本实现60秒倒计时
  5. 小程序:canvas绘制网络图片
  6. 如何解决苹果账号续费时支付失败的问题
  7. java实现excel 行列转置,行列转换。附完整代码
  8. 微信支付接口调用之统一下单(一)
  9. Python顺序结构
  10. ftp服务器的作用及意义,FTP服务器的作用