最近因为要做一个广告机上图片视频轮播,所以又是百度,github上一堆找,结果发现并没有特别合适的,不是播放视频首帧黑屏,就是切换界面后,没法继续播放,或者动态更新数据后,没法继续轮播等等问题,所以最后结合别人的方案自己修改了一下,具体参考android 实现图片和视频混合轮播_android 图片视频轮播_a1466850214的博客-CSDN博客
修改完成后,1.可以实现无黑屏
2.切换界面后,回到播放界面继续上次播放位置播放,
3.不支持点击和手动切换
4.动态更新数据后继续播放
下面是具体代码:
1.播放实体类(因为评论区要图片时长自定义,加了一个自定义时间)
public class Advance {
public String path;//路径 我使用的是本地绝对路径
public String type;//类型 1、视频 2、图片
private int playSecond;//图片播放时长
public Advance(String path, String type,int playSecond) {
this.path = path;
this.type = type;
this.playSecond = playSecond;
}
public int getPlaySecond() {
return playSecond;
}
public void setPlaySecond(int playSecond) {
this.playSecond = playSecond;
}
}
2.图片类
package com.anssy.smartbox.selfview.videoplay;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import com.anssy.smartbox.utils.glide.GlideApp;
public class AdvanceImageView extends RelativeLayout {
private ImageView imageView;
public AdvanceImageView(Context context) {
super(context);
initView();
}
public AdvanceImageView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public AdvanceImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView() {
imageView = new ImageView(getContext());
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
addView(imageView, new RelativeLayout.LayoutParams(-1, -1));
}
public void setImage(String path) {
GlideApp.with(getContext()).load(path).into(imageView);
}
}
3.视频播放类,这里面的播放视频控件,没有采用和其他博主用的系统VideoView,这里引用一个第三方,大家可以在gradle里面导入,至于为什么引入第三方,1.可以实现缓存2.视频pause后,切回后,可以无缝播放,Application中加入初始化,如果有视频播放不了,请切换内核为AndroidMediaFactory.create()或切换为ExoPlayer,具体参考GitHub - Doikki/DKVideoPlayer: Android Video Player. 安卓视频播放器,封装MediaPlayer、ExoPlayer、IjkPlayer。模仿抖音并实现预加载,列表播放,悬浮播放,广告播放,弹幕,视频水印,视频滤镜
VideoViewManager.setConfig(VideoViewConfig.newBuilder()
//使用使用IjkPlayer解码
.setPlayerFactory(IjkPlayerFactory.create())
.build());
implementation 'xyz.doikki.android.dkplayer:dkplayer-java:3.3.7'
implementation 'xyz.doikki.android.dkplayer:player-ijk:3.3.7'
implementation 'xyz.doikki.android.dkplayer:videocache:3.3.7'
package com.anssy.smartbox.selfview.videoplay;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewParent;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import com.anssy.smartbox.R;
import com.anssy.smartbox.utils.cache.PreloadManager;
import com.anssy.smartbox.utils.glide.GlideApp;
import com.bumptech.glide.request.RequestOptions;
import xyz.doikki.videoplayer.ijk.IjkPlayer;
import xyz.doikki.videoplayer.player.AbstractPlayer;
import xyz.doikki.videoplayer.player.VideoView;
import xyz.doikki.videoplayer.player.VideoViewManager;
public class AdvanceVideoView extends RelativeLayout {
public ImageView imageView;
private VideoView videoView;
private RelativeLayout videoRela;
private String path;
public int currentPosition;
public AdvanceVideoView(Context context) {
super(context);
initView();
}
public AdvanceVideoView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public AdvanceVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private PreloadManager mPreloadManager;
private void initView() {
videoRela = new RelativeLayout(getContext());
addView(videoRela, new RelativeLayout.LayoutParams(-1, -1));
imageView = new ImageView(getContext());
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
addView(imageView, new RelativeLayout.LayoutParams(-1, -1));
}
public void setImage(String path,VideoView videoView,PreloadManager preloadManager) {
this.path = path;
this.videoView = videoView;
this.mPreloadManager = preloadManager;
GlideApp.with(getContext()) .setDefaultRequestOptions(
new RequestOptions()
.frame(0)
).load(path).into(imageView);
}
/**
* 将View从父控件中移除
*/
public static void removeViewFormParent(View v) {
if (v == null) return;
ViewParent parent = v.getParent();
if (parent instanceof RelativeLayout) {
((RelativeLayout) parent).removeView(v);
}
}
public void setVideo() {
// if (videoView != null) {
// videoView.release();
// videoRela.removeView(videoView);
// videoView = null;
// }
removeViewFormParent(videoView);
videoView.release();
videoView.setPlayerBackgroundColor(getContext().getResources().getColor(R.color.main_color));
// videoView = new VideoView(getContext());
videoView.setScreenScaleType(VideoView.SCREEN_SCALE_MATCH_PARENT);
// HttpProxyCacheServer proxy = BaseApplication.getProxy(getContext());
// String proxyUrl = proxy.getProxyUrl(path);
// videoView.setVideoPath(proxyUrl);
//videoView.setVideoPath(path);
// videoView.setBackgroundColor(Color.TRANSPARENT);
String playUrl = mPreloadManager.getPlayUrl(path);
videoView.setUrl(playUrl);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(-1, -1);
//设置videoview占满父view播放
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
videoRela.addView(videoView, layoutParams);
videoView.start();
// videoView.setOnPreparedListener(mediaPlayer -> {
// new Handler().postDelayed(() -> {
// imageView.setVisibility(GONE);
// }, 400);//防止videoview播放视频前有个闪烁的黑屏
// });
}
public void setDestroy(){
if (videoView!=null){
videoView.release();
}
}
public void setPause() {
if (videoView != null) {
videoView.pause();
//currentPosition = videoView.getCurrentPosition();
// imageView.setVisibility(VISIBLE);
}
}
public void setRestart() {
if (videoView != null) {
videoView.resume();
// videoView.seekTo(currentPosition);
//videoView.start();
}
}
}
4.第三方缓存类
package com.anssy.smartbox.utils.cache;
import android.content.Context;
import android.util.Log;
import com.danikula.videocache.HttpProxyCacheServer;
import java.io.File;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import xyz.doikki.videoplayer.util.L;
/**
* 抖音预加载工具,使用AndroidVideoCache实现
*/
public class PreloadManager {
private static PreloadManager sPreloadManager;
/**
* 单线程池,按照添加顺序依次执行{@link PreloadTask}
*/
private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
/**
* 保存正在预加载的{@link PreloadTask}
*/
private final LinkedHashMap<String, PreloadTask> mPreloadTasks = new LinkedHashMap<>();
/**
* 标识是否需要预加载
*/
public boolean mIsStartPreload = true;
private final HttpProxyCacheServer mHttpProxyCacheServer;
/**
* 预加载的大小,每个视频预加载1M,这个参数可根据实际情况调整
*/
public static final int PRELOAD_LENGTH = 1024 * 1024;
private PreloadManager(Context context) {
mHttpProxyCacheServer = ProxyVideoCacheManager.getProxy(context);
}
public static PreloadManager getInstance(Context context) {
if (sPreloadManager == null) {
synchronized (PreloadManager.class) {
if (sPreloadManager == null) {
sPreloadManager = new PreloadManager(context.getApplicationContext());
}
}
}
return sPreloadManager;
}
/**
* 开始预加载
*
* @param rawUrl 原始视频地址
*/
public void addPreloadTask(String rawUrl, int position) {
if (isPreloaded(rawUrl)) return;
if (mPreloadTasks.containsKey(rawUrl)){
return;
}
PreloadTask task = new PreloadTask();
task.mRawUrl = rawUrl;
task.mPosition = position;
task.mCacheServer = mHttpProxyCacheServer;
L.i("addPreloadTask: " + position);
mPreloadTasks.put(rawUrl, task);
if (mIsStartPreload) {
//开始预加载
task.executeOn(mExecutorService);
}
}
/**
* 判断该播放地址是否已经预加载
*/
public boolean isPreloaded(String rawUrl) {
//先判断是否有缓存文件,如果已经存在缓存文件,并且其大小大于300KB,则表示已经预加载完成了
File cacheFile = mHttpProxyCacheServer.getCacheFile(rawUrl);
if (cacheFile.exists()) {
if (cacheFile.length() >= 1024 * 300) {
return true;
} else {
//这种情况一般是缓存出错,把缓存删掉,重新缓存
cacheFile.delete();
return false;
}
}
//再判断是否有临时缓存文件,如果已经存在临时缓存文件,并且临时缓存文件超过了预加载大小,则表示已经预加载完成了
File tempCacheFile = mHttpProxyCacheServer.getTempCacheFile(rawUrl);
if (tempCacheFile.exists()) {
return tempCacheFile.length() >= PRELOAD_LENGTH;
}
return false;
}
/**
* 暂停预加载
* 根据是否反向滑动取消在position之下或之上的PreloadTask
*
* @param position 当前滑到的位置
* @param isReverseScroll 列表是否反向滑动
*/
public void pausePreload(int position, boolean isReverseScroll) {
L.d("pausePreload:" + position + " isReverseScroll: " + isReverseScroll);
mIsStartPreload = false;
for (Map.Entry<String, PreloadTask> next : mPreloadTasks.entrySet()) {
PreloadTask task = next.getValue();
if (isReverseScroll) {
if (task.mPosition >= position) {
task.cancel();
}
} else {
if (task.mPosition <= position) {
task.cancel();
}
}
}
}
/**
* 恢复预加载
* 根据是否反向滑动开始在position之下或之上的PreloadTask
*
* @param position 当前滑到的位置
* @param isReverseScroll 列表是否反向滑动
*/
public void resumePreload(int position, boolean isReverseScroll) {
L.d("resumePreload:" + position + " isReverseScroll: " + isReverseScroll);
mIsStartPreload = true;
for (Map.Entry<String, PreloadTask> next : mPreloadTasks.entrySet()) {
PreloadTask task = next.getValue();
if (isReverseScroll) {
if (task.mPosition == position) {
if (!isPreloaded(task.mRawUrl)) {
task.executeOn(mExecutorService);
}
}
} else {
if (task.mPosition == position) {
if (!isPreloaded(task.mRawUrl)) {
task.executeOn(mExecutorService);
}
}
}
}
}
/**
* 通过原始地址取消预加载
*
* @param rawUrl 原始地址
*/
public void removePreloadTask(String rawUrl) {
PreloadTask task = mPreloadTasks.get(rawUrl);
if (task != null) {
task.cancel();
mPreloadTasks.remove(rawUrl);
}
}
/**
* 取消所有的预加载
*/
public void removeAllPreloadTask() {
Iterator<Map.Entry<String, PreloadTask>> iterator = mPreloadTasks.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, PreloadTask> next = iterator.next();
PreloadTask task = next.getValue();
task.cancel();
iterator.remove();
}
}
/**
* 获取播放地址
*/
public String getPlayUrl(String rawUrl) {
// PreloadTask task = mPreloadTasks.get(rawUrl);
// if (task != null) {
// task.cancel();
// }
if (isPreloaded(rawUrl)) {
return mHttpProxyCacheServer.getProxyUrl(rawUrl);
} else {
return rawUrl;
}
}
}
package com.anssy.smartbox.utils.cache;
import com.danikula.videocache.HttpProxyCacheServer;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import xyz.doikki.videoplayer.util.L;
/**
* 原理:主动去请求VideoCache生成的代理地址,触发VideoCache缓存机制
* 缓存到 PreloadManager.PRELOAD_LENGTH 的数据之后停止请求,完成预加载
* 播放器去播放VideoCache生成的代理地址的时候,VideoCache会直接返回缓存数据,
* 从而提升播放速度
*/
public class PreloadTask implements Runnable {
/**
* 原始地址
*/
public String mRawUrl;
/**
* 列表中的位置
*/
public int mPosition;
/**
* VideoCache服务器
*/
public HttpProxyCacheServer mCacheServer;
/**
* 是否被取消
*/
private boolean mIsCanceled;
/**
* 是否正在预加载
*/
private boolean mIsExecuted;
private final static List<String> blackList = new ArrayList<>();
@Override
public void run() {
if (!mIsCanceled) {
start();
}
mIsExecuted = false;
mIsCanceled = false;
}
/**
* 开始预加载
*/
private void start() {
// 如果在小黑屋里不加载
if (blackList.contains(mRawUrl)) return;
L.i("预加载开始:" + mPosition);
HttpURLConnection connection = null;
try {
//获取HttpProxyCacheServer的代理地址
String proxyUrl = mCacheServer.getProxyUrl(mRawUrl);
URL url = new URL(proxyUrl);
connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5_000);
connection.setReadTimeout(5_000);
InputStream in = new BufferedInputStream(connection.getInputStream());
int length;
int read = -1;
byte[] bytes = new byte[8 * 1024];
while ((length = in.read(bytes)) != -1) {
read += length;
//预加载完成或者取消预加载
if (mIsCanceled || read >= PreloadManager.PRELOAD_LENGTH) {
if (mIsCanceled) {
L.i("预加载取消:" + mPosition + " 读取数据:" + read + " Byte");
} else {
L.i("预加载成功:" + mPosition + " 读取数据:" + read + " Byte");
}
break;
}
}
} catch (Exception e) {
L.i("预加载异常:" + mPosition + " 异常信息:"+ e.getMessage());
// 关入小黑屋
blackList.add(mRawUrl);
} finally {
if (connection != null) {
connection.disconnect();
}
L.i("预加载结束: " + mPosition);
}
}
/**
* 将预加载任务提交到线程池,准备执行
*/
public void executeOn(ExecutorService executorService) {
if (mIsExecuted) return;
mIsExecuted = true;
executorService.submit(this);
}
/**
* 取消预加载任务
*/
public void cancel() {
if (mIsExecuted) {
mIsCanceled = true;
}
}
}
package com.anssy.smartbox.utils.cache;
import android.content.Context;
import com.danikula.videocache.HttpProxyCacheServer;
import com.danikula.videocache.StorageUtils;
import java.io.File;
public class ProxyVideoCacheManager {
private static HttpProxyCacheServer sharedProxy;
private ProxyVideoCacheManager() {
}
public static HttpProxyCacheServer getProxy(Context context) {
return sharedProxy == null ? (sharedProxy = newProxy(context)) : sharedProxy;
}
private static HttpProxyCacheServer newProxy(Context context) {
return new HttpProxyCacheServer.Builder(context)
.maxCacheSize(512 * 1024 * 1024) // 512MB for cache
//缓存路径,不设置默认在sd_card/Android/data/[app_package_name]/cache中
// .cacheDirectory()
.build();
}
/**
* 删除所有缓存文件
* @return 返回缓存是否删除成功
*/
public static boolean clearAllCache(Context context) {
getProxy(context);
return StorageUtils.deleteFiles(sharedProxy.getCacheRoot());
}
/**
* 删除url对应默认缓存文件
* @return 返回缓存是否删除成功
*/
public static boolean clearDefaultCache(Context context, String url) {
getProxy(context);
File pathTmp = sharedProxy.getTempCacheFile(url);
File path = sharedProxy.getCacheFile(url);
return StorageUtils.deleteFile(pathTmp.getAbsolutePath()) &&
StorageUtils.deleteFile(path.getAbsolutePath());
}
}
5.播放控件,其实是ViewPager切换
package com.anssy.smartbox.selfview.videoplay;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.RelativeLayout;
import androidx.viewpager.widget.ViewPager;
import java.util.ArrayList;
import java.util.List;
public class AdvanceView extends RelativeLayout {
private ViewPager viewPager;
private List<View> views = new ArrayList<>();
public AdvancePagerAdapter adapter;
public AdvanceView(Context context) {
super(context);
initView();
}
public AdvanceView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public AdvanceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
@SuppressLint("ClickableViewAccessibility")
private void initView() {
viewPager = new ViewPager(getContext());
viewPager.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
adapter = new AdvancePagerAdapter(getContext(),viewPager);
viewPager.setAdapter(adapter);
addView(viewPager, new RelativeLayout.LayoutParams(-1, -1));
}
public void setData(List<Advance> advances) {
adapter.setData(advances);
}
public void setDestroy(){
adapter.setDestroy();
}
public void setPause(){
adapter.setPause();
}
public void setResume(){
adapter.setResume();
}
}
6.ViewPager的适配器,里面原作者的代码我并未删除,大家也可以对比下,或者区别调用下,看下效果,我将原作者的thread定时器,改为了handler的方法,这样可操作性要强一点,避免动态切换数据后,轮播卡死的情况。这里我用的是自己定义的handler,大家替换为系统的handler即可
package com.anssy.smartbox.selfview.videoplay;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
import com.anssy.smartbox.utils.WeakHandler;
import com.anssy.smartbox.utils.cache.PreloadManager;
import java.util.ArrayList;
import java.util.List;
import tv.danmaku.ijk.media.player.IjkMediaPlayer;
import xyz.doikki.videoplayer.player.BaseVideoView;
import xyz.doikki.videoplayer.player.VideoView;
public class AdvancePagerAdapter extends PagerAdapter implements ViewPager.OnPageChangeListener,BaseVideoView.OnStateChangeListener {
private Context context;
private ViewPager viewPager;
private List<Advance> datas;
private List<View> list = new ArrayList<>();
private int current = 0;
private int time = 5000;
private boolean pause;
private Thread thread;
private int lastPosition = -1;
private final VideoView videoView;
private final PreloadManager mPreloadManager;
public AdvancePagerAdapter(Context context, ViewPager viewPager) {
this.context = context;
mPreloadManager = PreloadManager.getInstance(context);
videoView = new VideoView(context);
// 清空DNS,有时因为在APP里面要播放多种类型的视频(如:MP4,直播,直播平台保存的视频,和其他http视频), 有时会造成因为DNS的问题而报10000问题的
//videoView.addFormatOption( "dns_cache_clear", 1);
videoView.addOnStateChangeListener(this);
this.viewPager = viewPager;
}
private boolean isRunning = false;
public void setData(List<Advance> advances) {
if (advances.size() == 0) return;
for (int i = 0; i < advances.size(); i++) {
if (advances.get(i).type.equals("1")){
mPreloadManager.addPreloadTask(advances.get(i).path,i);
}
}
if (isRunning){
if (list.get(viewPager.getCurrentItem()) instanceof AdvanceVideoView){
((AdvanceVideoView) list.get(viewPager.getCurrentItem())).setDestroy();
((AdvanceVideoView) list.get(viewPager.getCurrentItem())).currentPosition = 0;
}
stopTimer();
}else {
viewPager.addOnPageChangeListener(this);
}
viewPager.removeAllViews();
this.datas = advances;
list.clear();
addView(advances.get(advances.size() - 1));
if (advances.size() > 1) { //多于1个要循环
for (Advance d : advances) { //中间的N个(index:1~N)
addView(d);
}
addView(advances.get(0));
}
notifyDataSetChanged();
if (advances.get(0).type.equals("1")) {//有人反应第一个是视频不播放这边优化了一下
// if ( ((AdvanceVideoView) list.get(viewPager.getCurrentItem())).imageView!=null){
// ((AdvanceVideoView) list.get(viewPager.getCurrentItem())).imageView.setVisibility(View.VISIBLE);
// }
((AdvanceVideoView) list.get(viewPager.getCurrentItem())).setVideo();
}else{
time = advances.get(0).getPlaySecond()*1000;
}
//在外层,将mViewPager初始位置设置为1即可
if (advances.size() > 1) { //多于1个,才循环并开启定时器
viewPager.setCurrentItem(1);
startNewTimer();
}
}
private void addView(Advance advance) {
if (advance.type.equals("1")) {
AdvanceVideoView videoView = new AdvanceVideoView(context);
videoView.setImage(advance.path,this.videoView,mPreloadManager);
list.add(videoView);
} else {
AdvanceImageView imageView = new AdvanceImageView(context);
imageView.setImage(advance.path);
list.add(imageView);
}
}
private int index;
private final WeakHandler weakHandler = new WeakHandler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
if (msg.what==22){
index++;
if (index==20){
mPreloadManager.removePreloadTask(datas.get(viewPager.getCurrentItem()-1).path);
index==0;
mPreloadManager.addPreloadTask(datas.get(viewPager.getCurrentItem()-1).path,viewPager.getCurrentItem()-1);
weakHandler.removeCallbacks(mTimePauseRun);
viewPager.setCurrentItem(viewPager.getCurrentItem()+1,true);
}
}
return true;
}
});
public void startNewTimer(){
isRunning = true;
weakHandler.post(timeRunnable);
}
private void stopTimer(){
isRunning = false;
current = 0;
lastPosition = -1;
weakHandler.removeCallbacks(timeRunnable);
}
private final Runnable timeRunnable = new Runnable() {
@Override
public void run() {
if (!pause && !(list.get(viewPager.getCurrentItem()) instanceof AdvanceVideoView)){
current+=1000;
}
if (current >= time) {
viewPager.post(() -> viewPager.setCurrentItem(viewPager.getCurrentItem() + 1, true));
current = 0;
}
weakHandler.postDelayed(this,1000);
}
};
private void startTimer() {
if (thread != null && !thread.isInterrupted()) {
thread.interrupt();
thread = null;
}
thread = new Thread(() -> {
while (thread != null && !thread.isInterrupted()) {
try {
Thread.sleep(1000);
if (!pause && !(list.get(viewPager.getCurrentItem()) instanceof AdvanceVideoView))
current += 1000;
if (current >= time) {
viewPager.post(() -> viewPager.setCurrentItem(viewPager.getCurrentItem() + 1, true));
current = 0;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
}
@Override
public int getCount() {
return list.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (position<list.size()){
container.removeView(list.get(position));
}
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
View view = list.get(position);
container.addView(view);
return view;
}
@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
//
// // 实现ViewPager.OnPageChangeListener接口
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
// 什么都不干
}
@Override
public void onPageScrollStateChanged(int state) {
// 由于viewpager的预加载机制onPageSelected这里面加载videoview 放的跟玩一样 等操作完成后再播放videoview就香了 很丝滑
if (state == 0) {
if (list.size() > 1) { //多于1,才会循环跳转
if (lastPosition != -1 && lastPosition != viewPager.getCurrentItem() && list.get(lastPosition) instanceof AdvanceVideoView) {
((AdvanceVideoView) list.get(lastPosition)).setPause();
}
if (viewPager.getCurrentItem() < 1) { //首位之前,跳转到末尾(N)
int position = datas.size(); //注意这里是mList,而不是mViews
viewPager.setCurrentItem(position, false);
} else if (viewPager.getCurrentItem() > datas.size()) { //末位之后,跳转到首位(1)
viewPager.setCurrentItem(1, false); //false:不显示跳转过程的动画
}
current = 0;//换页重新计算时间
if (list.get(viewPager.getCurrentItem()) instanceof AdvanceVideoView) {
if (!mPreloadManager.isPreloaded(datas.get(viewPager.getCurrentItem()-1).path)){
mPreloadManager.resumePreload(viewPager.getCurrentItem()-1,false);
}
((AdvanceVideoView) list.get(viewPager.getCurrentItem())).setVideo();
}else{
time = datas.get(viewPager.getCurrentItem()-1).getPlaySecond()*1000;
}
lastPosition = viewPager.getCurrentItem();
}
}
}
public void setDestroy(){
pause = true;
if (list.size() > 0 && list.get(viewPager.getCurrentItem()) instanceof AdvanceVideoView) {
((AdvanceVideoView) list.get(viewPager.getCurrentItem())).setDestroy();
Log.e("调用销毁", " destroy");
}
weakHandler.removeCallbacksAndMessages(null);
}
public void setPause() {
pause = true;
if (list.size() > 0 && list.get(viewPager.getCurrentItem()) instanceof AdvanceVideoView) {
((AdvanceVideoView) list.get(viewPager.getCurrentItem())).setPause();
Log.e("调用暂停", " pause");
}
}
public void setResume() {
pause = false;
if (list.size() > 0 && list.get(viewPager.getCurrentItem()) instanceof AdvanceVideoView) {
((AdvanceVideoView) list.get(viewPager.getCurrentItem())).setRestart();
Log.e("调用start", " start");
}
}
@Override
public void onPlayerStateChanged(int playerState) {
}
private final Runnable mTimePauseRun = new Runnable() {
@Override
public void run() {
weakHandler.sendEmptyMessage(22);
weakHandler.postDelayed(this,1000);
}
};
@Override
public void onPlayStateChanged(int playState) {
if (playState== VideoView.STATE_PLAYBACK_COMPLETED){
if (list.get(viewPager.getCurrentItem())instanceof AdvanceVideoView){
((AdvanceVideoView) list.get(viewPager.getCurrentItem())).currentPosition = 0;
if ( ((AdvanceVideoView) list.get(viewPager.getCurrentItem())).imageView!=null){
((AdvanceVideoView) list.get(viewPager.getCurrentItem())).imageView.setVisibility(View.VISIBLE);
}
}
if (list.size()>1){
viewPager.setCurrentItem(viewPager.getCurrentItem() + 1, true);
}else {
if (list.get(viewPager.getCurrentItem())instanceof AdvanceVideoView){
((AdvanceVideoView) list.get(viewPager.getCurrentItem())).setVideo();
}
}
}else if (playState==VideoView.STATE_PLAYING){
if (list.get(viewPager.getCurrentItem())instanceof AdvanceVideoView){
if ( ((AdvanceVideoView) list.get(viewPager.getCurrentItem())).imageView!=null){
((AdvanceVideoView) list.get(viewPager.getCurrentItem())).imageView.setVisibility(View.GONE);
}
}
}else if (playState==VideoView.STATE_ERROR){
mPreloadManager.pausePreload(viewPager.getCurrentItem()-1,false);
viewPager.setCurrentItem(viewPager.getCurrentItem() + 1, true);
}else if (playState==VideoView.STATE_BUFFERING){
weakHandler.post(mTimePauseRun);
}else if (playState==VideoView.STATE_BUFFERED) {
index = 0;
weakHandler.removeCallbacks(mTimePauseRun);
}
}
}
7.xml中引入
<com.anssy.smartbox.selfview.videoplay.AdvanceView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/home_vp"
android:clickable="false"
/>
8.activity中调用,oncreate中调用initData()即可,动态更新数据,只需调用data.clear(),mViewPager.setData(data)即可
private List<Advance> data = new ArrayList<>();
private void initView(){
mViewPager = findViewById(R.id.home_vp);
mViewPager.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
}
private void initData(){
data.clear();
//https://t7.baidu.com/it/u=1956604245,3662848045&fm=193&f=GIF
Advance advance1= new Advance(toURLString("https://t7.baidu.com/it/u=1956604245,3662848045&fm=193&f=GIF").trim(),"2",10);
data.add(advance1);
Advance advance = new Advance(toURLString("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4").trim(),"1",0);
data.add(advance);
// Advance advance2 = new Advance("http://vjs.zencdn.net/v/oceans.mp4","1",0);
// data.add(advance2);
Advance advance3 = new Advance(toURLString("https://t7.baidu.com/it/u=1415984692,3889465312&fm=193&f=GIF").trim(),"2",5);
data.add(advance3);
mViewPager.setData(data);
}
public static String toURLString(String str) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < str.length(); i++) {
char charAt = str.charAt(i);
if (charAt > 255) {
try {
sb.append(URLEncoder.encode(String.valueOf(charAt), "utf-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else {
sb.append(charAt);
}
}
return sb.toString();
}
@Override
protected void onResume() {
super.onResume();
mViewPager.setResume();
}
@Override
protected void onPause() {
super.onPause();
mViewPager.setPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
mViewPager.setDestroy();
}
9.播放效果
20230507103839
切换效果
20230507105453
源码下载地址(不建议下载):https://download.csdn.net/download/hardWork_yulu/87814212
看到很多小伙伴下载了源码,实在不忍心小伙伴使用HttpUrlConnection来进行网络请求,其实我在实际使用中也发现原有缓存存在一些不确定的问题,加上AndroidCache停止维护很久,所以我一直在寻找可替代的缓存框架,最后在GitHub - JeffMony/JeffVideoCache: Better than AndroidVideoCache, it supports M3U8 and MP4中使用作者的Cache框架,可使用okhttp,没有使用播放器,整合了一下。已将2.0视频轮播代码上传,感兴趣的小伙伴可以下载:https://download.csdn.net/download/hardWork_yulu/88059783
和1.0一样,一样下载在APP私有目录下,无需请求读写权限
效果图:
记得配置忽略http的xml文件,不然有的视频不能访问文章来源:https://www.toymoban.com/news/detail-596583.html
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <base-config cleartextTrafficPermitted="true"/> </network-security-config>
github地址:GitHub - yulu1121/VideoPhotoPlay: 一个安卓的视频图片轮播框架文章来源地址https://www.toymoban.com/news/detail-596583.html
到了这里,关于Android 广告机图片视频轮播的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!