有朋友問,抖音的視頻效果怎么實現,今天看到了作者做了一個實現,真讓人感嘆:
sorry, RecyclerView真的可以為所欲為!文末附小編整理的JAVA的一些干貨敬請領取吧
首先看作者實現的抖音的效果:
時下最火的莫過抖音了,實現這個效果應該很簡單嘛,用ViewPager就可以了。但是等你通過ViewPager來實現的時候,手機內存不夠用的情況就會顯現出來。
有沒有更好的方式呢???
自然是有,每個人都會用RecyclerView吧,我們就用RecyclerView來實現這個效果,關于內存的回收利用就交給RecyclerView就好了。
那么我們怎么通過RecyclerView來實現這個效果呢?如果你使用過SnapHelper的話,就會很好理解。
1.自定義LayoutManager,并且繼承LinearLayoutManager,這樣就得到一個可以水平排向或者豎向排向的布局策略。
如果你接觸過SnapHelper應該了解一個LinearSnapHelper的類,可以實現讓列表的Item居中顯示的效果。但是這里我們不用這個類,我們要的效果是一次只能滑動一個Item,也就是一頁。PagerSnapHelper就可以做到哦。
@Override public void onAttachedToWindow(RecyclerView view) { super.onAttachedToWindow(view); mPagerSnapHelper.attachToRecyclerView(view); this.mRecyclerView = view; }
2.經過第一步基本可以實現抖音的效果,但是寫完之后一會發現,不知道哪里來開始播放視頻和在哪里釋放視頻。
不要著急,要監聽滑動到哪頁,需要我們重寫onScrollStateChanged()函數,這里面有三種狀態:
- SCROLL_STATE_IDLE(空閑)
- SCROLL_STATE_DRAGGING(拖動)
- SCROLL_STATE_SETTLING(要移動到最后位置時)
我們需要的就是RecyclerView停止時的狀態,我們就可以拿到這個View的Position.注意這里還有一個問題,當你通過這個position去拿Item會報錯,這里涉及到RecyclerView的緩存機制,自己去腦補~~。
打印Log,你會發現RecyclerView.getChildCount()一直為1或者會出現為2的情況。好了,我們自己來實現一個接口然后通過接口把狀態傳遞出去。
監聽器
public interface OnViewPagerListener { /*釋放的監聽*/ void onPageRelease(boolean isNext,int position); /*選中的監聽以及判斷是否滑動到底部*/ void onPageSelected(int position,boolean isBottom); /*布局完成的監聽*/ 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.列表的選中監聽好了,我們就看看什么時候釋放視頻的資源,第二步中的三種狀態,去打印getChildCount()的日志,你會發現getChildCount()在:
- SCROLL_STATE_DRAGGING會為1
- SCROLL_STATE_SETTLING為2
- SCROLL_STATE_IDLE有時為1,有時為2
還是RecyclerView的緩存機制,這里不會去贅述緩存機制,我們要做的是要知道在什么時候去做釋放視頻的操作,還要分清是釋放上一頁還是下一頁,因為適配器adapter的position在這里不好使嘛,這里有兩個方法scrollHorizontallyBy()和scrollVerticallyBy()可以拿到滑動偏移量,可以判斷滑動方向,好~ 齊活了。
看代碼:
/** * 監聽豎直方向的相對偏移量 * @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); } /** * 監聽水平方向的相對偏移量 * @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); } // 可以釋放資源的監聽,也就是回收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的方式,掌握真正的內功心法。