前言:*近项目比较忙很久没写了,一个月迭代了三个版本也是醉了。。。谁叫我们是苦逼的程序猿呢,回归正传;*近主要在弄一个跟视频有关的项目,里面也学到一些东西,现在记录一下;其中一个是仿微博的视频列表自动播放功能,具体可以看下图:

%title插图%num

项目中视频使用的是GSYVideoPlayer 这个开源库。
ps:作者很热心,nice。

废话不多说,直接进入重点;既然要实现类似微博那种滚动列表时,处于当前屏幕的项自动播放,那么肯定得监听列表的滚动事件,好在Recyclerview中给我们提供了接口:

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
switch (newState) {
case SCROLL_STATE_IDLE: //滚动停止
break;
case SCROLL_STATE_DRAGGING: //手指拖动
break;
case SCROLL_STATE_SETTLING: //惯性滚动
break;
}
}

@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}
});

如上onScrollStateChanged 方法 给我们返回了三种状态:
SCROLL_STATE_DRAGGING: 手指按住屏幕拖动
SCROLL_STATE_SETTLING: 手指快速在屏幕滑一下后的惯性滑动
SCROLL_STATE_IDLE: 屏幕处于禁止状态

而onScrolled 方法给我们返回了 dx:水平滚动距离、dy:垂直滚动距离。这两个值都是用手指开始触摸的位置减去移动后的位置,所以:
dx > 0 时为手指向左滑动,列表滚动显示右面的内容
dx < 0 时为手指向右滑动,列表滚动显示左面的内容
dy > 0 时为手指向上滑动,列表滚动显示下面的内容
dy < 0 时为手指向下滑动,列表滚动显示上面的内容
项目中暂时没用到这些,但是这些值很有用。

在这个方法中我们需要获取三个值:
(1) 在屏幕可见区域的*项位置 : 通过findFirstVisibleItemPosition()方法获取
(2) 在屏幕可见区域的*后一项位置 : 通过findLastVisibleItemPosition() 方法获取
(3)屏幕可见项的数目 : 用(2)减去(1)即可

为什么需要以上值,这里说下整体思路:
(1)获取当前处于屏幕可见的列表
(2)滑出屏幕的视频我们需要回收掉
(3)当屏幕处于静止状态时我们才开始播放视频

*点:获取可见列表;上面获取的三个值已经解决了,直接看代码:

public int firstVisibleItem, lastVisibleItem, visibleCount;

@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
firstVisibleItem = layoutManager.findFirstVisibleItemPosition();
lastVisibleItem = layoutManager.findLastVisibleItemPosition();
visibleCount = lastVisibleItem – firstVisibleItem;
}

第二点:回收滑出屏幕的视频,获取到当前播放的位置(开源库中有相关方法)判断是否在屏幕可见,不可见就回收;具体看代码

@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
//大于0说明有播放
if (GSYVideoManager.instance().getPlayPosition() >= 0) {
//当前播放的位置
int position = GSYVideoManager.instance().getPlayPosition();
//对应的播放列表TAG
if (GSYVideoManager.instance().getPlayTag().equals(HomeAdapter.TAG) && (position < firstVisibleItem || position > lastVisibleItem)) {
GSYVideoManager.releaseAllVideos();
}
}
}

第三点:屏幕处于静止时才开始播放,只要播放的逻辑写在onScrollStateChanged 的 SCROLL_STATE_IDLE 状态下即可;

@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
switch (newState) {
case SCROLL_STATE_IDLE: //滚动停止
autoPlayVideo(recyclerView);
break;
}
}

private void autoPlayVideo(RecyclerView view) {
RecyclerView.LayoutManager layoutManager = view.getLayoutManager();
for (int i = 0; i < visibleCount; i++) {
if (layoutManager != null && layoutManager.getChildAt(i) != null && layoutManager.getChildAt(i).findViewById(R.id.video_item_player) != null) {
HomeGSYVideoPlayer homeGSYVideoPlayer = (HomeGSYVideoPlayer) layoutManager.getChildAt(i).findViewById(R.id.video_item_player);
Rect rect = new Rect();
homeGSYVideoPlayer.getLocalVisibleRect(rect);
int videoheight = homeGSYVideoPlayer.getHeight();
if (rect.top == 0 && rect.bottom == videoheight) {
if (homeGSYVideoPlayer.getCurrentState() == homeGSYVideoPlayer.CURRENT_STATE_NORMAL || homeGSYVideoPlayer.getCurrentState() == homeGSYVideoPlayer.CURRENT_STATE_ERROR) {
homeGSYVideoPlayer.getStartButton().performClick();
}
return;
}

}
}
GSYVideoPlayer.releaseAllVideos();
}

