Android 广告机图片视频轮播

最近因为要做一个广告机上图片视频轮播,所以又是百度,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 = ""
        if(path.startsWith("http")||path.startsWith("https")){
            playUrl = mPreloadManager.getPlayUrl(path);
        }else{
           playUrl = 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);
//            }

           if (list.size()>1){
                ((AdvanceVideoView) list.get(1)).setVideo();
            }else {
                ((AdvanceVideoView) list.get(0)).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文件,不然有的视频不能访问

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true"/>
</network-security-config>

github地址:GitHub - yulu1121/VideoPhotoPlay: 一个安卓的视频图片轮播框架

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 28
    评论
你可以使用Android Studio中的ViewPager和FragmentPagerAdapter来实现视频图片轮播效果。 首先,你需要创建一个ViewPager和一个FragmentPagerAdapter。具体实现可以参考以下代码: ``` public class MyPagerAdapter extends FragmentPagerAdapter { private List<Fragment> fragmentList; public MyPagerAdapter(FragmentManager fm, List<Fragment> fragmentList) { super(fm); this.fragmentList = fragmentList; } @Override public Fragment getItem(int position) { return fragmentList.get(position); } @Override public int getCount() { return fragmentList.size(); } } ``` 然后,你需要创建一个Fragment来展示视频图片。具体实现可以参考以下代码: ``` public class MyFragment extends Fragment { private int resId; public MyFragment(int resId) { this.resId = resId; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_my, container, false); ImageView imageView = view.findViewById(R.id.image_view); VideoView videoView = view.findViewById(R.id.video_view); if (resId == R.drawable.image1 || resId == R.drawable.image2 || resId == R.drawable.image3) { imageView.setVisibility(View.VISIBLE); videoView.setVisibility(View.GONE); imageView.setImageResource(resId); } else { imageView.setVisibility(View.GONE); videoView.setVisibility(View.VISIBLE); videoView.setVideoURI(Uri.parse("android.resource://" + getActivity().getPackageName() + "/" + resId)); videoView.start(); } return view; } } ``` 最后,你需要在Activity中初始化ViewPager并设置Adapter。具体实现可以参考以下代码: ``` List<Fragment> fragmentList = new ArrayList<>(); fragmentList.add(new MyFragment(R.drawable.image1)); fragmentList.add(new MyFragment(R.drawable.video1)); fragmentList.add(new MyFragment(R.drawable.image2)); fragmentList.add(new MyFragment(R.drawable.video2)); fragmentList.add(new MyFragment(R.drawable.image3)); ViewPager viewPager = findViewById(R.id.view_pager); MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager(), fragmentList); viewPager.setAdapter(adapter); viewPager.setOffscreenPageLimit(fragmentList.size()); ``` 这样,你就可以实现视频图片轮播效果了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 28
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值