Android的RecyclerView视频自动播放(滑动停止时自动播放)

前言:之前项目里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;
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354