Android自定義LayoutManger-仿寫抖音主界面

看下項目整體實現效果,上下翻頁時完成視頻切換:

視頻的切換這裏我們使用RecyclerView進行實現,核心是需要自定義LayoutManager;視頻播放我們使用VideoView控件。

在MyLayoutManager加載成功後,我們需要添加監聽addOnChildAttachStateChangeListener,目的是監聽到RecyclerView中一個item的滑出以及下一個item的進入,以方便我們關閉上一個滑出item的視頻播放,以及開始下一個劃入item的視頻播放。

    @Override
    public void onAttachedToWindow(RecyclerView view) {
        // 監聽子item上一個移除,下一個進入的回調方法
        // 重寫:onChildViewAttachedToWindow,onChildViewDetachedFromWindow
        view.addOnChildAttachStateChangeListener(this);
        mPagerSnapHelper.attachToRecyclerView(view);
        super.onAttachedToWindow(view);
    }

    //當Item添加進來了調用這個方法
    @Override
    public void onChildViewAttachedToWindow(@NonNull View view) {
        //播放視頻操作:即將要播放的是上一個視頻,還是下一個視頻
        if (mDrift > 0) {
            //向上
            if (mOnViewPagerListener != null) {
                mOnViewPagerListener.onPageSelected(getPosition(view), true);
            }

        } else {
            if (mOnViewPagerListener != null) {
                mOnViewPagerListener.onPageSelected(getPosition(view), false);
            }
        }
    }

    @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));
        }
    }

我們監聽scrollVerticallyBy方法即可判斷出是上滑還是下滑,進而完成暫停上一個視頻後播放下一個視頻:

    @Override
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        this.mDrift = dy;
        return super.scrollVerticallyBy(dy, recycler, state);
    }

在MainActivity的回調中實現視頻播放關閉即可。

設置自定義LayoutManager的回調監聽,以便MainActivity完成視頻暫停、關閉、開始播放的邏輯:

設置的監聽回調爲:

public interface OnViewPagerListener {
    /*初始化完成*/
    void onInitComplete();

    /*釋放的監聽*/
    void onPageRelease(boolean isNext, int position);

    /*選中的監聽以及判斷是否滑動到底部*/
    void onPageSelected(int position, boolean isBottom);

}

接口實現:

    private void initListener() {
        myLayoutManager.setOnViewPagerListener(new OnViewPagerListener() {
            @Override
            public void onInitComplete() {

            }

            @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 isNext) {
                Log.e(TAG, "釋放位置:" + position + " 下一頁:" + isNext);

                int index = 0;
                if (isNext) {
                    index = 0;
                } else {
                    index = 1;
                }
                playVideo(index);
            }
        });
    }

我們使用VideoView進行視頻的播放控制:

    private void releaseVideo(int index) {
        View itemView = mRecyclerView.getChildAt(index);
        final VideoView videoView = itemView.findViewById(R.id.video_view);
        final ImageView imgThumb = itemView.findViewById(R.id.img_thumb);
        final ImageView imgPlay = itemView.findViewById(R.id.img_play);
        videoView.stopPlayback();
        imgThumb.animate().alpha(1).start();
        imgPlay.animate().alpha(0f).start();
    }


    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    private void playVideo(int position) {
        View itemView = mRecyclerView.getChildAt(0);
        final VideoView videoView = itemView.findViewById(R.id.video_view);
        final ImageView imgPlay = itemView.findViewById(R.id.img_play);
        final ImageView imgThumb = itemView.findViewById(R.id.img_thumb);
        final RelativeLayout rootView = itemView.findViewById(R.id.root_view);
        final MediaPlayer[] mediaPlayer = new MediaPlayer[1];
        videoView.start();
        videoView.setOnInfoListener(new MediaPlayer.OnInfoListener() {
            @Override
            public boolean onInfo(MediaPlayer mp, int what, int extra) {
                mediaPlayer[0] = mp;
                mp.setLooping(true);
                imgThumb.animate().alpha(0).setDuration(200).start();
                return false;
            }
        });
        videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mp) {

            }
        });


        imgPlay.setOnClickListener(new View.OnClickListener() {
            boolean isPlaying = true;

            @Override
            public void onClick(View v) {
                if (videoView.isPlaying()) {
                    imgPlay.animate().alpha(1f).start();
                    videoView.pause();
                    isPlaying = false;
                } else {
                    imgPlay.animate().alpha(0f).start();
                    videoView.start();
                    isPlaying = true;
                }
            }
        });
    }

當我們滑動RecyclerView中的子item到一半時它是定位在那裏不會繼續滑動的,而viewPager是可以當一個page滑動到一多半時,鬆手後這個page會自動滑出來;爲了讓RecyclerView實現和viewPager一樣的鬆手滑動的效果,我們還需要使用PagerSnapHelper實現滑動監聽,可以看到這個類最終是繼承自RecyclerView.OnFlingListener,這個類的作用就是手指猛然滑動item時,RecyclerView能夠成功滑動到下一個完整的item。

還有需要注意的一點是onChildViewAttachedToWindow和onChildViewDetachedFromWindow方法並不是成對的出現,例如上面的item高度是30dp,而他下面的item高度是300dp,當上面item移除出界面時會調用detach但是下面由於過高還沒有觸發到attached方法,因此我們應該還需要監聽RecyclerView的onScrollStateChanged方法進行是否滑動完成狀態的監聽,當滑動完成時,使用RecyclerView.SCROLL_STATE_IDLE這個狀態進行下一個item的視頻播放的觸發點即可。

此處還有個小bug,使用videoView播放視頻時候無法撐滿屏幕,需要重寫他的onMeasure方法:

public class FullScreenVideoView extends VideoView {

    public FullScreenVideoView(Context context) {
        super(context);
    }

    public FullScreenVideoView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FullScreenVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        int width = wm.getDefaultDisplay().getWidth();
        int height = wm.getDefaultDisplay().getHeight();
        setMeasuredDimension(width, height);
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章