Android 广告机图片视频轮播

这篇具有很好参考价值的文章主要介绍了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 = 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私有目录下,无需请求读写权限

效果图:

android 图片视频轮播框架,音视频,android,ffmpeg

记得配置忽略http的xml文件,不然有的视频不能访问

<?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模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • android 11及以上保存图片视频到相册

    Android 10之前版本主要步骤 请求读写权限 图片/视频下载到/storage/emulated/0/Android/data/包名/xxx 复制到系统相册目录下 扫描媒体库 Android 10及以上版本主要步骤 请求读写权限 图片/视频下载到/storage/emulated/0/Android/data/包名/xxx 创建ContentValues,写入要保存的信息 调用ContentResolver插入

    2024年02月11日
    浏览(44)
  • android项目实战之使用框架 集成多图片、视频的上传

    效果图  实现方式,本功能使用PictureSelector 第三方库  。作者项目地址:https://github.com/LuckSiege/PictureSelector 1. builder.gradle 增加 2. XML布局 3. 适配器,这里对GridImageAdapter进行了改进。 5. 点击增加弹框布局 6. 弹框页面初始化 7.  弹框页面监听初始化 8. 增加拍照回调,不加这

    2024年01月23日
    浏览(37)
  • Android修行手册-基础优化系列图片篇,ios音视频面试内容

    图片款=(480/480)*400=400 占用内存为300*400*4=480000 那么它占用内存为什么是变化的? Android会先解析图片文件本身的数据格式,然后还原成Bitmap对象,Bitmap的大小就跟上面的计算方式相关联。 再举例1080*452的png图片,图片占用存储空间大小为56kb,内存如图: 上图一目了然,不

    2024年04月27日
    浏览(66)
  • uniapp实现app端图片+视频轮播

    需求 :swiper轮播里面,可能有图片也可能有视频。当swiper切换到视频时,视频以动画的样式展示(无按钮、进度条等默认播放控件),自动轮播取消,手动滑动切换取消。当视频播放完毕后,可以自动轮播,可以手动滑动切换。 找了个插件市场里的改的。( 感谢大佬的插件

    2024年02月02日
    浏览(31)
  • uni-app 轮播图视频+图片 视频图片全屏预览 两种方法

    在做商城项目的时候,商品详情的轮播图需要同时显示视频和图片,并且能够全局预览 如果项目里有uview这个组件库,可以通过swiper轮播图的指定类型进行解决,点击这里进行跳转 也可以通过uni-app自带的swiper去解决这个问题,点击这里进行跳转 就目前的两种方案,第二种方

    2024年02月12日
    浏览(110)
  • uniapp实现移动端的视频图片轮播组件

    1、视频轮播组件app体验地址 https://tt.appc02.com/nesxr6 2、支持小程序、H5、APP,在小程序上运行的效果图 3、使用方法 第一步,按照截图步骤配置好 第二步:参考以下代码,使用视频图片轮播组件 3、组件属性说明 ad-list 广告的数据。数组形式,每个item需要包含url、isVideo字段,

    2024年02月10日
    浏览(34)
  • 使用vant+video.js实现轮播图图片和视频轮播播放

    先上效果图 1. 安装 2. 在需要用到的页面引入 3. 具体页面使用 假设传给子组件的数组结构 按照步骤来使用,其他地方视频播放情况都还好,至少安卓是好的,只是点击播放和暂停时候,ios需要点击多下才能触发点击事件; 然后以为是video.js插件可能没更新,ios版本迭代超过

    2023年04月08日
    浏览(47)
  • 前端Vue自定义轮播图视频播放组件 仿京东商品详情轮播图视频Video播放效果 可图片预览

    随着技术的发展,开发的复杂度也越来越高,传统开发方式将一个系统做成了整块应用,经常出现的情况就是一个小小的改动或者一个小功能的增加可能会引起整体逻辑的修改,造成牵一发而动全身。通过组件化开发,可以有效实现单独开发,单独维护,而且他们之间可以随

    2024年02月15日
    浏览(54)
  • Android---Banner轮播图

    轮播图是一种很常见的UI。 Banner框架 能够帮助我们快速开发,完成首页轮播图效果的需求。 1、导入Banner依赖 2、activity_main.xml布局。 banner_loop_time: 设置轮播间隔时间,默认3000;banner_radius: 设置轮播图的圆角   3、设置Banner 适配器 。 实现一个简单的轮播图效果,Banner框架已

    2024年02月01日
    浏览(44)
  • Android 优化广告图加载

    作用是:下载完成后,把图片显示在你广告图上,由于glide有三级缓存机制,因此,最好是提前在接口返回的时候,进行预加载,然后进入到广告启动的流程中的时候就可以直接复用上一次的bitmap,不会浪费相应的时间。因此思路如下: 接口数据---开始预加载广告图---》loa

    2024年01月20日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包