autoPlayVideo方法里面就是通过 循环遍历 可见区域的播放器 然后通过 getLocalVisibleRect(rect) 方法计算出谁完全显示出来,对应方法可以查看getLocalVisibleRect 写的很详细;其中getChildAt() 获取的是屏幕可见范围下标从0开始的view,例如当前屏幕只够显示三个view,对应的下标就是0、1、2;与滑动的item的position没有关系。 *后判断如果播放器处于可播放的状态即调用start按钮播放。

因为是公司代码,所以很多地方不能开放出来,忘见谅。*后附上主要完整代码:

public int firstVisibleItem, lastVisibleItem, visibleCount;

public void loadListener() {

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

boolean scrollState = false;

@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
switch (newState) {
case SCROLL_STATE_IDLE: //滚动停止
scrollState = false;
// autoPlayVideo(recyclerView);
break;
case SCROLL_STATE_DRAGGING: //手指拖动
scrollState = true;
break;
case SCROLL_STATE_SETTLING: //惯性滚动
scrollState = true;
break;
}
}

@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
firstVisibleItem = layoutManager.findFirstVisibleItemPosition();
lastVisibleItem = layoutManager.findLastVisibleItemPosition();
visibleCount = lastVisibleItem – firstVisibleItem;

//大于0说明有播放
if (GSYVideoManager.instance().getPlayPosition() >= 0) {
//当前播放的位置
int position = GSYVideoManager.instance().getPlayPosition();
//对应的播放列表TAG
if (GSYVideoManager.instance().getPlayTag().equals(HomeAdapter.TAG) && (position < firstVisibleItem || position > lastVisibleItem)) {
GSYVideoManager.onPause();
}
}
}

});
}

private void autoPlayVideo(RecyclerView view) {
RecyclerView.LayoutManager layoutManager = view.getLayoutManager();

for (int i = 0; i < visibleCount; i++) {
if (layoutManager != null && layoutManager.getChildAt(i) != null && layoutManager.getChildAt(i).findViewById(R.id.video_item_player) != null) {
HomeGSYVideoPlayer homeGSYVideoPlayer = (HomeGSYVideoPlayer) layoutManager.getChildAt(i).findViewById(R.id.video_item_player);
Rect rect = new Rect();
homeGSYVideoPlayer.getLocalVisibleRect(rect);
int videoheight = homeGSYVideoPlayer.getHeight();
if (rect.top == 0 && rect.bottom == videoheight) {
if (homeGSYVideoPlayer.getCurrentState() == homeGSYVideoPlayer.CURRENT_STATE_NORMAL || homeGSYVideoPlayer.getCurrentState() == homeGSYVideoPlayer.CURRENT_STATE_ERROR) {
homeGSYVideoPlayer.getStartButton().performClick();
}
return;
}

}
}
GSYVideoPlayer.releaseAllVideos();
}

说到这里还有一点可以说下,就是给视频弄圆角的时候,具体效果可以看上图,这里需要给TextureView设置圆角,一般的通过写shape设置background已经不适用了,好在网上找到一个方法:

public class TextureVideoViewOutlineProvider extends ViewOutlineProvider {
private float mRadius;

public TextureVideoViewOutlineProvider(float radius) {
this.mRadius = radius;
}

@Override
public void getOutline(View view, Outline outline) {
Rect rect = new Rect();
view.getGlobalVisibleRect(rect);
int leftMargin = 0;
int topMargin = 0;
Rect selfRect = new Rect(leftMargin, topMargin,
rect.right – rect.left – leftMargin, rect.bottom – rect.top – topMargin);
outline.setRoundRect(selfRect, mRadius);
}
}

*后给view设置就行:

mVideoView.setOutlineProvider(new TextureVideoViewOutlineProvider(radius));
mVideoView.setClipToOutline(true);