原创 2017-08-10 认真的 小苏
视频列表滚动连播技术探究系列
1、仿网易/QQ空间视频列表滚动连播炫酷效果(V1.0 挖坑之路)。
2、仿网易/QQ空间视频列表滚动连播炫酷效果(V2.0 填坑之路) 想看源码的,看这篇文章。
3、仿网易视频列表滚动连播炫酷效果(v3.0 稳定版-思想改变及优化) 稳定版-进行优化和思想上的改变。
4、RecyclerView 平滑滚动可控制滚动速度的终极解决方案
5、仿网易视频列表连播炫酷效果 - v3.1 升级版-细节优化(网络状态切换、item点击事件等)
持续更新中.....
细节优化:
- 点击上一个item 或 下一个item 平滑的滚动并播放。
- 网络状态切换的处理:从WiFi切换到数据、数据切换到WiFi、无网络时切换到WiFi 和 数据的处理。
1.item 点击事件处理
这个就不细讲,非常常见的知识点,主要讲下滑动时的处理。
看下面 Adapter 代码
private ItemClickListener itemClickListener;
public VideoAdapter(Context context, ItemClickListener itemClickListener) {
this.context = context;
this.itemClickListener = itemClickListener;
}
@Override
public void onBindViewHolder(final VideoFeedHolder holder, final int position) {
holder.update(position, mlist);
View itemView = holder.itemView;
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
itemClickListener.onItemClick(v, position);
}
});
}
注意要在holder 中处理 ,itemView 是否可以点击
/**
* 显示当前播放的 item 的蒙层和 图片
*/
public void visMasked() {
img.setVisibility(View.VISIBLE);
video_masked.setVisibility(View.VISIBLE);
ll_not_wifi.setVisibility(View.GONE);
itemView.setEnabled(true);
}
/**
* 隐藏当前播放的 item 的蒙层和 图片
*/
public void goneMasked() {
img.setVisibility(View.GONE);
video_masked.setVisibility(View.GONE);
ll_not_wifi.setVisibility(View.GONE);
itemView.setEnabled(false);
}
Activity 中点击事件滑动的处理,看下面代码
adapter = new VideoAdapter(this, new ItemClickListener() {
@Override
public void onItemClick(View v, Object object) {
//手动点击下一个,暂停之前的 并显示蒙层
stopPlayer(playerPosition);
missVideoTips();
// 缓慢平滑的滚动到下一个
itemPosition = (int) object;
playerPosition = itemPosition;
rl_video.smoothScrollToPosition(itemPosition);
}
});
2.网络状态切换的处理
我们需要动态的监听网络状态,所以需要写一个BroadcastReceiver , 监听网络状态,并发送广播。
网络判断工具类
public class NetChangeManager {
private static NetChangeManager netChangeManager;
private Context context;
public NetChangeManager() {
}
public static NetChangeManager getInstance() {
if (netChangeManager == null) {
synchronized (NetChangeManager.class) {
if (netChangeManager == null) {
netChangeManager = new NetChangeManager();
}
}
}
return netChangeManager;
}
/**
* 获取网络类型
*
* @return 返回值 -1:没有网络 1:WIFI网络 2:wap网络3:net网络
*/
public int getNetType() {
int netType = -1;
ConnectivityManager connMgr = (ConnectivityManager) VideoApp.getAppContext().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
if (networkInfo == null) {
return netType;
}
int nType = networkInfo.getType();
Log.e("networkInfo", "networkInfo.getExtraInfo() is " + networkInfo.getExtraInfo());
if (nType == ConnectivityManager.TYPE_MOBILE) {
if (networkInfo.getExtraInfo() == null) {
netType = 2;
} else {
if (networkInfo.getExtraInfo().toLowerCase().equals("cmnet")) {
netType = 3;
} else {
netType = 2;
}
}
} else if (nType == ConnectivityManager.TYPE_WIFI) {
netType = 1;
}
return netType;
}
/**
* 当前是否有网络
*
* @return true - 有网 false 无网
*/
public boolean hasNet() {
return getNetType() != -1;
}
}
下面我们看一下 ,网络监听的广播。
public class NetworkConnectChangedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = manager.getActiveNetworkInfo();
if (activeNetworkInfo != null) {
if (activeNetworkInfo.isConnected()) {
if (activeNetworkInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
if (listener != null) {
Log.e("linksu",
"onReceive(NetworkConnectChangedReceiver.java:34) 切换到数据流量");
listener.dataNetwork(true);
}
} else if (activeNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
if (listener != null) {
Log.e("linksu",
"onReceive(NetworkConnectChangedReceiver.java:40) 切换到WiFi");
listener.wifiNetwork(true);
}
}
} else {
if (listener != null) {
Log.e("linksu",
"onReceive(NetworkConnectChangedReceiver.java:47) 无网络连接");
listener.notNetWork();
}
}
} else {
if (listener != null) {
Log.e("linksu",
"onReceive(NetworkConnectChangedReceiver.java:54) 无网络连接");
listener.notNetWork();
}
}
}
private ConnectChangedListener listener;
public void setConnectChangeListener(ConnectChangedListener listener) {
this.listener = listener;
}
public interface ConnectChangedListener {
void wifiNetwork(boolean flag);
void dataNetwork(boolean flag);
void notNetWork();
}
}
holder 中处理网络变化情况,在开始播放一个视频时,先调用这个方法
/**
* 判断是不是WiFi的情况
*/
public void playerWifi() {
if (!NetChangeManager.getInstance().hasNet()) {
img.setVisibility(View.VISIBLE);
ll_not_wifi.setVisibility(View.GONE);
iv_video_feed_start.setEnabled(false);
} else {
if (Constants.VIDEO_FEED_WIFI) {
ll_not_wifi.setVisibility(View.GONE);
iv_video_feed_start.setEnabled(false);
} else {
int netType = NetChangeManager.getInstance().getNetType();
if (netType != 1) {// 不是WiFi下的情况
img.setVisibility(View.GONE);
video_masked.setVisibility(View.GONE);
ll_not_wifi.setVisibility(View.VISIBLE);
iv_video_feed_start.setEnabled(true);
} else {
ll_not_wifi.setVisibility(View.GONE);
iv_video_feed_start.setEnabled(false);
}
}
}
}
/**
* 停止滚动手指抬起时 动态添加播放器,开始播放视频,并获取之前的播放进度
*
* @param recyclerView
*/
private void aoutPlayVideo(final RecyclerView recyclerView) {
if (!lVideoView.isPlayer()) {
VideoFeedHolder childViewHolder = (VideoFeedHolder) recyclerView.findViewHolderForAdapterPosition(itemPosition);
if (childViewHolder != null) {
// 注册监听以及隐藏蒙层
childViewHolder.registerVideoPlayerListener(this);
childViewHolder.goneMasked();
childViewHolder.playerWifi();
if (!NetChangeManager.getInstance().hasNet()) { // 无网络的情况
Toast.makeText(this, "无法连接到网络,请稍后再试", Toast.LENGTH_SHORT).show();
} else {
int netType = NetChangeManager.getInstance().getNetType();
if (netType == 1 || Constants.VIDEO_FEED_WIFI) { // WiFi的情况下,或者允许不是WiFi情况下继续播放
// 动态添加播放器
View itemView = childViewHolder.itemView;
FrameLayout frameLayout = (FrameLayout) itemView.findViewById(R.id.ll_video);
frameLayout.removeAllViews();
ViewGroup last = (ViewGroup) lVideoView.getParent();//找到videoitemview的父类,然后remove
if (last != null && last.getChildCount() > 0) {
last.removeAllViews();
}
frameLayout.addView(lVideoView);
// 获取播放进度
TabFragMainBeanItemBean itemBean = itemBeens.get(itemPosition);
long videoProgress = itemBean.videoProgress;
long duration = itemBean.mDuration;
if (videoProgress != 0 && videoProgress != duration) { // 跳转到之前的进度,继续播放
lVideoView.startLive(itemBean.video_url);
lVideoView.setSeekTo(videoProgress);
} else {//从头播放
lVideoView.startLive(itemBean.video_url);
}
}
}
}
}
}
最后一步,在Activity中注册广播,监听网络状态变化,处理如下:
@Override
public void wifiNetwork(boolean flag) {
aoutPlayVideo(rl_video);
}
@Override
public void dataNetwork(boolean flag) {
Log.e("linksu", "onReceive(dataNetwork:273) 切换到数据流量");
if (!Constants.VIDEO_FEED_WIFI) {
dataNetwork(itemPosition);
aoutPlayVideo(rl_video);
}
}
@Override
public void notNetWork() {
}
/**
* 数据流量时显示的逻辑
*
* @param position
*/
private void dataNetwork(int position) {
VideoFeedHolder childViewHolder = (VideoFeedHolder) rl_video.findViewHolderForAdapterPosition(position);
if (childViewHolder != null) {
View itemView = childViewHolder.itemView;
FrameLayout frameLayout = (FrameLayout) itemView.findViewById(R.id.ll_video);
frameLayout.removeAllViews();
lVideoView.stopVideoPlay();
TabFragMainBeanItemBean itemBean = itemBeens.get(position);
itemBean.videoProgress = currentPosition;
itemBean.mDuration = mDuration;
itemBeens.set(position, itemBean);
}
}
细节方面,处理完毕,如发现其他细节问题,请尽情的提出问题吧。
最后的最后,请不要客气,尽情的砸issue或者pull request过来吧!(https://github.com/susussa/VideoFeed)