基于videoView的自定义播放控制器的视频播放器

1.概述

Android为我们提供了视频播放的控件VideoView,通过这个控件我们只需要设置controller,setVideoUrl然后调用start方法就可以播放视频。但是这种最原始的播放器完全不能满足我们开发中的需求。特别是videoView自带的播放控制器只有播放暂停,快进快退的功能,像拖拽进度,横屏竖屏,手势等功能都没有。而且原始的videoView的大小我们还不能随意的修改。所以我们自定义了videoView,自定义了controller,从而优化功能,达到Android开发的标准,博客记录一下。

先看一张最后的效果图:



2.实现思路

自定义一个view继承framLayout,自己写一个布局通过LayoutInflate注入进来,布局的代码会在下面体现。总体布局就是头部有返回按钮,视频标题,中间是自定义的videoView,还有加载缩略图的ImageView,底部布局有播放暂停的按钮,有当前播放时间/总时间,有可以改变播放进度的seekBar,还有一个可以控制横竖屏的按钮。缩略图中间有一个开始播放的按钮。点击后加载loading动画,当player prepared监听中隐藏动画。当开始按钮点击后,将top bottom显示出来,然后点击videoView可以控制top and bottom的显示隐藏。点击bottom的开始暂停按钮可以控制视频的播放暂停。通过handler定义一个定时器每0.5秒获取一下videoView当前的播放位置,计算seekbar的进度,更新进度条。监听seekbar的进度改变事件,通过videoView的seekTo方法改变videoView的播放进度。横竖屏的按钮控制全屏播放,因为系统的videoView不允许随便修改大小,所以我们自定义videoView并重写onMeasure方法。好了,说了这么多看一下核心代码吧:

1.自定义的videoview,可以随意设置宽高

public class MyVideoView extends VideoView {
    public MyVideoView(Context context) {
        super(context);
    }

    public MyVideoView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyVideoView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = getDefaultSize(getWidth(), widthMeasureSpec);
        int height = getDefaultSize(getHeight(), heightMeasureSpec);
        setMeasuredDimension(width, height);
    }
}
2.自定义的播放控制器

public class MyController extends FrameLayout implements View.OnClickListener, MediaPlayer.OnPreparedListener, MediaPlayer.OnInfoListener, SeekBar.OnSeekBarChangeListener, MediaPlayer.OnCompletionListener {
    private Context mContext;

    private String url;
    private FrameLayout mRoot_rl;
    private MyVideoView videoView;
    private FrameLayout fl_videoParent;
    private ImageView mImage;
    private ImageView mCenterStart;
    private LinearLayout mTop;
    private ImageView mBack;
    private TextView mTitle;
    private LinearLayout mBottom;
    private ImageView mRestartPause;
    private TextView mPosition;
    private TextView mDuration;
    private SeekBar mSeek;
    private ImageView mFullScreen;
    private ImageView iv_loading;

    //播放控件的动画
    private ObjectAnimator bufferAnimation;

    private boolean isTopBottomShow = false;
    //更新进度条进度定时器
    private Handler mHandler = new Handler();
    private Runnable mRunnable;
    //当前视屏的播放进度
    private int currentProgress;

    public MyController(Context context) {
        super(context);
        this.mContext = context;
        init(context);
    }

    public MyController(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;
        init(context);
    }

    public MyController(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        init(context);
    }

    private void init(Context context) {
        View view = LayoutInflater.from(mContext).inflate(R.layout.layout_controller, null);
        fl_videoParent = (FrameLayout)view.findViewById(R.id.fl_videoParent);
        videoView = (MyVideoView) view.findViewById(R.id.videoView);
        mRoot_rl = (FrameLayout) view.findViewById(R.id.root_rl);
        mCenterStart = (ImageView) view.findViewById(R.id.center_start);
        mImage = (ImageView) view.findViewById(R.id.image);

        mTop = (LinearLayout) view.findViewById(R.id.top);
        mBack = (ImageView) view.findViewById(R.id.back);
        mTitle = (TextView) view.findViewById(R.id.title);

        mBottom = (LinearLayout) view.findViewById(R.id.bottom);
        mRestartPause = (ImageView) view.findViewById(R.id.restart_or_pause);
        mPosition = (TextView) view.findViewById(R.id.position);
        mDuration = (TextView) view.findViewById(R.id.duration);
        mSeek = (SeekBar) view.findViewById(R.id.seek);
        mFullScreen = (ImageView) view.findViewById(R.id.full_screen);
        iv_loading = view.findViewById(R.id.iv_loading);
        addView(view);
    }

