有朋友問,抖音的視頻效果怎么實現(xiàn),今天看到了作者做了一個實現(xiàn),真讓人感嘆:
sorry, RecyclerView真的可以為所欲為!文末附小編整理的JAVA的一些干貨敬請領取吧
首先看作者實現(xiàn)的抖音的效果:
時下最火的莫過抖音了,實現(xiàn)這個效果應該很簡單嘛,用ViewPager就可以了。但是等你通過ViewPager來實現(xiàn)的時候,手機內(nèi)存不夠用的情況就會顯現(xiàn)出來。
有沒有更好的方式呢???
自然是有,每個人都會用RecyclerView吧,我們就用RecyclerView來實現(xiàn)這個效果,關于內(nèi)存的回收利用就交給RecyclerView就好了。
那么我們怎么通過RecyclerView來實現(xiàn)這個效果呢?如果你使用過SnapHelper的話,就會很好理解。
1.自定義LayoutManager,并且繼承LinearLayoutManager,這樣就得到一個可以水平排向或者豎向排向的布局策略。
如果你接觸過SnapHelper應該了解一個LinearSnapHelper的類,可以實現(xiàn)讓列表的Item居中顯示的效果。但是這里我們不用這個類,我們要的效果是一次只能滑動一個Item,也就是一頁。PagerSnapHelper就可以做到哦。
@Override public void onAttachedToWindow(RecyclerView view) { super.onAttachedToWindow(view); mPagerSnapHelper.attachToRecyclerView(view); this.mRecyclerView = view; }
2.經(jīng)過第一步基本可以實現(xiàn)抖音的效果,但是寫完之后一會發(fā)現(xiàn),不知道哪里來開始播放視頻和在哪里釋放視頻。
不要著急,要監(jiān)聽滑動到哪頁,需要我們重寫onScrollStateChanged()函數(shù),這里面有三種狀態(tài):
- SCROLL_STATE_IDLE(空閑)
- SCROLL_STATE_DRAGGING(拖動)
- SCROLL_STATE_SETTLING(要移動到最后位置時)
我們需要的就是RecyclerView停止時的狀態(tài),我們就可以拿到這個View的Position.注意這里還有一個問題,當你通過這個position去拿Item會報錯,這里涉及到RecyclerView的緩存機制,自己去腦補~~。
打印Log,你會發(fā)現(xiàn)RecyclerView.getChildCount()一直為1或者會出現(xiàn)為2的情況。好了,我們自己來實現(xiàn)一個接口然后通過接口把狀態(tài)傳遞出去。
監(jiān)聽器
public interface OnViewPagerListener { /*釋放的監(jiān)聽*/ void onPageRelease(boolean isNext,int position); /*選中的監(jiān)聽以及判斷是否滑動到底部*/ void onPageSelected(int position,boolean isBottom); /*布局完成的監(jiān)聽*/ void onLayoutComplete(); }
獲取到RecyclerView空閑時選中的Item,重寫LinearLayoutManager的onScrollStateChanged方法
@Override public void onScrollStateChanged(int state) { switch (state) { case RecyclerView.SCROLL_STATE_IDLE: View viewIdle = mPagerSnapHelper.findSnapView(this); int 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); int positionDrag = getPosition(viewDrag); break; case RecyclerView.SCROLL_STATE_SETTLING: View viewSettling = mPagerSnapHelper.findSnapView(this); int positionSettling = getPosition(viewSettling); break; } }
3.列表的選中監(jiān)聽好了,我們就看看什么時候釋放視頻的資源,第二步中的三種狀態(tài),去打印getChildCount()的日志,你會發(fā)現(xiàn)getChildCount()在:
- SCROLL_STATE_DRAGGING會為1
- SCROLL_STATE_SETTLING為2
- SCROLL_STATE_IDLE有時為1,有時為2
還是RecyclerView的緩存機制,這里不會去贅述緩存機制,我們要做的是要知道在什么時候去做釋放視頻的操作,還要分清是釋放上一頁還是下一頁,因為適配器adapter的position在這里不好使嘛,這里有兩個方法scrollHorizontallyBy()和scrollVerticallyBy()可以拿到滑動偏移量,可以判斷滑動方向,好~ 齊活了。
看代碼:
/** * 監(jiān)聽豎直方向的相對偏移量 * @param dy * @param recycler * @param state * @return */ @Override public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { this.mDrift = dy; return super.scrollVerticallyBy(dy, recycler, state); } /** * 監(jiān)聽水平方向的相對偏移量 * @param dx * @param recycler * @param state * @return */ @Override public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) { this.mDrift = dx; return super.scrollHorizontallyBy(dx, recycler, state); } // 可以釋放資源的監(jiān)聽,也就是回收Item的時候 private RecyclerView.OnChildAttachStateChangeListener mChildAttachStateChangeListener = new RecyclerView.OnChildAttachStateChangeListener() { @Override public void onChildViewAttachedToWindow(View view) { } @Override public void onChildViewDetachedFromWindow(View view) { if (mDrift >= 0){ if (mOnViewPagerListener != null) mOnViewPagerListener.onPageRelease(true,getPosition(view)); }else { if (mOnViewPagerListener != null) mOnViewPagerListener.onPageRelease(false,getPosition(view)); } } };
大功告成。
是不是覺得很不可思議就好了,貼一哈具體使用的代碼,初始化視頻和釋放視頻的地方:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_view_pager_layout_manager); initView(); initListener(); } private void initView() { mRecyclerView = findViewById(R.id.recycler); mLayoutManager = new ViewPagerLayoutManager(this, OrientationHelper.VERTICAL); mAdapter = new MyAdapter(); mRecyclerView.setLayoutManager(mLayoutManager); mRecyclerView.setAdapter(mAdapter); } private void initListener(){ mLayoutManager.setOnViewPagerListener(new OnViewPagerListener() { @Override public void onPageRelease(boolean isNext,int position) { Log.e(TAG,"釋放位置:"+position +" 下一頁:"+isNext); int index = 0; if (isNext){ index = 0; }else { index = 1; } releaseVideo(index); } @Override public void onPageSelected(int position,boolean isBottom) { Log.e(TAG,"選中位置:"+position+" 是否是滑動到底部:"+isBottom); playVideo(0); } @Override public void onLayoutComplete() { playVideo(0); } }); }
作者也曾指出,更多效果,等你來補充。最近的姿勢還是學會作者自定義LayoutManager的方式,掌握真正的內(nèi)功心法。