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

 

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