仿抖音上下滑动播放视频

不少朋友对短视频,上下滑动播放视频效果比较比较感兴趣,今天看看这个案例。

1、效果图:

仿抖音上下滑动播放视频

仿抖音上下滑动播放视频

仿抖音上下滑动播放视频

讲下大概思路,使用Recycleview配合自定义LinearLayoutManager来实现这个功能,这里着重说下自定义LinearLayoutManager的实现可以看到每当下一个item滑入屏幕时,上面的item会继续播放视频,而滑入的item只有当全部进入屏幕才会播放,而且当手指抬起时,当前item会根据滑动的距离相应的自动滑入滑出,针对这种情形,就会想到使用SnapHelper

RecyclerView在24.2.0版本中新增了SnapHelper这个辅助类,用于辅助RecyclerView在滚动结束时将Item对齐到某个位置。特别是列表横向滑动时,很多时候不会让列表滑到任意位置,而是会有一定的规则限制,这时候就可以通过SnapHelper来定义对齐规则了。

SnapHelper是一个抽象类,官方提供了一个LinearSnapHelper的子类,可以让RecyclerView滚动停止时相应的Item停留中间位置。25.1.0版本中官方又提供了一个PagerSnapHelper的子类,可以使RecyclerView像ViewPager一样的效果,一次只能滑一页,而且居中显示,也就是说使用SnapHelper可以帮助RecyclerView滑动完成后进行对齐操作,让item的侧边对齐或者居中对齐,这样实现上下滑动进行视频切换。这里有SnapHelper的详解

2、正式撸代码:1.首先定义一个接口,用来执行item的相关操作

<code>```
public interface OnViewPagerListener {
/*初始化完成*/
void onInitComplete();
/*释放的监听*/
void onPageRelease(boolean isNext, int position);
/*选中的监听以及判断是否滑动到底部*/
void onPageSelected(int position, boolean isBottom);
}
```复制代码/<code>

2.继承LinearLayoutManager ,对滑入滑出的item回调1中接口里面的方法

<code>```
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.PagerSnapHelper;
import android.support.v7.widget.RecyclerView;
import android.view.View;

public class MyLayoutManager extends LinearLayoutManager implements RecyclerView.OnChildAttachStateChangeListener {
private int mDrift;//位移,用来判断移动方向

private PagerSnapHelper mPagerSnapHelper;
private OnViewPagerListener mOnViewPagerListener;

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

public MyLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
mPagerSnapHelper = new PagerSnapHelper();
}

@Override
public void onAttachedToWindow(RecyclerView view) {

view.addOnChildAttachStateChangeListener(this);
mPagerSnapHelper.attachToRecyclerView(view);
super.onAttachedToWindow(view);
}
//当Item添加进来了 调用这个方法

//
@Override
public void onChildViewAttachedToWindow(@NonNull View view) {
// 播放视频操作 即将要播放的是上一个视频 还是下一个视频
int position = getPosition(view);
if (0 == position) {
if (mOnViewPagerListener != null) {
mOnViewPagerListener.onPageSelected(getPosition(view), false);
}

}
}

public void setOnViewPagerListener(OnViewPagerListener mOnViewPagerListener) {
this.mOnViewPagerListener = mOnViewPagerListener;
}

@Override
public void onScrollStateChanged(int state) {
switch (state) {
case RecyclerView.SCROLL_STATE_IDLE:
View view = mPagerSnapHelper.findSnapView(this);
int position = getPosition(view);
if (mOnViewPagerListener != null) {
mOnViewPagerListener.onPageSelected(position, position == getItemCount() - 1);

}
// postion ---回调 ----》播放


break;
}
super.onScrollStateChanged(state);
}

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


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

@Override
public boolean canScrollVertically() {
return true;
}
}
```复制代码/<code>

3.接下来就是正常使用recycleview了 配合原生VideoView 播放视频,切换时先用一张截图盖住视频,视频渲染成功再隐藏截图,感觉上是无缝切换(这里是原生播放器初始加载视频会黑屏,如果用更高级的播放器可能不会有这个问题)

<code>```
import android.annotation.TargetApi;
import android.content.Context;

import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.OrientationHelper;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.VideoView;


public class MainActivity extends AppCompatActivity {
private static final String TAG = "douyin";
private RecyclerView mRecyclerView;
private MyAdapter mAdapter;
MyLayoutManager2 myLayoutManager;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initListener();
}

private void initView() {
mRecyclerView = findViewById(R.id.recycler);
myLayoutManager = new MyLayoutManager2(this, OrientationHelper.VERTICAL, false);

mAdapter = new MyAdapter(this);
mRecyclerView.setLayoutManager(myLayoutManager);
mRecyclerView.setAdapter(mAdapter);

}

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 bottom) {
Log.e(TAG, "选择位置:" + position + " 下一页:" + bottom);

playVideo(0);
}
});
}

class MyAdapter extends RecyclerView.Adapter<myadapter.viewholder> {
private int[] imgs = {R.mipmap.img_video_1, R.mipmap.img_video_2, R.mipmap.img_video_3, R.mipmap.img_video_4, R.mipmap.img_video_5, R.mipmap.img_video_6, R.mipmap.img_video_7, R.mipmap.img_video_8};
private int[] videos = {R.raw.video_1, R.raw.video_2, R.raw.video_3, R.raw.video_4, R.raw.video_5, R.raw.video_6, R.raw.video_7, R.raw.video_8};
private int index = 0;
private Context mContext;

public MyAdapter(Context context) {
this.mContext = context;
}


@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view_pager, parent, false);
return new ViewHolder(view);
}

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.img_thumb.setImageResource(imgs[index]);
holder.videoView.setVideoURI(Uri.parse("android.resource://" + getPackageName() + "/" + videos[index]));
index++;
if (index >= 7) {
index = 0;
}
}

@Override
public int getItemCount() {
return 88;
}

public class ViewHolder extends RecyclerView.ViewHolder {
ImageView img_thumb;
VideoView videoView;
ImageView img_play;
RelativeLayout rootView;

public ViewHolder(View itemView) {
super(itemView);
img_thumb = itemView.findViewById(R.id.img_thumb);
videoView = itemView.findViewById(R.id.video_view);
img_play = itemView.findViewById(R.id.img_play);
rootView = itemView.findViewById(R.id.root_view);
}
}
}

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(position);
final FullWindowVideoView 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.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {

}
});
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.start();

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

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

}
```/<myadapter.viewholder>/<code>


分享到:


相關文章: