自定义VideoView或MediaPlayer的MediaController播放样式

项目中需要做一个视频播放模块,大体是用的七牛播放器android端SDK+SurfaceView+自定义MediaController。这里就只介绍自己写的mediaController。下面正式开始。
三步走:重点内容
1. 定义MediaControllerInterface接口
2. 自定义controller类,实现MediaControllerInterface接口
3. 创建播放界面实现MediaControllerInterface.MediaControl接口

第一步:先上代码:


public interface MediaControllerInterface {

    void show();//显示

    void show(int time);//显示

    void hide();//隐藏

    boolean isShowing();//是否显示

    void setCollected(boolean isCollected);//是否被收藏

    void setSeekBarEnabled(boolean var1);

    void isCompleted();//设置已经播放完成了

    void To_change_screen(int w,int h);//改变宽高


    interface MediaControl {
        void VideoStart();//开始播放

        void VideoResume();//继续播放

        void VideoPause();//暂停

        void VideoStop();//停止

        long getDuration();//视频时长

        long getCurrentPosition();//当前播放进度

        void seekTo(long var1);//拖动进度

        boolean isPlaying();//是否正在播放

        boolean isPausing();//是否是暂停状态

        boolean isFullScreen();//判断是否是全屏状态

        void toCollect();//收藏按钮

        void actionForFullScreen();//收藏按钮
    }
}

这个MediaControllerInterface 接口类主要是为了让controller类实现,与实现了MediaControllerInterface.MediaControl的播放界面类,进行回调操作。
重点在MediaControl接口,需要播放界面实现当中的方法,与controller上面的组件进行绑定。

第二步:部分代码如下:
初始化代码:

//初始化
    private View initView() {
        View view = LayoutInflater.from(mContext).inflate(R.layout.view_controller, null);

        ll_top = (LinearLayout) view.findViewById(R.id.ll_top);
        iv_collect = (ImageView) view.findViewById(R.id.iv_collect);
        iv_center_play = (ImageView) view.findViewById(R.id.iv_center_play);
        ll_bottom = (LinearLayout) view.findViewById(R.id.ll_bottom);
        iv_play = (ImageView) view.findViewById(R.id.iv_play);
        tv_watched = (TextView) view.findViewById(R.id.tv_watched);
        sb_progress = (SeekBar) view.findViewById(R.id.sb_progress);
        tv_total = (TextView) view.findViewById(R.id.tv_total);
        iv_fullscreen = (ImageView) view.findViewById(R.id.iv_fullscreen);

        iv_collect.setOnClickListener(mCollectListener);//收藏监听
        iv_center_play.setOnClickListener(mPlayListener);//播放按钮的监听
        iv_play.setOnClickListener(mPlayListener);//播放按钮的监听
        iv_fullscreen.setOnClickListener(mFullListener);//全屏按钮的监听

        sb_progress.setMax((int) SEEKBAR_MAX);
        //进度条拖动的监听
        sb_progress.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {

            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                mPlayer.seekTo(mDuration * seekBar.getProgress() / SEEKBAR_MAX);
            }
        });
        return view;
    }

与播放器的绑定:

//添加控制绑定
    public void setControl(MediaControllerInterface.MediaControl mPlayer) {
        this.mPlayer = mPlayer;
    }

    public void setAnchorView(ViewGroup view) {
        mAnchorVGroup = view;
        FrameLayout.LayoutParams frameParams = new FrameLayout.LayoutParams(
                mAnchorVGroup.getWidth(),
                mAnchorVGroup.getHeight()
        );
        removeAllViews();

        controllerView = initView();
        mAnchorVGroup.addView(controllerView, frameParams);
    }

controller触摸事件的获取:目的是为了防止用户操作焦点在controller上时,controller不会消失。

