代碼下載:https://github.com/TangfeiJi/DouYinViewPager
時下最火的莫過抖音了,實現(xiàn)這個效果應(yīng)該很簡單嘛,用ViewPager就可以了。但是等你通過ViewPager來實現(xiàn)的時候,手機內(nèi)存不夠用的情況就會顯現(xiàn)出來。
每個人都會用RecyclerView吧,我們就用RecyclerView來實現(xiàn)這個效果,關(guān)于內(nèi)存的回收利用就交給RecyclerView就好了。
那么我們怎么通過RecyclerView來實現(xiàn)這個效果呢?如果你使用過SnapHelper的話,就會很好理解。


1.自定義LayoutManager,并且繼承LinearLayoutManager,這樣就得到一個可以水平排向或者豎向排向的布局策略。
如果你接觸過SnapHelper應(yīng)該了解一個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)聽器
publicinterface 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) {
caseRecyclerView.SCROLL_STATE_IDLE:
View viewIdle = mPagerSnapHelper.findSnapView(this);
intpositionIdle = getPosition(viewIdle);
if(mOnViewPagerListener !=null&& getChildCount() ==1) {
mOnViewPagerListener.onPageSelected(positionIdle,positionIdle == getItemCount() -1);
? ? ? ? ?}
break;
caseRecyclerView.SCROLL_STATE_DRAGGING:
View viewDrag = mPagerSnapHelper.findSnapView(this);
intpositionDrag = getPosition(viewDrag);
break;
caseRecyclerView.SCROLL_STATE_SETTLING:
View viewSettling = mPagerSnapHelper.findSnapView(this);
intpositionSettling = 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;
returnsuper.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;
returnsuper.scrollHorizontallyBy(dx, recycler, state);
}
// 可以釋放資源的監(jiān)聽,也就是回收Item的時候
privateRecyclerView.OnChildAttachStateChangeListener mChildAttachStateChangeListener =newRecyclerView.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 =newViewPagerLayoutManager(this, OrientationHelper.VERTICAL);
mAdapter =newMyAdapter();
? ?mRecyclerView.setLayoutManager(mLayoutManager);
? ?mRecyclerView.setAdapter(mAdapter);
}
private void initListener(){
mLayoutManager.setOnViewPagerListener(newOnViewPagerListener() {
@Override
public void onPageRelease(boolean isNext,int position) {
Log.e(TAG,"釋放位置:"+position +" 下一頁:"+isNext);
intindex =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);
? ? ? ?}
? ?});