    /**
     * 播放器初始化
     *
     * @param url
     * @param thumbUrl
     */
    public void initVideo(String url, String thumbUrl,String title) {
        this.url = url;
        videoView.setVideoPath(url);
        mTitle.setText(title);
        LayoutParams lp = (LayoutParams) videoView.getLayoutParams();
        lp.width = (int) Util.getScreenWidth(mContext);
        lp.height = lp.width * 9 / 16;
        videoView.setLayoutParams(lp);

        LayoutParams lp2 = (LayoutParams) mImage.getLayoutParams();
        lp2.width = (int) Util.getScreenWidth(mContext);
        lp2.height = lp2.width * 9 / 16;
        mImage.setLayoutParams(lp2);

        mTop.setVisibility(GONE);
        mBottom.setVisibility(GONE);

        Glide.with(mContext).load(thumbUrl).into(mImage);
        initListener();

        mRunnable = new Runnable() {
            @Override
            public void run() {
                float position = videoView.getCurrentPosition();
                currentProgress = (int) ((position / videoView.getDuration()) * 100);
                if(!isPlayOver){
                    mSeek.setProgress(currentProgress);
                }
                mPosition.setText(stringForTime((int) position));
                mHandler.postDelayed(mRunnable, 500);
            }
        };
    }

    /**
     * 监听初始化
     */
    private void initListener() {
        mCenterStart.setOnClickListener(this);
        fl_videoParent.setOnClickListener(this);
        mRestartPause.setOnClickListener(this);
        mFullScreen.setOnClickListener(this);
        mBack.setOnClickListener(this);

        videoView.setOnPreparedListener(this);
        videoView.setOnInfoListener(this);
        videoView.setOnCompletionListener(this);
        mSeek.setOnSeekBarChangeListener(this);
    }


    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.center_start:
                startPlay();
                if(url.startsWith("http") || url.startsWith("https")){
                    iv_loading.setVisibility(VISIBLE);
                }else {
                    iv_loading.setVisibility(GONE);
                }
                startBufferAnimation();
                break;
            case R.id.fl_videoParent:
                if(videoView.isPlaying()){
                    showHideTopAndBottom();
                }
                break;
            case R.id.restart_or_pause:
                if(videoView != null){
                    if(videoView.isPlaying()){
                        videoView.pause();
                        mRestartPause.setImageResource(R.drawable.ic_player_start);
                    }else {
                        startPlay();
                        mRestartPause.setImageResource(R.drawable.ic_player_pause);
                    }
                }
                break;
            case R.id.full_screen:
                switchScreenOrientation();
                break;
            case R.id.back:
                switchScreenOrientation();
                break;
        }
    }

    private void startPlay(){

        mRestartPause.setImageResource(R.drawable.ic_player_pause);
        videoView.start();
        mImage.setVisibility(GONE);
        mCenterStart.setVisibility(GONE);
    }

    /**
     * videoView准备完毕监听
     *
     * @param mediaPlayer
     */
    @Override
    public void onPrepared(MediaPlayer mediaPlayer) {
        stopBufferAnimation();
        iv_loading.setVisibility(GONE);

        mDuration.setText(stringForTime(videoView.getDuration()));
        mHandler.post(mRunnable);
    }

    /**
     * 缓冲标志动画
     */
    private void startBufferAnimation() {
        if (bufferAnimation == null) {
            bufferAnimation = ObjectAnimator.ofFloat(iv_loading, "rotation", 0, 359);
            bufferAnimation.setDuration(1500);
            bufferAnimation.setInterpolator(new LinearInterpolator());
            bufferAnimation.setRepeatCount(ObjectAnimator.INFINITE);
        }
        if (!bufferAnimation.isRunning()) {
            bufferAnimation.start();
        }
    }

    private void stopBufferAnimation() {
        if (bufferAnimation != null) {
            bufferAnimation.cancel();
        }
    }

    /**
     * 头部,底部隐藏显示
     */
    private void showHideTopAndBottom() {
        if (isTopBottomShow) {
            mTop.setVisibility(GONE);
            mBottom.setVisibility(GONE);
            isTopBottomShow = false;
        } else {
            mTop.setVisibility(VISIBLE);
            mBottom.setVisibility(VISIBLE);
            isTopBottomShow = true;
        }
    }

    @Override
    public boolean onInfo(MediaPlayer mediaPlayer, int what, int extra) {
        if(what == MediaPlayer.MEDIA_INFO_BUFFERING_START){//开始缓冲
            iv_loading.setVisibility(VISIBLE);
            startBufferAnimation();
        }else if(what == MediaPlayer.MEDIA_INFO_BUFFERING_END){//缓冲结束
            iv_loading.setVisibility(GONE);
            stopBufferAnimation();
        }
        return false;
    }

    private String stringForTime(int timeMs) {
        if (timeMs <= 0 || timeMs >= 24 * 60 * 60 * 1000) {
            return "00:00";
        }
        int totalSeconds = timeMs / 1000;
        int seconds = totalSeconds % 60;
        int minutes = (totalSeconds / 60) % 60;
        int hours = totalSeconds / 3600;
        StringBuilder stringBuilder = new StringBuilder();
        Formatter mFormatter = new Formatter(stringBuilder, Locale.getDefault());
        if (hours > 0) {
            return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString();
        } else {
            return mFormatter.format("%02d:%02d", minutes, seconds).toString();
        }
    }

    /**
     * seekBar进度改变实现方法
     * @param seekBar
     * @param i
     * @param b
     */
    @Override
    public void onProgressChanged(SeekBar seekBar, int i, boolean b) {

    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {

    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        //滑动SeekBar结束后让视屏定位到对应位置
        if (videoView != null) {
            int position = (int) ((seekBar.getProgress() * 1.0f /
                    seekBar.getMax()) * videoView.getDuration());
            videoView.seekTo(position);
        }
    }

    /**
     * 视频播放结束监听
     * @param mediaPlayer
     */
    private boolean isPlayOver = false;

    @Override
    public void onCompletion(MediaPlayer mediaPlayer) {
        mImage.setVisibility(VISIBLE);
        mCenterStart.setVisibility(VISIBLE);
        mSeek.setProgress(0);

        mTop.setVisibility(VISIBLE);
        mBottom.setVisibility(VISIBLE);
        isTopBottomShow = true;
        isPlayOver = true;
    }

    /**
     * 设置全屏
     */
    public boolean fullscreen = false;
    public void switchScreenOrientation() {
        Activity activity = (Activity) getContext();
        LayoutParams lp = (LayoutParams) videoView.getLayoutParams();

        if (fullscreen) {//全屏,也就是横屏
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            fullscreen = false;

            mBack.setVisibility(VISIBLE);
            lp.width = (int) Util.getScreenWidth(mContext);
            lp.height = (int) Util.getScreenHeight(mContext);
            videoView.setLayoutParams(lp);
            mFullScreen.setImageResource(R.drawable.ic_player_shrink);
        } else {//不是全屏,也就是竖屏
            activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            fullscreen = true;

            mBack.setVisibility(GONE);
            lp.width = (int) Util.getScreenWidth(mContext);
            lp.height = lp.width * 9 / 16;
            videoView.setLayoutParams(lp);
            mFullScreen.setImageResource(R.drawable.ic_player_enlarge);
        }
    }

    /**
     * 停止播放
     */
    public void onStop(){
        isPlayOver = false;
        mHandler.removeCallbacksAndMessages(null);
        videoView.stopPlayback();
        videoView = null;
    }

    /**
     * 返回键点击事件
     */
    public boolean onBackPressed(){
        if(!fullscreen){
            switchScreenOrientation();
            return true;
        }else{
            return false;
        }
    }
}
当返回键点击的时候回去判断这时候是不是全屏状态,如果是全屏状态就先退出全屏,如果不是就直接退出。好了,核心代码就是这些了。需要的朋友可以下载源码。