//当触摸事件处于控制器上的时候,显示控制器,不让其消失
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        show(sDefaultTimeout);
        return false;
    }

    //当触摸事件处于控制器上的时候,显示控制器,不让其消失
    @Override
    public boolean onTrackballEvent(MotionEvent ev) {
        show(sDefaultTimeout);
        return false;
    }

    //当触摸事件处于控制器上的时候,显示控制器,不让其消失
    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        int keyCode = event.getKeyCode();
        if (event.getRepeatCount() == 0
                && (keyCode == KeyEvent.KEYCODE_HEADSETHOOK
                || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE || keyCode == KeyEvent.KEYCODE_SPACE)) {
            show(sDefaultTimeout);
            if (iv_play != null)
                iv_play.requestFocus();
            return true;
        } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP) {
            if (mPlayer.isPlaying()) {
                mPlayer.VideoPause();
            }
            return true;
        } else if (keyCode == KeyEvent.KEYCODE_BACK
                || keyCode == KeyEvent.KEYCODE_MENU) {
            hide();
            return true;
        } else {
            show(sDefaultTimeout);
        }
        return super.dispatchKeyEvent(event);
    }

控制器的显示和隐藏

@SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            long progress;
            switch (msg.what) {
                case FADE_OUT:
                    hide();
                    break;
                case SHOW_PROGRESS:
                    if (mPlayer.isPlaying()) {
                        progress = setProgress();
                    } else {
                        return;
                    }

                    if (mShowing && mPlayer.isPlaying()) {
                        msg = obtainMessage(SHOW_PROGRESS);
                        sendMessageDelayed(msg, SEEKBAR_MAX - (progress % SEEKBAR_MAX));
                    }
                    break;
            }
        }
    };

    private long setProgress() {
        if (mPlayer == null) {
            return 0;
        }

        long position = mPlayer.getCurrentPosition();
        long duration = mPlayer.getDuration();
        if (sb_progress != null) {
            if (duration > 0) {
                long pos = SEEKBAR_MAX * position / duration;
                sb_progress.setProgress((int) pos);
            }
//            int percent = mPlayer.getBufferPercentage();//得到当前缓存进度
//            sb_progress.setSecondaryProgress(percent * 10);//设置缓存进度
        }

        mDuration = duration;

        if (tv_total != null)
            tv_total.setText(generateTime(mDuration));
        if (tv_watched != null)
            tv_watched.setText(generateTime(position));

        return position;
    }

    //long数据格式化
    private static String generateTime(long position) {
        int totalSeconds = (int) (position / 1000);

        int seconds = totalSeconds % 60;
        int minutes = (totalSeconds / 60) % 60;
        int hours = totalSeconds / 3600;

        if (hours > 0) {
            return String.format(Locale.US, "%02d:%02d:%02d", hours, minutes,
                    seconds).toString();
        } else {
            return String.format(Locale.US, "%02d:%02d", minutes, seconds)
                    .toString();
        }
    }

    @Override
    public void show() {
        show(sDefaultTimeout);
    }

    //显示控制器
    @Override
    public void show(int time) {
        if (!mShowing && mAnchorVGroup != null) {
            if (mPlayer.isFullScreen()) {//是全屏,显示上下
                showTopAndBottom();
            } else {//显示底部
                showBottom();
            }
        }

        if (mPlayer.isPlaying()) {
            iv_play.setImageResource(R.mipmap.k_stop);
        } else {
            iv_play.setImageResource(R.mipmap.k_play);
        }

        mHandler.sendEmptyMessage(SHOW_PROGRESS);

        mShowing = true;
        if (time != 0) {
            mHandler.removeMessages(FADE_OUT);
            mHandler.sendMessageDelayed(mHandler.obtainMessage(FADE_OUT),
                    time);
        }
    }

    //隐藏控制器
    @Override
    public void hide() {
        if (mAnchorVGroup == null) {
            return;
        }
        try {
            if (mPlayer.isPausing()) {//正处于暂停状态,隐藏上下,中间按钮不隐藏
                hideAll();
            } else {
                hideTopAndBottom();
            }
            if (mHandler != null) {
                mHandler.removeMessages(SHOW_PROGRESS);
            }
        } catch (IllegalArgumentException ex) {
            Log.w("MediaController", "already removed");
        }
        mShowing = false;
    }

第三步:播放页面实现MediaControllerInterface.MediaControl接口,代码如下:

