一、效果图
二、准备
项目中视频使用的是 GSYVideoPlayer 这个开源库。
既然要实现类似微博那种滚动列表时,处于当前屏幕的项自动播放,那么肯定得监听列表的滚动事件,好在 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();
}
三、实战
1. 效果图
2. 添加依赖:
implementation 'com.github.CarGuo.GSYVideoPlayer:GSYVideoPlayer:v8.2.0-release-jitpack'
3. 布局控件
item_home.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.hkt.nfcdemo.views.SampleCoverVideo
android:id="@+id/video_item_player"
android:layout_width="match_parent"
android:layout_height="200dp"/>
</FrameLayout>
list_video_item.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<FrameLayout
android:id="@+id/list_item_container"
android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="centerCrop"
android:src="@mipmap/a" />
<ImageView
android:id="@+id/list_item_btn"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_gravity="center_vertical"
android:src="@drawable/video_click_play_selector" />
</RelativeLayout>
video_layout_cover.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black">
<FrameLayout
android:id="@+id/surface_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
</FrameLayout>
<RelativeLayout
android:id="@+id/thumb"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:background="#000000"
android:scaleType="fitCenter">
<ImageView
android:id="@+id/thumb_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop" />
</RelativeLayout>
<LinearLayout
android:id="@+id/layout_bottom"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
android:background="#99000000"
android:gravity="center_vertical"
android:orientation="horizontal"
android:visibility="invisible">
<TextView
android:id="@+id/current"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:text="00:00"
android:textColor="#ffffff" />
<SeekBar
android:id="@+id/progress"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1.0"
android:background="@null"
android:max="100"
android:maxHeight="4dp"
android:minHeight="4dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:progressDrawable="@drawable/video_seek_progress"
android:thumb="@drawable/video_seek_thumb" />
<TextView
android:id="@+id/total"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="16dp"
android:text="00:00"
android:textColor="#ffffff" />
<ImageView
android:id="@+id/fullscreen"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:paddingRight="16dp"
android:scaleType="center"
android:src="@drawable/video_enlarge" />
</LinearLayout>
<ProgressBar
android:id="@+id/bottom_progressbar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="1.5dp"
android:layout_alignParentBottom="true"
android:max="100"
android:progressDrawable="@drawable/video_progress" />
<ImageView
android:id="@+id/back_tiny"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginLeft="6dp"
android:layout_marginTop="6dp"
android:visibility="gone" />
<LinearLayout
android:id="@+id/layout_top"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="@drawable/video_title_bg"
android:gravity="center_vertical">
<ImageView
android:id="@+id/back"
android:layout_width="48dp"
android:layout_height="48dp"
android:paddingLeft="10dp"
android:scaleType="centerInside"
android:src="@drawable/video_back" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:textColor="@android:color/white"
android:textSize="18sp" />
</LinearLayout>
<moe.codeest.enviews.ENDownloadView
android:id="@+id/loading"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:visibility="invisible" />
<moe.codeest.enviews.ENPlayView
android:id="@+id/start"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_gravity="center_vertical" />
<ImageView
android:id="@+id/small_close"
android:layout_width="30dp"
android:layout_height="30dp"
android:paddingLeft="10dp"
android:paddingTop="10dp"
android:scaleType="centerInside"
android:src="@drawable/video_small_close"
android:visibility="gone" />
<ImageView
android:id="@+id/lock_screen"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="50dp"
android:scaleType="centerInside"
android:src="@drawable/unlock"
android:visibility="gone" />
</RelativeLayout>
4. 自定义控件 SampleCoverVideo.java
/**
* Created on 2022/6/1 14:07
*
* @author Gong Youqiang
*/
public class SampleCoverVideo extends StandardGSYVideoPlayer {
ImageView mCoverImage;
String mCoverOriginUrl;
int mCoverOriginId = 0;
int mDefaultRes;
public SampleCoverVideo(Context context, Boolean fullFlag) {
super(context, fullFlag);
}
public SampleCoverVideo(Context context) {
super(context);
}
public SampleCoverVideo(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void init(Context context) {
super.init(context);
mCoverImage = (ImageView) findViewById(R.id.thumb_image);
if (mThumbImageViewLayout != null &&
(mCurrentState == -1 || mCurrentState == CURRENT_STATE_NORMAL || mCurrentState == CURRENT_STATE_ERROR)) {
mThumbImageViewLayout.setVisibility(VISIBLE);
}
}
@Override
public int getLayoutId() {
return R.layout.video_layout_cover;
}
public void loadCoverImage(String url, int res) {
mCoverOriginUrl = url;
mDefaultRes = res;
Glide.with(getContext().getApplicationContext())
.setDefaultRequestOptions(
new RequestOptions()
.frame(1000000)
.centerCrop()
.error(res)
.placeholder(res))
.load(url)
.into(mCoverImage);
}
public void loadCoverImageBy(int id, int res) {
mCoverOriginId = id;
mDefaultRes = res;
mCoverImage.setImageResource(id);
}
@Override
public GSYBaseVideoPlayer startWindowFullscreen(Context context, boolean actionBar, boolean statusBar) {
GSYBaseVideoPlayer gsyBaseVideoPlayer = super.startWindowFullscreen(context, actionBar, statusBar);
SampleCoverVideo sampleCoverVideo = (SampleCoverVideo) gsyBaseVideoPlayer;
if(mCoverOriginUrl != null) {
sampleCoverVideo.loadCoverImage(mCoverOriginUrl, mDefaultRes);
} else if(mCoverOriginId != 0) {
sampleCoverVideo.loadCoverImageBy(mCoverOriginId, mDefaultRes);
}
return gsyBaseVideoPlayer;
}
@Override
public GSYBaseVideoPlayer showSmallVideo(Point size, boolean actionBar, boolean statusBar) {
//下面这里替换成你自己的强制转化
SampleCoverVideo sampleCoverVideo = (SampleCoverVideo) super.showSmallVideo(size, actionBar, statusBar);
sampleCoverVideo.mStartButton.setVisibility(GONE);
sampleCoverVideo.mStartButton = null;
return sampleCoverVideo;
}
@Override
protected void cloneParams(GSYBaseVideoPlayer from, GSYBaseVideoPlayer to) {
super.cloneParams(from, to);
SampleCoverVideo sf = (SampleCoverVideo) from;
SampleCoverVideo st = (SampleCoverVideo) to;
st.mShowFullAnimation = sf.mShowFullAnimation;
}
/**
* 退出window层播放全屏效果
*/
@SuppressWarnings("ResourceType")
@Override
protected void clearFullscreenLayout() {
if (!mFullAnimEnd) {
return;
}
mIfCurrentIsFullscreen = false;
int delay = 0;
// ------- !!!如果不需要旋转屏幕,可以不调用!!!-------
// 不需要屏幕旋转,还需要设置 setNeedOrientationUtils(false)
if (mOrientationUtils != null) {
delay = mOrientationUtils.backToProtVideo();
mOrientationUtils.setEnable(false);
if (mOrientationUtils != null) {
mOrientationUtils.releaseListener();
mOrientationUtils = null;
}
}
if (!mShowFullAnimation) {
delay = 0;
}
final ViewGroup vp = (CommonUtil.scanForActivity(getContext())).findViewById(Window.ID_ANDROID_CONTENT);
final View oldF = vp.findViewById(getFullId());
if (oldF != null) {
//此处fix bug#265,推出全屏的时候,虚拟按键问题
SampleCoverVideo gsyVideoPlayer = (SampleCoverVideo) oldF;
gsyVideoPlayer.mIfCurrentIsFullscreen = false;
}
if (delay == 0) {
backToNormal();
} else {
postDelayed(new Runnable() {
@Override
public void run() {
backToNormal();
}
}, delay);
}
}
/******************* 下方两个重载方法,在播放开始前不屏蔽封面,不需要可屏蔽 ********************/
@Override
public void onSurfaceUpdated(Surface surface) {
super.onSurfaceUpdated(surface);
if (mThumbImageViewLayout != null && mThumbImageViewLayout.getVisibility() == VISIBLE) {
mThumbImageViewLayout.setVisibility(INVISIBLE);
}
}
@Override
protected void setViewShowState(View view, int visibility) {
if (view == mThumbImageViewLayout && visibility != VISIBLE) {
return;
}
super.setViewShowState(view, visibility);
}
@Override
public void onSurfaceAvailable(Surface surface) {
super.onSurfaceAvailable(surface);
if (GSYVideoType.getRenderType() != GSYVideoType.TEXTURE) {
if (mThumbImageViewLayout != null && mThumbImageViewLayout.getVisibility() == VISIBLE) {
mThumbImageViewLayout.setVisibility(INVISIBLE);
}
}
}
/******************* 下方重载方法,在播放开始不显示底部进度和按键,不需要可屏蔽 ********************/
protected boolean byStartedClick;
@Override
protected void onClickUiToggle(MotionEvent e) {
if (mIfCurrentIsFullscreen && mLockCurScreen && mNeedLockFull) {
setViewShowState(mLockScreen, VISIBLE);
return;
}
byStartedClick = true;
super.onClickUiToggle(e);
}
@Override
protected void changeUiToNormal() {
super.changeUiToNormal();
byStartedClick = false;
}
@Override
protected void changeUiToPreparingShow() {
super.changeUiToPreparingShow();
Debuger.printfLog("Sample changeUiToPreparingShow");
setViewShowState(mBottomContainer, INVISIBLE);
setViewShowState(mStartButton, INVISIBLE);
}
@Override
protected void changeUiToPlayingBufferingShow() {
super.changeUiToPlayingBufferingShow();
Debuger.printfLog("Sample changeUiToPlayingBufferingShow");
if (!byStartedClick) {
setViewShowState(mBottomContainer, INVISIBLE);
setViewShowState(mStartButton, INVISIBLE);
}
}
@Override
protected void changeUiToPlayingShow() {
super.changeUiToPlayingShow();
Debuger.printfLog("Sample changeUiToPlayingShow");
if (!byStartedClick) {
setViewShowState(mBottomContainer, INVISIBLE);
setViewShowState(mStartButton, INVISIBLE);
}
}
@Override
public void startAfterPrepared() {
super.startAfterPrepared();
Debuger.printfLog("Sample startAfterPrepared");
setViewShowState(mBottomContainer, INVISIBLE);
setViewShowState(mStartButton, INVISIBLE);
setViewShowState(mBottomProgressBar, VISIBLE);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
byStartedClick = true;
super.onStartTrackingTouch(seekBar);
}
}
5. 工具类 ScrollCalculatorHelper.java
/**
* Created on 2022/6/1 14:32
*
* @author Gong Youqiang
*/
public class ScrollCalculatorHelper {
private int firstVisible = 0;
private int lastVisible = 0;
private int visibleCount = 0;
private int playId;
private int rangeTop;
private int rangeBottom;
private PlayRunnable runnable;
private Handler playHandler = new Handler();
public ScrollCalculatorHelper(int playId, int rangeTop, int rangeBottom) {
this.playId = playId;
this.rangeTop = rangeTop;
this.rangeBottom = rangeBottom;
}
public void onScrollStateChanged(RecyclerView view, int scrollState) {
switch (scrollState) {
case RecyclerView.SCROLL_STATE_IDLE:
playVideo(view);
break;
}
}
public void onScroll(RecyclerView view, int firstVisibleItem, int lastVisibleItem, int visibleItemCount) {
if (firstVisible == firstVisibleItem) {
return;
}
firstVisible = firstVisibleItem;
lastVisible = lastVisibleItem;
visibleCount = visibleItemCount;
}
void playVideo(RecyclerView view) {
if (view == null) {
return;
}
RecyclerView.LayoutManager layoutManager = view.getLayoutManager();
GSYBaseVideoPlayer gsyBaseVideoPlayer = null;
boolean needPlay = false;
for (int i = 0; i < visibleCount; i++) {
if (layoutManager.getChildAt(i) != null && layoutManager.getChildAt(i).findViewById(playId) != null) {
GSYBaseVideoPlayer player = (GSYBaseVideoPlayer) layoutManager.getChildAt(i).findViewById(playId);
Rect rect = new Rect();
player.getLocalVisibleRect(rect);
int height = player.getHeight();
//说明第一个完全可视
if (rect.top == 0 && rect.bottom == height) {
gsyBaseVideoPlayer = player;
if ((player.getCurrentPlayer().getCurrentState() == GSYBaseVideoPlayer.CURRENT_STATE_NORMAL
|| player.getCurrentPlayer().getCurrentState() == GSYBaseVideoPlayer.CURRENT_STATE_ERROR)) {
needPlay = true;
}
break;
}
}
}
if (gsyBaseVideoPlayer != null && needPlay) {
if (runnable != null) {
GSYBaseVideoPlayer tmpPlayer = runnable.gsyBaseVideoPlayer;
playHandler.removeCallbacks(runnable);
runnable = null;
if (tmpPlayer == gsyBaseVideoPlayer) {
return;
}
}
runnable = new PlayRunnable(gsyBaseVideoPlayer);
//降低频率
playHandler.postDelayed(runnable, 400);
}
}
private class PlayRunnable implements Runnable {
GSYBaseVideoPlayer gsyBaseVideoPlayer;
public PlayRunnable(GSYBaseVideoPlayer gsyBaseVideoPlayer) {
this.gsyBaseVideoPlayer = gsyBaseVideoPlayer;
}
@Override
public void run() {
boolean inPosition = false;
//如果未播放,需要播放
if (gsyBaseVideoPlayer != null) {
int[] screenPosition = new int[2];
gsyBaseVideoPlayer.getLocationOnScreen(screenPosition);
int halfHeight = gsyBaseVideoPlayer.getHeight() / 2;
int rangePosition = screenPosition[1] + halfHeight;
//中心点在播放区域内
if (rangePosition >= rangeTop && rangePosition <= rangeBottom) {
inPosition = true;
}
if (inPosition) {
startPlayLogic(gsyBaseVideoPlayer, gsyBaseVideoPlayer.getContext());
//gsyBaseVideoPlayer.startPlayLogic();
}
}
}
}
/***************************************自动播放的点击播放确认******************************************/
private void startPlayLogic(GSYBaseVideoPlayer gsyBaseVideoPlayer, Context context) {
if (!com.shuyu.gsyvideoplayer.utils.CommonUtil.isWifiConnected(context)) {
//这里判断是否wifi
showWifiDialog(gsyBaseVideoPlayer, context);
return;
}
gsyBaseVideoPlayer.startPlayLogic();
}
private void showWifiDialog(final GSYBaseVideoPlayer gsyBaseVideoPlayer, Context context) {
if (!NetworkUtils.isAvailable(context)) {
Toast.makeText(context, context.getResources().getString(com.shuyu.gsyvideoplayer.R.string.no_net), Toast.LENGTH_LONG).show();
return;
}
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage(context.getResources().getString(com.shuyu.gsyvideoplayer.R.string.tips_not_wifi));
builder.setPositiveButton(context.getResources().getString(com.shuyu.gsyvideoplayer.R.string.tips_not_wifi_confirm), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
gsyBaseVideoPlayer.startPlayLogic();
}
});
builder.setNegativeButton(context.getResources().getString(com.shuyu.gsyvideoplayer.R.string.tips_not_wifi_cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
builder.create().show();
}
}
6. 适配器
6.1 RecyclerBaseAdapter.java
/**
* Created on 2022/6/1 14:17
*
* @author Gong Youqiang
*/
public class RecyclerBaseAdapter extends RecyclerView.Adapter {
private final static String TAG = "RecyclerBaseAdapter";
private List<VideoModel> itemDataList = null;
private Context context = null;
private GSYVideoHelper smallVideoHelper;
private GSYVideoHelper.GSYVideoHelperBuilder gsySmallVideoHelperBuilder;
public RecyclerBaseAdapter(Context context, List<VideoModel> itemDataList) {
this.itemDataList = itemDataList;
this.context = context;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent,
int viewType) {
View v = LayoutInflater.from(context).inflate(R.layout.list_video_item, parent, false);
final RecyclerView.ViewHolder holder = new RecyclerItemViewHolder(context, v);
return holder;
}
@Override
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, int position) {
RecyclerItemViewHolder recyclerItemViewHolder = (RecyclerItemViewHolder) holder;
recyclerItemViewHolder.setVideoHelper(smallVideoHelper, gsySmallVideoHelperBuilder);
recyclerItemViewHolder.setRecyclerBaseAdapter(this);
recyclerItemViewHolder.onBind(position, itemDataList.get(position));
}
@Override
public int getItemCount() {
return itemDataList.size();
}
@Override
public int getItemViewType(int position) {
return 1;
}
public void setListData(List<VideoModel> data) {
itemDataList = data;
notifyDataSetChanged();
}
public GSYVideoHelper getVideoHelper() {
return smallVideoHelper;
}
public void setVideoHelper(GSYVideoHelper smallVideoHelper, GSYVideoHelper.GSYVideoHelperBuilder gsySmallVideoHelperBuilder) {
this.smallVideoHelper = smallVideoHelper;
this.gsySmallVideoHelperBuilder = gsySmallVideoHelperBuilder;
}
}
6.2 RecyclerNormalAdapter.java
/**
* Created on 2022/6/1 9:42
*
* @author Gong Youqiang
*/
public class RecyclerNormalAdapter extends RecyclerView.Adapter {
private final static String TAG = "RecyclerBaseAdapter";
private List<VideoModel> itemDataList = null;
private Context context = null;
public RecyclerNormalAdapter(Context context, List<VideoModel> itemDataList) {
this.itemDataList = itemDataList;
this.context = context;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent,
int viewType) {
View v = LayoutInflater.from(context).inflate(R.layout.item_home, parent, false);
final RecyclerView.ViewHolder holder = new RecyclerItemNormalHolder(context, v);
return holder;
}
@Override
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, int position) {
RecyclerItemNormalHolder recyclerItemViewHolder = (RecyclerItemNormalHolder) holder;
recyclerItemViewHolder.setRecyclerBaseAdapter(this);
recyclerItemViewHolder.onBind(position, itemDataList.get(position));
}
@Override
public int getItemCount() {
return itemDataList.size();
}
@Override
public int getItemViewType(int position) {
return 1;
}
public void setListData(List<VideoModel> data) {
itemDataList = data;
notifyDataSetChanged();
}
}
7. holder
7.1 RecyclerItemBaseHolder.java
/**
* Created on 2022/6/1 14:02
*
* @author Gong Youqiang
*/
public class RecyclerItemBaseHolder extends RecyclerView.ViewHolder {
RecyclerView.Adapter recyclerBaseAdapter;
public RecyclerItemBaseHolder(View itemView) {
super(itemView);
}
public RecyclerView.Adapter getRecyclerBaseAdapter() {
return recyclerBaseAdapter;
}
public void setRecyclerBaseAdapter(RecyclerView.Adapter recyclerBaseAdapter) {
this.recyclerBaseAdapter = recyclerBaseAdapter;
}
}
7.2 RecyclerItemNormalHolder.java
/**
* Created on 2022/6/1 9:43
*
* @author Gong Youqiang
*/
public class RecyclerItemNormalHolder extends RecyclerItemBaseHolder {
public final static String TAG = "RecyclerView2List";
protected Context context;
SampleCoverVideo gsyVideoPlayer;
ImageView imageView;
GSYVideoOptionBuilder gsyVideoOptionBuilder;
public RecyclerItemNormalHolder(Context context, View v) {
super(v);
this.context = context;
gsyVideoPlayer = v.findViewById(R.id.video_item_player);
imageView = new ImageView(context);
gsyVideoOptionBuilder = new GSYVideoOptionBuilder();
}
public void onBind(final int position, VideoModel videoModel) {
String url;
String title;
if (position % 2 == 0) {
url = "https://pointshow.oss-cn-hangzhou.aliyuncs.com/McTk51586843620689.mp4";
title = "这是title";
} else {
url = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4";
title = "哦?Title?";
}
Map<String, String> header = new HashMap<>();
header.put("ee", "33");
//防止错位,离开释放
//gsyVideoPlayer.initUIState();
gsyVideoOptionBuilder
.setIsTouchWiget(false)
//.setThumbImageView(imageView)
.setUrl(url)
.setVideoTitle(title)
.setCacheWithPlay(false)
.setRotateViewAuto(true)
.setLockLand(true)
.setPlayTag(TAG)
.setMapHeadData(header)
.setShowFullAnimation(true)
.setNeedLockFull(true)
.setPlayPosition(position)
.setVideoAllCallBack(new GSYSampleCallBack() {
@Override
public void onPrepared(String url, Object... objects) {
super.onPrepared(url, objects);
if (!gsyVideoPlayer.isIfCurrentIsFullscreen()) {
//静音
GSYVideoManager.instance().setNeedMute(true);
}
}
@Override
public void onQuitFullscreen(String url, Object... objects) {
super.onQuitFullscreen(url, objects);
//全屏不静音
GSYVideoManager.instance().setNeedMute(true);
}
@Override
public void onEnterFullscreen(String url, Object... objects) {
super.onEnterFullscreen(url, objects);
GSYVideoManager.instance().setNeedMute(false);
gsyVideoPlayer.getCurrentPlayer().getTitleTextView().setText((String) objects[0]);
}
}).build(gsyVideoPlayer);
//增加title
gsyVideoPlayer.getTitleTextView().setVisibility(View.GONE);
//设置返回键
gsyVideoPlayer.getBackButton().setVisibility(View.GONE);
//设置全屏按键功能
gsyVideoPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
resolveFullBtn(gsyVideoPlayer);
}
});
gsyVideoPlayer.loadCoverImageBy(R.mipmap.a, R.mipmap.b);
}
/**
* 全屏幕按键处理
*/
private void resolveFullBtn(final StandardGSYVideoPlayer standardGSYVideoPlayer) {
standardGSYVideoPlayer.startWindowFullscreen(context, true, true);
}
public SampleCoverVideo getPlayer() {
return gsyVideoPlayer;
}
}
7.3 RecyclerItemViewHolder
/**
* Created on 2022/6/1 14:25
*
* @author Gong Youqiang
*/
public class RecyclerItemViewHolder extends RecyclerItemBaseHolder {
public final static String TAG = "RecyclerView2List";
protected Context context ;
FrameLayout listItemContainer;
ImageView listItemBtn;
ImageView imageView;
private GSYVideoHelper smallVideoHelper;
private GSYVideoHelper.GSYVideoHelperBuilder gsySmallVideoHelperBuilder;
public RecyclerItemViewHolder(Context context, View v) {
super(v);
this.context = context;
listItemContainer = v.findViewById(R.id.list_item_container);
listItemBtn = v.findViewById(R.id.list_item_btn);
imageView = new ImageView(context);
}
public void onBind(final int position, VideoModel videoModel) {
//增加封面
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setImageResource(R.mipmap.a);
smallVideoHelper.addVideoPlayer(position, imageView, TAG, listItemContainer, listItemBtn);
listItemBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
smallVideoHelper.setPlayPositionAndTag(position, TAG);
getRecyclerBaseAdapter().notifyDataSetChanged();
//listVideoUtil.setLoop(true);
String url;
if (position % 2 == 0) {
url = "https://res.exexm.com/cw_145225549855002";
} else {
url = "http://7xjmzj.com1.z0.glb.clouddn.com/20171026175005_JObCxCE2.mp4";
}
//listVideoUtil.setCachePath(new File(FileUtils.getPath()));
gsySmallVideoHelperBuilder.setVideoTitle("title " + position).setUrl(url);
smallVideoHelper.startPlay();
//必须在startPlay之后设置才能生效
//listVideoUtil.getGsyVideoPlayer().getTitleTextView().setVisibility(View.VISIBLE);
}
});
}
public void setVideoHelper(GSYVideoHelper smallVideoHelper, GSYVideoHelper.GSYVideoHelperBuilder gsySmallVideoHelperBuilder) {
this.smallVideoHelper = smallVideoHelper;
this.gsySmallVideoHelperBuilder = gsySmallVideoHelperBuilder;
}
}
8. 使用 MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
LinearLayoutManager linearLayoutManager;
RecyclerBaseAdapter recyclerBaseAdapter;
List<VideoModel> dataList = new ArrayList<>();
boolean mFull = false;
ScrollCalculatorHelper scrollCalculatorHelper;
@BindView(R.id.rv_home)
RecyclerView mRecyclerView;
@BindView(R.id.topbar)
QMUITopBar mTopBar;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
getWindow().setEnterTransition(new Explode());
getWindow().setExitTransition(new Explode());
}
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
resolveData();
mTopBar.setTitle("视频号");
//限定范围为屏幕一半的上下偏移180
int playTop = CommonUtil.getScreenHeight(this) / 2 - CommonUtil.dip2px(this, 180);
int playBottom = CommonUtil.getScreenHeight(this) / 2 + CommonUtil.dip2px(this, 180);
//自定播放帮助类
scrollCalculatorHelper = new ScrollCalculatorHelper(R.id.video_item_player, playTop, playBottom);
final RecyclerNormalAdapter recyclerNormalAdapter = new RecyclerNormalAdapter(this, dataList);
linearLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(linearLayoutManager);
mRecyclerView.addItemDecoration(new MyItemDecoration());
mRecyclerView.setAdapter(recyclerNormalAdapter);
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
int firstVisibleItem, lastVisibleItem;
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
scrollCalculatorHelper.onScrollStateChanged(recyclerView, newState);
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
firstVisibleItem = linearLayoutManager.findFirstVisibleItemPosition();
lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
//这是滑动自动播放的代码
if (!mFull) {
scrollCalculatorHelper.onScroll(recyclerView, firstVisibleItem, lastVisibleItem, lastVisibleItem - firstVisibleItem);
}
}
});
}
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
//如果旋转了就全屏
mFull = newConfig.orientation == ActivityInfo.SCREEN_ORIENTATION_USER;
}
@Override
public void onBackPressed() {
if (GSYVideoManager.backFromWindowFull(this)) {
return;
}
super.onBackPressed();
}
@Override
protected void onPause() {
super.onPause();
GSYVideoManager.onPause();
}
@Override
protected void onResume() {
super.onResume();
GSYVideoManager.onResume();
}
@Override
protected void onDestroy() {
super.onDestroy();
GSYVideoManager.releaseAllVideos();
}
private void resolveData() {
for (int i = 0; i < 19; i++) {
VideoModel videoModel = new VideoModel();
dataList.add(videoModel);
}
if (recyclerBaseAdapter != null)
recyclerBaseAdapter.notifyDataSetChanged();
}
public static class MyItemDecoration extends RecyclerView.ItemDecoration{
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
outRect.set(10,20,10,20);
}
}
}