新版的百度贴吧,网易新闻中有看视频的界面。
是随着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;
}
}
}
嘿,写了一点,就开始复制粘贴了。。反正是写给自己看的。。。