public class MainAcivity extends AppCompatActivity {
    private static final String TAG = "MainAcivity";

    private SurfaceView mSurfaceView;
    private PLMediaPlayer mMediaPlayer;
    private AVOptions mAVOptions;

    private int mSurfaceWidth = 0;
    private int mSurfaceHeight = 0;

    private String mVideoPath = null;

    private long mLastUpdateStatTime = 0;
    private String is_over = "0";
    private int sv_height;//记录非全屏状态时,surfaceView的高度,以便退出全屏时,设置回来
    boolean firstRendering = true;//视频第一次渲染,在OnInfoListener 中改变值,保证恢复到先前播放的进度
    private MyController controller;
    private FrameLayout fl_surfaceview_parent;
    private boolean seekbarDrag = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setContentView(R.layout.module_activity_video_detail);
        super.onCreate(savedInstanceState);

        boolean isLiveStreaming = getIntent().getIntExtra("liveStreaming", 0) == 1;

        //播放地址
        mVideoPath = "";

        mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView);
        fl_surfaceview_parent = (FrameLayout) findViewById(R.id.fl_surfaceview_parent);
        mSurfaceView.getHolder().addCallback(mCallback);
        mSurfaceWidth = getResources().getDisplayMetrics().widthPixels;
        mSurfaceHeight = getResources().getDisplayMetrics().heightPixels;

        mAVOptions = new AVOptions();
        mAVOptions.setInteger(AVOptions.KEY_PREPARE_TIMEOUT, 10 * 1000);
        int codec = getIntent().getIntExtra("mediaCodec", AVOptions.MEDIA_CODEC_SW_DECODE);
        mAVOptions.setInteger(AVOptions.KEY_MEDIACODEC, codec);
        mAVOptions.setInteger(AVOptions.KEY_LIVE_STREAMING, isLiveStreaming ? 1 : 0);

        AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        audioManager.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);

        controller = new MyController(this);

        ViewTreeObserver vto2 = mSurfaceView.getViewTreeObserver();
        vto2.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                mSurfaceView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                sv_height = mSurfaceView.getHeight();
                mSurfaceView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, sv_height));
            }
        });

        mSurfaceView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                controller.show();
            }
        });
    }

    private boolean isPausing = false;//是否是暂停状态
    private boolean isFullScreen = false;//是否是全屏状态
    private MediaControllerInterface.MediaControl mPlayerControl = new MediaControllerInterface.MediaControl() {
        @Override
        public void VideoStart() {
            mMediaPlayer.start();
        }

        @Override
        public void VideoResume() {
            mMediaPlayer.start();
        }

        @Override
        public void VideoPause() {
            mMediaPlayer.pause();
        }

        @Override
        public void VideoStop() {
            mMediaPlayer.stop();
        }

        @Override
        public long getDuration() {
            return mMediaPlayer.getDuration();
        }

        @Override
        public long getCurrentPosition() {
            return mMediaPlayer.getCurrentPosition();
        }

        @Override
        public void seekTo(long var1) {
            mMediaPlayer.seekTo(var1);
        }

        @Override
        public boolean isPlaying() {
            return mMediaPlayer.isPlaying();
        }

        @Override
        public boolean isPausing() {
            return isPausing;
        }

        @Override
        public boolean isFullScreen() {
            return isFullScreen;
        }

        @Override
        public void toCollect() {
        }

        @Override
        public void actionForFullScreen() {
            if (isFullScreen) {
                //退出全屏
                Exit_full_screen();
            } else {
                To_full_screen();
            }
        }
    };

    //进入全屏的操作
    public void To_full_screen() {
        isFullScreen = true;
        fl_surfaceview_parent.setLayoutParams(new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.MATCH_PARENT));
        mSurfaceView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT));
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);//设置activity横屏
        getWindow().getDecorView().setSystemUiVisibility(View.INVISIBLE);//隐藏状态栏
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
        Log.i("tag", "1--" + fl_surfaceview_parent.getMeasuredWidth() + "---" + fl_surfaceview_parent.getMeasuredHeight());
        controller.To_change_screen(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
    }

    //退出全屏的操作
    public void Exit_full_screen() {
        isFullScreen = false;
        fl_surfaceview_parent.setLayoutParams(new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                sv_height));
        mSurfaceView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, sv_height));
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//设置activity竖屏
        getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);//显示状态栏
        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
        Log.i("tag", "2--" + fl_surfaceview_parent.getWidth() + "---" + fl_surfaceview_parent.getHeight());
        controller.To_change_screen(FrameLayout.LayoutParams.MATCH_PARENT, sv_height);
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        release();
        controller.release();
        AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        audioManager.abandonAudioFocus(null);
    }

    @Override
    protected void onResume() {
        super.onResume();
    }


    public void releaseWithoutStop() {
        if (mMediaPlayer != null) {
            mMediaPlayer.setDisplay(null);
        }
    }

    public void release() {
        if (mMediaPlayer != null) {
            mMediaPlayer.stop();
            mMediaPlayer.release();
            mMediaPlayer = null;
        }
    }

    //播放准备
    private void prepare() {
        if (mMediaPlayer != null) {
            mMediaPlayer.setDisplay(mSurfaceView.getHolder());
            return;
        }

        try {
            if (mMediaPlayer == null) {
                mMediaPlayer = new PLMediaPlayer(this, mAVOptions);
            }
            mMediaPlayer.setDebugLoggingEnabled(true);
            mMediaPlayer.setOnPreparedListener(mOnPreparedListener);
            mMediaPlayer.setOnVideoSizeChangedListener(mOnVideoSizeChangedListener);
            mMediaPlayer.setOnCompletionListener(mOnCompletionListener);
            mMediaPlayer.setOnErrorListener(mOnErrorListener);
            mMediaPlayer.setOnInfoListener(mOnInfoListener);
            mMediaPlayer.setOnBufferingUpdateListener(mOnBufferingUpdateListener);
//             mMediaPlayer.setLooping(true);//循环播放
            mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
            mMediaPlayer.setDataSource(mVideoPath);
            mMediaPlayer.setDisplay(mSurfaceView.getHolder());
            mMediaPlayer.prepareAsync();
        } catch (UnsatisfiedLinkError e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //surfaceview回调函数
    private SurfaceHolder.Callback mCallback = new SurfaceHolder.Callback() {

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            prepare();
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            // release();
            releaseWithoutStop();
        }
    };

    //视频大小改变的监听
    private PLMediaPlayer.OnVideoSizeChangedListener mOnVideoSizeChangedListener = new PLMediaPlayer.OnVideoSizeChangedListener() {
        public void onVideoSizeChanged(PLMediaPlayer mp, int width, int height) {
            // resize the display window to fit the screen
            if (width != 0 && height != 0) {
                float ratioW = (float) width / (float) mSurfaceWidth;
                float ratioH = (float) height / (float) mSurfaceHeight;
                float ratio = Math.max(ratioW, ratioH);
                width = (int) Math.ceil((float) width / ratio);
                height = (int) Math.ceil((float) height / ratio);
                FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(width, height);
                layout.gravity = Gravity.CENTER;
                mSurfaceView.setLayoutParams(layout);
            }
        }
    };


    //mediaplayer准备好监听
    private PLMediaPlayer.OnPreparedListener mOnPreparedListener = new PLMediaPlayer.OnPreparedListener() {
        @Override
        public void onPrepared(PLMediaPlayer mp, int preparedTime) {
            controller.setControl(mPlayerControl);
            controller.setAnchorView(fl_surfaceview_parent);
            controller.setSeekBarEnabled(false);
            mMediaPlayer.start();
        }
    };

    /**
     * 转换播放时间
     *
     * @param milliseconds 传入毫秒值
     * @return 返回 hh:mm:ss或mm:ss格式的数据
     */
    @SuppressLint("SimpleDateFormat")
    public String getShowTime(long milliseconds) {
        // 获取日历函数
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(milliseconds);
        SimpleDateFormat dateFormat = null;
        // 判断是否大于60分钟,如果大于就显示小时。设置日期格式
        if (milliseconds / 60000 > 60) {
            dateFormat = new SimpleDateFormat("hh:mm:ss");
        } else {
            dateFormat = new SimpleDateFormat("mm:ss");
        }
        return dateFormat.format(calendar.getTime());
    }

    //视频播放状态监听
    private PLMediaPlayer.OnInfoListener mOnInfoListener = new PLMediaPlayer.OnInfoListener() {
        @Override
        public boolean onInfo(PLMediaPlayer mp, int what, int extra) {
            switch (what) {
                case PLMediaPlayer.MEDIA_INFO_BUFFERING_START://第一帧视频已成功渲染
                    break;
                case PLMediaPlayer.MEDIA_INFO_BUFFERING_END:
                    break;
                case PLMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START:
//                    showToastTips("first video render time: " + extra + "ms");
                    if (firstRendering && "0".equals(is_over)) {//第一次渲染,并且是未看过
                        firstRendering = false;
                    }
                    break;
                case PLMediaPlayer.MEDIA_INFO_VIDEO_GOP_TIME:
                    break;
                case PLMediaPlayer.MEDIA_INFO_AUDIO_RENDERING_START:
                    break;
                case PLMediaPlayer.MEDIA_INFO_SWITCHING_SW_DECODE:
                    break;
                case PLMediaPlayer.MEDIA_INFO_METADATA:
                    break;
                case PLMediaPlayer.MEDIA_INFO_VIDEO_BITRATE:
                case PLMediaPlayer.MEDIA_INFO_VIDEO_FPS:
//                    updateStatInfo();
                    break;
                case PLMediaPlayer.MEDIA_INFO_CONNECTED:
                    break;
                default:
                    break;
            }
            return true;
        }
    };

    //视频缓存监听
    private PLMediaPlayer.OnBufferingUpdateListener mOnBufferingUpdateListener = new PLMediaPlayer.OnBufferingUpdateListener() {
        @Override
        public void onBufferingUpdate(PLMediaPlayer mp, int percent) {
            long current = System.currentTimeMillis();
            if (current - mLastUpdateStatTime > 3000) {
                mLastUpdateStatTime = current;
            }
        }
    };

    private boolean onComplet = false;//是否已经播放完一个视频
    //视频播放完成监听
    private PLMediaPlayer.OnCompletionListener mOnCompletionListener = new PLMediaPlayer.OnCompletionListener() {
        @Override
        public void onCompletion(PLMediaPlayer mp) {
            // 设置播放标记为false
            controller.isCompleted();
        }
    };


    private PLMediaPlayer.OnErrorListener mOnErrorListener = new PLMediaPlayer.OnErrorListener() {
        @Override
        public boolean onError(PLMediaPlayer mp, int errorCode) {
            switch (errorCode) {
                case PLMediaPlayer.ERROR_CODE_IO_ERROR://网络异常
                    /**
                     * SDK will do reconnecting automatically
                     */
                    //如果网络异常播放失败,尝试重新连接
                    reload();
                    return false;
                case PLMediaPlayer.ERROR_CODE_OPEN_FAILED://播放器打开失败
                    //重新打开播放器
                    release();//先关闭先前的播放器
                    prepare();
                    break;
                case PLMediaPlayer.ERROR_CODE_SEEK_FAILED://seek拖动失败
                    break;
                case PLMediaPlayer.ERROR_CODE_PLAYER_DESTROYED://播放器已被销毁,需要再次 setVideoURL 或 prepareAsync
                    //重新打开播放器
                    release();//先关闭先前的播放器
                    prepare();
                    break;
                default://未知错误
                    break;
            }
            return true;
        }
    };

    //如果网络异常播放失败,尝试重新连接
    public void reload() {
        release();//先关闭先前的播放器
        //判断网络连接
        prepare();
    }
}

在播放页面主要实现了MediaControl接口,创建了一个controller对象,让界面上的操作得以体现到controller对象上,并进行相关操作,如show(),hide()等。。。

注:运行demo时,需要先设置一下mVideoPath ,将视频地址设置好,再进播放

demo下载地址为:https://github.com/midux001/mediacontroller

你可能感兴趣的:(android)