前言:之前项目里feed里需要自动播放视频,老代码用的是第三方,但是遇到的视频不能正确自动播放的情况,于是自己动手写了一个。
思路:在recyclerview的OnScrollListener中进行滑动监听,当滑动停止时,去判断视频的view可见度为多少,进而判断是否需要进行自动播放。
import android.view.View;
//辅助接口
public interface AutoPlayItem {
void setActive();
void deactivate();
View getAutoplayView();
}
主要的工具类,有两种播放模式,MODE_PLAY_FIRST播放第一个可见的视频,MODE_PLAY_CENTER播放靠中间的可见的一个视频.
需要用到globalVisibleRect或者getLocalVisibleRect,通过源码可以看到getLocalVisibleRect调用了globalVisibleRect。可参考:https://www.jianshu.com/p/2aa908f6a2e6
需要知道的是globalVisibleRect获取到的可见区域的坐标是屏幕中真实的坐标,getLocalVisibleRect获取到的是0开始的坐标。
//源码
public final boolean getGlobalVisibleRect(Rect r) {
return getGlobalVisibleRect(r, null);
}
public final boolean getLocalVisibleRect(Rect r) {
final Point offset = mAttachInfo != null ? mAttachInfo.mPoint : new Point();
if (getGlobalVisibleRect(r, offset)) {
r.offset(-offset.x, -offset.y); // make r local
return true;
}
return false;
}
由于需要计算view的可见百分比,所以还用到的了getMeasuredHeight(),通过源码可知这个方法获取到的是view向父布局申请的高度,实际的高度可能会因为屏幕限制而没有那么高。
另外,getHight()方法获取到的是父布局通过layout方法实际给view的高度。
可参考:https://www.jianshu.com/p/27e765cf24c2
//源码
public final int getMeasuredHeight() {
return mMeasuredHeight & MEASURED_SIZE_MASK;
}
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
因为需要播放屏幕中间的那个视频,所以还要知道屏幕的高度,我用的是
getResources().getDisplayMetrics().heightPixels;
/**
* 自动播放的工具
*/
public class AutoPlayTool {
private AutoPlayItem mHolder;
private int visiblePercent=60;
public static int MODE_PLAY_FIRST=0;
public static int MODE_PLAY_CENTER=1;
private int mode=MODE_PLAY_FIRST;
public AutoPlayTool() {
}
public AutoPlayTool(int visiblePercent) {
this.visiblePercent = visiblePercent;
}
public AutoPlayTool(int visiblePercent, int mode) {
this.visiblePercent = visiblePercent;
this.mode = mode;
}
public void setMode(int mode) {
this.mode = mode;
}
/**
* 当滑动停止的时候,开始视频播放
* @param recyclerView
* @return
*/
public int onActiveWhenNoScrolling(RecyclerView recyclerView){
LinearLayoutManager layoutManager=null;
if(recyclerView.getLayoutManager() instanceof LinearLayoutManager) {
layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
}
if(layoutManager!=null) {
int firstItemPosition = layoutManager.findFirstVisibleItemPosition();
int lastItemPosition=layoutManager.findLastVisibleItemPosition();
LinkedHashMap<Integer ,AutoPlayItem> items=new LinkedHashMap();
while (firstItemPosition<=lastItemPosition){
RecyclerView.ViewHolder holder=recyclerView.findViewHolderForLayoutPosition(firstItemPosition);
if(holder instanceof AutoPlayItem){
View view=(((AutoPlayItem) holder)).getAutoplayView();
if(view!=null&&getVisible(view,visiblePercent)){
if(mode==MODE_PLAY_FIRST){//优先播放第一个的情况
((AutoPlayItem) holder).setActive();
mHolder= ((AutoPlayItem) holder);
return firstItemPosition;
}
items.put(firstItemPosition,((AutoPlayItem) holder));
}
}
firstItemPosition++;
}
//下面的逻辑是播放靠中间的视频
int d=Integer.MAX_VALUE;
AutoPlayItem findHolder=null;
int position=-1;
//找出距离中间最近的一个
for(Map.Entry<Integer,AutoPlayItem> entry : items.entrySet()) {
int d2=getDistanceFromCenter(entry.getValue().getAutoplayView());
if(d2<d){
findHolder = entry.getValue();
d=d2;
position=entry.getKey();
}
}
if(mHolder!=findHolder) {
if (mHolder != null) {
mHolder.deactivate();
}
mHolder=findHolder;
}
if (mHolder != null) {
mHolder.setActive();
return position;
}
}
return -1;
}
//当视频画出屏幕时停止播放
public void onScrolledAndDeactivate(RecyclerView recyclerView){
if(mHolder!=null&&mHolder.getAutoplayView()!=null&&!getVisible(mHolder.getAutoplayView(),visiblePercent)){
mHolder.deactivate();
}
}
/**
* 用于停止滑出去的视频
*/
public void onScrolledAndDeactivate(){
if(mHolder!=null&&mHolder.getAutoplayView()!=null&&!getVisible(mHolder.getAutoplayView(),visiblePercent)){
mHolder.deactivate();
}
}
public void setVisiblePercent(int visiblePercent) {
this.visiblePercent = visiblePercent;
}
private int getVisiblePercent(View v) {
Rect r =new Rect();
boolean visible = v.getLocalVisibleRect(r);
if (visible&& v.getMeasuredHeight()>0) {
int percent = 100 * r.height() / v.getMeasuredHeight();
return percent;
}
return -1;
}
private boolean getVisible(View v, int value) {
Rect r = new Rect();
boolean visible = v.getLocalVisibleRect(r);
if (visible&&v.getVisibility()==View.VISIBLE) {
if (getVisiblePercent(v) >= value) {
return true;
} else {
return false;
}
}
return false;
}
private int getDistanceFromCenter(View view){
int centerHeight=(int) (DensityUtil.getScreenHeight()/2.3);//中间线靠上一点,
//项目代码原因,可以写getResources().getDisplayMetrics().heightPixels;
int[] viewLocation = new int[2];
view.getLocationOnScreen(viewLocation);
return Math.abs(viewLocation[1]+view.getHeight()/2-centerHeight);
}
}
在recyclerview中使用
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
AutoPlayTool autoPlayTool=new AutoPlayTool(60,1);
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if(newState==RecyclerView.SCROLL_STATE_IDLE){
autoPlayTool.onActiveWhenNoScrolling(recyclerView);
}
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
autoPlayTool.onScrolledAndDeactivate();
}
});
最后,ViewHold需要继承AutoPlayItem接口。
在setActive() 方法中开始自动播放,在deactivate()停止播放,getAutoplayView()返回你的videoview。
public class ViewHoldVideo extends RecyclerView.ViewHolder implements AutoPlayItem{
public ViewHoldVideo(@NonNull View itemView) {
super(itemView);
}
@Override
public void setActive() {
}
@Override
public void deactivate() {
}
@Override
public View getAutoplayView() {
return null;
}
}
网友评论