zoukankan      html  css  js  c++  java
  • Android在滚动列表中实现视频的播放(RecyclerView)

    新版的百度贴吧,网易新闻中有看视频的界面。

    是随着view的滚动自动加载的。

    如图所示,很方便查看。

    因为项目需要,我在开发一个APP,也需要查看视频,便想实现一个差不多功能的。

    经过搜索,我发现GITHUB上有这个开源的东西,可以很方便的实现这样的效果

    VideoPlayerManager

    试着做了个Demo,在此记录下,以后自己查起来也方便。

    要使用这个很方便,只需要在android studio的build.gradle文件里加入以下内容就行了。

    dependencies {
        compile 'com.github.danylovolokh:video-player-manager:0.2.0'
        compile 'com.github.danylovolokh:list-visibility-utils:0.2.0'
    }
    

      

    先从布局开始吧!

    首先,需要一个recycleview。

    所以定义一个布局:

      video_watch_layout.xml

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3     android:orientation="vertical" android:layout_width="match_parent"
     4     android:layout_height="match_parent">
     5 
     6     <android.support.v7.widget.RecyclerView
     7         android:id="@+id/video_watch_list"
     8         android:layout_width="match_parent"
     9         android:layout_height="match_parent">
    10 
    11     </android.support.v7.widget.RecyclerView>
    12 
    13 
    14 
    15 </LinearLayout>

    然后再定义它的item

    video_watch_list_item.xml

    
    
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="320dp"
    android:layout_marginBottom="4dp"
    android:layout_marginTop="4dp">

    <RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <!--播放器-->
    <com.volokh.danylo.video_player_manager.ui.VideoPlayerView
    android:id="@+id/item_video_vpv_player"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_above="@+id/item_video_tv_title"/>

    <!--背景-->
    <com.facebook.drawee.view.SimpleDraweeView
    android:id="@+id/item_video_iv_cover"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_above="@+id/item_video_tv_title"
    />

    <!--标题-->
    <TextView
    android:id="@+id/item_video_tv_title"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:padding="10dp"
    android:layout_alignParentBottom="true"/>
    </RelativeLayout>

    </LinearLayout>
     
    其中SimpleDraweeView这个控件,可以改成Imageview,用来显示视频封面图的。



    下面是展示视频的Activity的代码。
    package com.duanqu.Idea.activity;
    
    import android.os.Bundle;
    import android.support.annotation.Nullable;
    import android.support.v7.app.AppCompatActivity;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.view.View;
    
    import com.duanqu.Idea.Adapter.OnlineVideoListItem;
    import com.duanqu.Idea.Adapter.VideoListItem;
    import com.duanqu.Idea.Adapter.VideoWatchAdapter;
    import com.duanqu.Idea.R;
    import com.volokh.danylo.video_player_manager.manager.PlayerItemChangeListener;
    import com.volokh.danylo.video_player_manager.manager.SingleVideoPlayerManager;
    import com.volokh.danylo.video_player_manager.manager.VideoItem;
    import com.volokh.danylo.video_player_manager.manager.VideoPlayerManager;
    import com.volokh.danylo.video_player_manager.meta.MetaData;
    import com.volokh.danylo.visibility_utils.calculator.DefaultSingleItemCalculatorCallback;
    import com.volokh.danylo.visibility_utils.calculator.ListItemsVisibilityCalculator;
    import com.volokh.danylo.visibility_utils.calculator.SingleListViewItemActiveCalculator;
    import com.volokh.danylo.visibility_utils.items.ListItem;
    import com.volokh.danylo.visibility_utils.scroll_utils.RecyclerViewItemPositionGetter;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Created by Administrator on 2016/7/28.
     */
    public class VideoWatchActivity  extends AppCompatActivity implements View.OnClickListener{
        private RecyclerView mRecyclerView;
    
        //视频数据,相当于普通adapter里的datas
        private List<VideoListItem> mLists = new ArrayList<>();
    
        //它充当ListItemsVisibilityCalculator和列表(ListView, RecyclerView)之间的适配器(Adapter)。
        private RecyclerViewItemPositionGetter mItemsPositionGetter;
    
        //ListItemsVisibilityCalculator可以追踪滑动的方向并在过程中计算每个Item的可见度
        //SingleListViewItemActiveCalculator会在滑动时获取每个View的可见度百分比.
        //所以其构造方法里需要传入mLists,而mLists里的每个item实现了ListItem接口
        //的getVisibilityPercents方法,也就是返回当前item可见度的方法.
        //这样ListItemsVisibilityCalculator就可以计算当前item的可见度了.
    
        private final ListItemsVisibilityCalculator mVideoVisibilityCalculator =
                new SingleListViewItemActiveCalculator(new DefaultSingleItemCalculatorCallback(), mLists);
    
    
        //SingleVideoPlayerManager就是只能同时播放一个视频。
        //当一个view开始播放时,之前那个就会停止
        private final VideoPlayerManager<MetaData> mVideoPlayerManager = new SingleVideoPlayerManager(new PlayerItemChangeListener() {
            @Override
            public void onPlayerItemChanged(MetaData metaData) {
            }
        });
    
        private int mScrollState;
        private LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
        private static final String URL =
                "http://dn-chunyu.qbox.me/fwb/static/images/home/video/video_aboutCY_A.mp4";
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.video_watch_layout);
            mRecyclerView = (RecyclerView) findViewById(R.id.video_watch_list);
    
    
            //添加视频数据
            for (int i = 0; i < 10; ++i) {
                mLists.add(new OnlineVideoListItem(mVideoPlayerManager, "测试", "http://115.159.159.65:8080/EAsy/cover.jpg", URL));
            }
    
    
            mRecyclerView.setLayoutManager(mLayoutManager);
            VideoWatchAdapter adapter = new VideoWatchAdapter(mLists);
            mRecyclerView.setAdapter(adapter);
            //////////////////////////////////////////////
    
            //这里是文档上默认的写法,直接复制下来。
            //查看了下源码其中VisibilityCalculator.onScrollStateIdle的这
            //个方法又调用了方法calculateMostVisibleItem,用来计算滑动状态改变时
            //的最大可见度的item.这个方法的计算方法是这样的:当view无论是向上还是
            //向下滚动时,在滚动的过程中,计算可见度最大的item。当滚动状态为空闲时
            //此时最后一个计算得出的可见度最大的item就是当前可见度最大的item
            //而onScroll方法是处理item滚出屏幕后的计算,用于发现新的活动item
            mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    
                @Override
                public void onScrollStateChanged(RecyclerView recyclerView, int scrollState) {
                    mScrollState = scrollState;
                    if(scrollState == RecyclerView.SCROLL_STATE_IDLE && !mLists.isEmpty()){
    
                        mVideoVisibilityCalculator.onScrollStateIdle(
                                mItemsPositionGetter,
                                mLayoutManager.findFirstVisibleItemPosition(),
                                mLayoutManager.findLastVisibleItemPosition());
                    }
                }
    
                @Override
                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                    if(!mLists.isEmpty()){
                        mVideoVisibilityCalculator.onScroll(
                                mItemsPositionGetter,
                                mLayoutManager.findFirstVisibleItemPosition(),
                                mLayoutManager.findLastVisibleItemPosition() - mLayoutManager.findFirstVisibleItemPosition() + 1,
                                mScrollState);
                    }
                }
            });
    
            mItemsPositionGetter = new RecyclerViewItemPositionGetter(mLayoutManager, mRecyclerView);
    
            /////////////////////////////////////////////
    
    
    
    
        }
    
        //文档上的默认实现,复制下来
        //onResume()中调用方法,使屏幕亮起时启动对View的可见度的计算。
        @Override
        public void onResume() {
            super.onResume();
            if(!mLists.isEmpty()){
                // need to call this method from list view handler in order to have filled list
    
                mRecyclerView.post(new Runnable() {
                    @Override
                    public void run() {
    
                        mVideoVisibilityCalculator.onScrollStateIdle(
                                mItemsPositionGetter,
                                mLayoutManager.findFirstVisibleItemPosition(),
                                mLayoutManager.findLastVisibleItemPosition());
    
                    }
                });
            }
        }
    
        @Override
        public void onClick(View v) {
    
        }
    
    
        @Override
        public void onStop() {
            super.onStop();
            mVideoPlayerManager.resetMediaPlayer(); // 页面不显示时, 释放播放器
        }
    }

    其次是

    ListItem的实现,其实现的是当前item的可见度计算。
    当然也保存了视频数据的一些信息,供Adapter使用
    package com.duanqu.Idea.Adapter;
    
    import android.graphics.Rect;
    import android.support.annotation.DrawableRes;
    import android.view.View;
    
    import com.volokh.danylo.video_player_manager.manager.VideoItem;
    import com.volokh.danylo.video_player_manager.manager.VideoPlayerManager;
    import com.volokh.danylo.video_player_manager.meta.CurrentItemMetaData;
    import com.volokh.danylo.video_player_manager.meta.MetaData;
    import com.volokh.danylo.visibility_utils.items.ListItem;
    
    
    /**
     * 基本视频项, 实现适配项和列表项
     * <p/>
     * Created by wangchenlong on 16/1/27.
     */
    public abstract class VideoListItem implements VideoItem, ListItem {
    
        private final Rect mCurrentViewRect; // 当前视图的方框
        private final VideoPlayerManager<MetaData> mVideoPlayerManager; // 视频播放管理器
        private final String mTitle; // 标题
        private final String CoverImageUrl; // 图片资源
    
        // 构造器, 输入视频播放管理器
        public VideoListItem(
                VideoPlayerManager<MetaData> videoPlayerManager,
                String title,
                String imageResource) {
            mVideoPlayerManager = videoPlayerManager;
            mTitle = title;
            CoverImageUrl = imageResource;
    
            mCurrentViewRect = new Rect();
        }
    
        // 视频项的标题
        public String getTitle() {
            return mTitle;
        }
    
        public String getCoverImageUrl() {
            return CoverImageUrl;
        }
    
        // 显示可视的百分比程度
        @Override
        public int getVisibilityPercents(View view) {
            int percents = 100;
    
            view.getLocalVisibleRect(mCurrentViewRect);
            int height = view.getHeight();
    
            if (viewIsPartiallyHiddenTop()) {
                percents = (height - mCurrentViewRect.top) * 100 / height;
            } else if (viewIsPartiallyHiddenBottom(height)) {
                percents = mCurrentViewRect.bottom * 100 / height;
            }
    
    
    
            return percents;
        }
    
        @Override
        public void setActive(View newActiveView, int newActiveViewPosition) {
            VideoWatchAdapter.VideoViewHolder viewHolder =
                    (VideoWatchAdapter.VideoViewHolder) newActiveView.getTag();
            playNewVideo(new CurrentItemMetaData(newActiveViewPosition, newActiveView),
                    viewHolder.getVpvPlayer(), mVideoPlayerManager);
        }
    
        @Override
        public void deactivate(View currentView, int position) {
            stopPlayback(mVideoPlayerManager);
        }
    
        @Override
        public void stopPlayback(VideoPlayerManager videoPlayerManager) {
            videoPlayerManager.stopAnyPlayback();
        }
    
        // 顶部出现
        private boolean viewIsPartiallyHiddenTop() {
            return mCurrentViewRect.top > 0;
        }
    
        // 底部出现
        private boolean viewIsPartiallyHiddenBottom(int height) {
            return mCurrentViewRect.bottom > 0 && mCurrentViewRect.bottom < height;
        }
    }

    对这个类进行继承

    package com.duanqu.Idea.Adapter;
    
    import android.support.annotation.DrawableRes;
    
    import com.volokh.danylo.video_player_manager.manager.VideoPlayerManager;
    import com.volokh.danylo.video_player_manager.meta.MetaData;
    import com.volokh.danylo.video_player_manager.ui.VideoPlayerView;
    
    /**
     * Created by Administrator on 2016/7/28.
     */
    public class OnlineVideoListItem extends VideoListItem {
    
        private final String mOnlineUrl; // 资源文件描述
    
        public OnlineVideoListItem(
                VideoPlayerManager<MetaData> videoPlayerManager,
                String title,
                String imageResource,
                String onlineUrl
        ) {
            super(videoPlayerManager, title, imageResource);
    
            mOnlineUrl = onlineUrl;
        }
    
        @Override
        public void playNewVideo(MetaData currentItemMetaData, VideoPlayerView player, VideoPlayerManager<MetaData> videoPlayerManager) {
            videoPlayerManager.playNewVideo(currentItemMetaData, player, mOnlineUrl);
        }
    }

    复写一个playNewVideo方法。

    最后是recycleview的Adapter方法:

    package com.duanqu.Idea.Adapter;
    
    
    import android.content.Context;
    import android.net.Uri;
    import android.support.v7.widget.RecyclerView;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ImageView;
    import android.widget.TextView;
    
    import com.duanqu.Idea.R;
    import com.volokh.danylo.video_player_manager.ui.MediaPlayerWrapper;
    import com.volokh.danylo.video_player_manager.ui.VideoPlayerView;
    
    import java.util.List;
    
    
    /**
     * Created by Administrator on 2016/7/28.
     */
    public class VideoWatchAdapter extends RecyclerView.Adapter<VideoWatchAdapter.VideoViewHolder>{
    
        private final List<VideoListItem> mList; // 视频项列表
    
        // 构造器
        public VideoWatchAdapter(List<VideoListItem> list) {
            mList = list;
        }
    
        @Override
        public VideoWatchAdapter.VideoViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.video_watch_list_item, parent, false);
            // 必须要设置Tag, 否则无法显示
            VideoWatchAdapter.VideoViewHolder holder = new VideoWatchAdapter.VideoViewHolder(view);
            view.setTag(holder);
            return new VideoWatchAdapter.VideoViewHolder(view);
        }
    
        @Override
        public void onBindViewHolder(final VideoWatchAdapter.VideoViewHolder holder, int position) {
            VideoListItem videoItem = mList.get(position);
            holder.bindTo(videoItem);
        }
    
        @Override
        public int getItemCount() {
            return mList.size();
        }
    
        public static class VideoViewHolder extends RecyclerView.ViewHolder {
            VideoPlayerView mVpvPlayer; // 播放控件
            ImageView mIvCover; // 覆盖层
            TextView mTvTitle; // 标题
    
            private Context mContext;
            private MediaPlayerWrapper.MainThreadMediaPlayerListener mPlayerListener;
    
            public VideoViewHolder(View itemView) {
                super(itemView);
                mVpvPlayer = (VideoPlayerView) itemView.findViewById(R.id.item_video_vpv_player); // 播放控件
                mTvTitle = (TextView) itemView.findViewById(R.id.item_video_tv_title);
                mIvCover = (ImageView) itemView.findViewById(R.id.item_video_iv_cover);
    
                mContext = itemView.getContext().getApplicationContext();
                mPlayerListener = new MediaPlayerWrapper.MainThreadMediaPlayerListener() {
                    @Override
                    public void onVideoSizeChangedMainThread(int width, int height) {
                    }
    
                    @Override
                    public void onVideoPreparedMainThread() {
                        // 视频播放隐藏前图
                        mIvCover.setVisibility(View.INVISIBLE);
                    }
    
                    @Override
                    public void onVideoCompletionMainThread() {
                    }
    
                    @Override
                    public void onErrorMainThread(int what, int extra) {
                    }
    
                    @Override
                    public void onBufferingUpdateMainThread(int percent) {
                    }
    
                    @Override
                    public void onVideoStoppedMainThread() {
                        // 视频暂停显示前图
                        mIvCover.setVisibility(View.VISIBLE);
                    }
                };
    
                mVpvPlayer.addMediaPlayerListener(mPlayerListener);
            }
    
            public void bindTo(VideoListItem vli) {
                mTvTitle.setText(vli.getTitle());
                mIvCover.setImageURI(Uri.parse(vli.getCoverImageUrl()));
            }
    
            // 返回播放器
            public VideoPlayerView getVpvPlayer() {
                return mVpvPlayer;
            }
    
        }
    }

    嘿,写了一点,就开始复制粘贴了。。反正是写给自己看的。。。

  • 相关阅读:
    构建之法
    第一阶段SCRUM冲刺
    NABCD项目分析
    结对开发
    梦断代码阅读-04
    梦断代码阅读-05
    移动端疫情展示
    用Python爬取疫情数据
    全球疫情可视化地图
    第二阶段冲刺第三天个人记录
  • 原文地址:https://www.cnblogs.com/You0/p/5716266.html
Copyright © 2011-2022 走看看