最后贴一下源码地址:https://gitee.com/fireqiang/OwnDefineVideoView.git




  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Android VideoView 是一个可以用来播放视频的视图控件,但是有时候会遇到无法播放某些视频的情况。 造成 Android VideoView 无法播放视频的原因有很多,下面是一些可能的原因: 1. 文件格式不受支持:Android VideoView支持播放的视频格式有限,例如常见的MP4、3GP等格式,但是某些特殊格式如MKV、FLV等可能无法播放。 2. 编码方式不受支持:即使是支持的文件格式,如果视频使用了不受支持的编码方式,也无法播放Android支持的编码方式包括H.264、H.263等。 3. 视频文件损坏:如果视频文件本身损坏或者缺失必要的编码信息,也会导致无法播放。 4. 网络问题:如果播放的视频来自网络,可能是网络连接问题导致无法播放。例如服务器问题、网络限速等。 解决这个问题的方法有几种: 1. 转码视频:将不受支持的视频格式或编码方式转换为支持的格式,可以使用一些视频转换工具来完成。 2. 使用其他视频播放器:如果VideoView无法播放某个视频,可以尝试其他的视频播放器,如ExoPlayer、VLC等。 3. 检查视频文件:确保视频文件没有损坏,尝试播放其他已知可以正常播放的视频文件来检查。 4. 检查网络连接:如果视频来自网络,确保网络连接正常,并尝试使用其他网络或播放其他视频来排除网络问题。 总之,如果Android VideoView无法播放某个视频,我们需要先检查视频文件格式、编码方式、文件完整性以及网络连接等方面的问题,然后采取相应的解决方法来解决这个问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值