自定义 视频播放器 SurfaceView +MediaPlayer

直接上代码:

自定义surfaceview

package com.blue.mvvm.media.view;

import android.content.Context;
import android.util.AttributeSet;
import android.view.SurfaceView;

public class VideoSurfaceView extends SurfaceView {

    // 视频宽度
    private int videoWidth;
    // 视频高度
    private int videoHeight;

    public VideoSurfaceView(Context context) {
        this(context, null);
    }

    public VideoSurfaceView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public VideoSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        videoWidth = 0;
        videoHeight = 0;
        setFocusable(true);
        setFocusableInTouchMode(true);
        requestFocus();
    }

    /**
     * 根据视频的宽高设置SurfaceView的宽高
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = getDefaultSize(videoWidth, widthMeasureSpec);
        int height = getDefaultSize(videoHeight, heightMeasureSpec);
        if (videoWidth > 0 && videoHeight > 0) {
            // 获取测量模式和测量大小
            int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
            int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
            int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
            int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
            // 分情况设置大小
            if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.EXACTLY) {
                // layout_width = 确定值或match_parent
                // layout_height = 确定值或match_parent
                // the size is fixed
                width = widthSpecSize;
                height = heightSpecSize;
                // 做适配,不让视频拉伸,保持原来宽高的比例
                // for compatibility, we adjust size based on aspect ratio
                if ( videoWidth * height  < width * videoHeight) {
                    //Log.i("@@@", "image too wide, correcting");
                    width = height * videoWidth / videoHeight;
                } else if ( videoWidth * height  > width * videoHeight) {
                    //Log.i("@@@", "image too tall, correcting");
                    height = width * videoHeight / videoWidth;
                }
            } else if (widthSpecMode == MeasureSpec.EXACTLY) {
                // layout_width = 确定值或match_parent
                // layout_height = wrap_content
                // only the width is fixed, adjust the height to match aspect ratio if possible
                width = widthSpecSize;
                // 计算高多少,保持原来宽高的比例
                height = width * videoHeight / videoWidth;
                if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
                    // couldn't match aspect ratio within the constraints
                    height = heightSpecSize;
                }
            } else if (heightSpecMode == MeasureSpec.EXACTLY) {
                // layout_width = wrap_content
                // layout_height = 确定值或match_parent
                // only the height is fixed, adjust the width to match aspect ratio if possible
                height = heightSpecSize;
                // 计算宽多少,保持原来宽高的比例
                width = height * videoWidth / videoHeight;
                if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
                    // couldn't match aspect ratio within the constraints
                    width = widthSpecSize;
                }
            } else {
                // layout_width = wrap_content
                // layout_height = wrap_content
                // neither the width nor the height are fixed, try to use actual video size
                width = videoWidth;
                height = videoHeight;
                if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
                    // too tall, decrease both width and height
                    height = heightSpecSize;
                    width = height * videoWidth / videoHeight;
                }
                if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
                    // too wide, decrease both width and height
                    width = widthSpecSize;
                    height = width * videoHeight / videoWidth;
                }
            }
        } else {
            // no size yet, just adopt the given spec sizes
        }
        // 设置SurfaceView的宽高
        setMeasuredDimension(width, height);
    }

    /**
     * 调整大小
     * @param videoWidth
     * @param videoHeight
     */
    public void adjustSize(int videoWidth, int videoHeight) {
        if (videoWidth == 0 || videoHeight == 0) return;
        // 赋值自己的宽高
        this.videoWidth = videoWidth;
        this.videoHeight = videoHeight;
        // 设置Holder固定的大小
        getHolder().setFixedSize(videoWidth, videoHeight);
        // 重新设置自己的大小
        requestLayout();
    }

}

 

自定义视频  VideoSurfaceView  +MediaPlayer

package com.blue.mvvm.media;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Outline;
import android.graphics.Rect;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import com.blue.mvvm.R;
import com.blue.mvvm.media.view.VideoSurfaceView;
import com.blue.mvvm.utils.LogUtil;
import com.bumptech.glide.Glide;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;

/**
 * 自定义  视频播放器
 */
public class VideoPlay extends LinearLayout {
    private String TAG=VideoPlay.class.getSimpleName();
    private Activity mactivity;
    private ImageView playOrPauseIv, previewIv;  //中间播放按钮
    private ImageView playOrPause2, playFull;
    private SeekBar mSeekBar;//进度条
    private TextView startTime, endTime; //开始时间   结束时间
    private VideoSurfaceView surfaceView; //
    private RelativeLayout rootViewRl;
    private LinearLayout controlLl;
    private MediaPlayer mediaPlayer;
    private boolean isShow = false;
    private int mProgress;
    public static final int UPDATE_TIME = 0x0001;
    public static final int HIDE_CONTROL = 0x0002;
    private VideoPlayListeren mvideoPlayListeren;
    private VideoPlayFullListeren mvideoPlayFullListeren;
    private Uri mUri;

    public VideoPlay(Context context) {
        super(context);
        this.mactivity = (Activity) context;
        initViews();
    }

    public VideoPlay(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mactivity = (Activity) context;
        initViews();
    }

    public VideoPlay(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mactivity = (Activity) context;
        initViews();
    }

    @SuppressLint("NewApi")
    public VideoPlay(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        this.mactivity = (Activity) context;
        initViews();
    }

    /**
     * 初始化View
     */
    private void initViews() {
        View view = LayoutInflater.from(getContext()).inflate(R.layout.layout_video_play, null, false);
        addView(view);
        playOrPauseIv = findViewById(R.id.video_play_playOrPause);
        previewIv = findViewById(R.id.video_play_previewImage);
        playOrPause2 = findViewById(R.id.video_play_playOrPause2);
        startTime = findViewById(R.id.video_play_start_time);
        endTime = findViewById(R.id.video_play_end_time);
        mSeekBar = findViewById(R.id.video_play_progess);
        controlLl = findViewById(R.id.video_play_control_ll);
        rootViewRl = findViewById(R.id.video_play_root_rl);
        playFull = findViewById(R.id.video_play_full);
        initSurfaceView();
        initMediaPlayer();
        initEvent();
    }

    /**
     * 初始化SurfaceView
     */
    private void initSurfaceView() {
        surfaceView = findViewById(R.id.video_play_surfaceView);
        //设置视频在顶部false
        surfaceView.setZOrderOnTop(false);
        //设置视频圆角   api>=21   也就是5.0  及以上
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            surfaceView.setOutlineProvider(new RadiusViewOutlineProvider(10f));
            surfaceView.setClipToOutline(true);
        }
        surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder surfaceHolder) {
                mediaPlayer.setDisplay(surfaceHolder);
            }

            @Override
            public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {


            }

            @Override
            public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
                if (mediaPlayer != null) {
                    if (mediaPlayer.isPlaying()) {
                        mediaPlayer.stop();
                    }
                } else {
                    return;
                }
            }
        });
    }

    /**
     * 初始化MediaPlayer
     */
    private void initMediaPlayer() {
        mediaPlayer = new MediaPlayer();
        mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mediaPlayer) {
                //播放时时间变化
                startTime.setText(formatTime(mediaPlayer.getCurrentPosition()));//开始时间
                endTime.setText(formatTime(mediaPlayer.getDuration()));//结束时间
                mSeekBar.setMax(mediaPlayer.getDuration());//进度条最大值  视频最长时间
                mSeekBar.setProgress(mediaPlayer.getCurrentPosition());更新进度条
            }
        });
    }

    /**
     * 格式化时间
     * mm:ss
     *
     * @param time
     * @return
     */
    private String formatTime(long time) {
        String resultTime = "00:00";
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("mm:ss");
        Date date = new Date(time);
        resultTime = simpleDateFormat.format(date);
        return resultTime;
    }

    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case UPDATE_TIME:
                    updateTime();
                    mHandler.sendEmptyMessageDelayed(UPDATE_TIME, 500);//跟新播放时间  500毫秒更新
                    break;
                case HIDE_CONTROL:
                    hideControl();//显示操作栏
                    break;
            }
        }
    };

    /**
     * 更新播放时间
     */
    private void updateTime() {
        startTime.setText(formatTime(mediaPlayer.getCurrentPosition()));//更新播放了多长时间
        mSeekBar.setProgress(mediaPlayer.getCurrentPosition());//更新进度条
        if (null != mvideoPlayListeren) {
            mvideoPlayListeren.progressChanged(mediaPlayer.getCurrentPosition());
        }
    }

    /**
     * 隐藏进度条
     */
    private void hideControl() {
        isShow = false;
        controlLl.animate().setDuration(300).translationY(controlLl.getHeight());//动画缩出
    }

    /**
     * 显示进度条
     */
    private void showControl() {
        if (isShow) {
            play();
        }
        isShow = true;
        mHandler.removeMessages(HIDE_CONTROL);
        mHandler.sendEmptyMessage(UPDATE_TIME);
        mHandler.sendEmptyMessageDelayed(HIDE_CONTROL, 5000);//控制栏显示5秒
        controlLl.animate().setDuration(300).translationY(0);//动画缩进
    }

    private void play() {
        previewIv.setVisibility(GONE);
        if (mediaPlayer == null) return;
        if (mediaPlayer.isPlaying()) {
            //暂停
            onPause();
        } else {
            //开始
            mHandler.sendEmptyMessageDelayed(UPDATE_TIME, 500);//500毫秒更新开始时间
            mHandler.sendEmptyMessageDelayed(HIDE_CONTROL, 5000);
            playOrPauseIv.setVisibility(GONE);
            playOrPause2.setImageResource(android.R.drawable.ic_media_pause);
            playOrPause2.setVisibility(VISIBLE);
            mediaPlayer.start();
        }
    }


    /**
     * 初始化操作  view  点击事件 的监听
     */
    private void initEvent() {
        rootViewRl.setOnClickListener(view -> {
            showControl();
        });
        playOrPause2.setOnClickListener(view -> {
            play();
        });
        playOrPauseIv.setOnClickListener(view -> {
            play();
        });
        playFull.setOnClickListener(view -> {
            if (null != mvideoPlayFullListeren) {
                mvideoPlayFullListeren.FullChanged(true);
            }
            if (mactivity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
                //如果是横屏   则 切换竖屏
                mactivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            } else if (getContext().getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
                ///如果是竖屏  则 切换横屏
                mactivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            }
        });
        //进度条改变监听
        mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean b) {
                if (mediaPlayer != null && b) {
                    mProgress = progress;
                    mediaPlayer.seekTo(mProgress);//设置播放的进度条位置
                    if (null != mvideoPlayListeren) {
                        mvideoPlayListeren.progressChanged(mProgress);
                    }
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
            }
        });
    }
    /**
     * 初始化播放路径
     *
     * @param urlOrPath 播放地址  网络地址  本地地址
     */
    public void setUrlOrPath(String urlOrPath) {
        mUri = Uri.parse(urlOrPath);
        if (null == mUri) return;
        setPreviewIv(urlOrPath);
        //初始化要播放的视频地址    本地   和网络
        try {
            mediaPlayer.setDataSource(getContext(), mUri);
            mediaPlayer.prepareAsync();
        } catch (IOException e) {
//            e.printStackTrace();
            LogUtil.logE(TAG,e+"");
        }
    }

    /**
     * 设置首
     * @param path
     */
    public void setPreviewIv(String path) {
        //获取第一帧  图片
        previewIv.setVisibility(VISIBLE);
        //获取视频缩略图
        Glide.with(getContext())
                .load(mUri)
                .frame(0) //第一桢图
                .into(previewIv);
    }


    /**
     * 设置播放回调监听
     *
     * @param videoPlayListeren
     */
    public void setVideoPlayListeren(VideoPlayListeren videoPlayListeren) {
        this.mvideoPlayListeren = videoPlayListeren;
    }

    @FunctionalInterface
    public interface VideoPlayListeren {
        /**
         * 播放回调方法
         *
         * @param progress 当前播放进度时间
         */
        public void progressChanged(int progress);
    }

    /**
     * 设置全屏监听  这里还没写  需求有可能不要
     *
     * @param videoPlayFullListeren
     */
    public void setVideoPlayFullListeren(VideoPlayFullListeren videoPlayFullListeren) {
        this.mvideoPlayFullListeren = videoPlayFullListeren;
    }

    @FunctionalInterface
    public interface VideoPlayFullListeren {
        public void FullChanged(boolean b);
    }

    /**
     * 设置圆角类
     */
    @SuppressLint("NewApi")
    public class RadiusViewOutlineProvider extends ViewOutlineProvider {
        private float mRadius;

        public RadiusViewOutlineProvider(float radius) {
            this.mRadius = radius;
        }

        @Override
        public void getOutline(View view, Outline outline) {
            Rect rect = new Rect();
            view.getGlobalVisibleRect(rect);
            int leftMargin = 0;
            int topMargin = 0;
            Rect selfRect = new Rect(leftMargin, topMargin,
                    rect.right - rect.left - leftMargin,
                    rect.bottom - rect.top - topMargin);
            outline.setRoundRect(selfRect, mRadius);//设置圆角  和
        }
    }


    /**
     * 暂停播放
     * 在用的activity  onPause 周期里面调用
     */
    public void onPause() {
        //暂停
        if (null == mediaPlayer) return;
        mHandler.removeMessages(UPDATE_TIME);
        mHandler.removeMessages(HIDE_CONTROL);
        playOrPauseIv.setVisibility(VISIBLE);
        playOrPauseIv.setImageResource(android.R.drawable.ic_media_play);
        playOrPause2.setImageResource(android.R.drawable.ic_media_play);
        playOrPause2.setVisibility(VISIBLE);
        mediaPlayer.pause();
    }

}

 

相应布局  layout_video_play

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/video_play_root_rl"
    android:layout_width="match_parent"
    android:clickable="true"
    android:layout_height="wrap_content">

    <com.blue.mvvm.media.view.VideoSurfaceView
        android:id="@+id/video_play_surfaceView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <ImageView
        android:id="@+id/video_play_previewImage"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone"
        android:scaleType="fitXY"
        />

    <ImageView
        android:id="@+id/video_play_playOrPause"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:clickable="true"
        android:src="@android:drawable/ic_media_play" />

    <LinearLayout
        android:id="@+id/video_play_control_ll"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_alignParentBottom="true"
        android:clickable="true"
        android:gravity="center_vertical"
        android:orientation="horizontal">


        <ImageView
            android:id="@+id/video_play_playOrPause2"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_marginLeft="12dp"
            android:clickable="true"
            android:src="@android:drawable/ic_media_play"
            android:visibility="visible" />

        <TextView
            android:id="@+id/video_play_start_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="12dp"
            android:text="00.00"
            android:textColor="#ffffff" />

        <SeekBar
            android:id="@+id/video_play_progess"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="6"
            android:maxHeight="1dp"
            android:progressDrawable="@drawable/po_seekbar"
            android:thumb="@drawable/seekbar_thumb" />

        <TextView
            android:id="@+id/video_play_end_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="12dp"
            android:text="00.00"
            android:textColor="#ffffff" />

        <ImageView
            android:id="@+id/video_play_full"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_centerInParent="true"
            android:layout_marginLeft="12dp"
            android:scaleType="centerCrop"
            android:clickable="true"
            android:src="@drawable/ic_media_full"
            android:visibility="visible" />

    </LinearLayout>
</RelativeLayout>

用法在mainActivity     main_layout.xml

<com.blue.mvvm.media.VideoPlay
    android:id="@+id/main_VideoPlay"
    android:layout_width="match_parent"
    android:layout_height="300dp" />

用法在mainActivity   setUrlOrPath(url)本地视频地址  和 网络地址

binding.mainVideoPlay.setUrlOrPath("https://vdse.bdstatic.com/9712394c6705f0b81841b28590061766.mp4");

****注意重要事情说三遍  如果要全屏播放

在activity里重写

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    //横屏 竖屏切换的操作  android:configChanges="orientation|keyboardHidden|screenSize"   不重走activity 的生命周期
    Display display = getWindowManager().getDefaultDisplay();
    Point size = new Point();
    display.getSize(size);
    LinearLayout.LayoutParams params=(LinearLayout.LayoutParams)binding.mainVideoPlay.getLayoutParams();
    if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
        //如果是横屏
        params.width=size.x;
        params.height=size.y;
     
    } else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
        ///如果是竖屏
        params.width=size.x;
        params.height=size.y/3;
       
    }
}

在重写该方法是要让他全屏时运行到这个方法,必须在manifest里面设置以下方才有效

android:configChanges="orientation|keyboardHidden|screenSize"

android:screenOrientation="portrait"

<activity
    android:name=".module.MainActivity"
    android:configChanges="orientation|keyboardHidden|screenSize"
    android:screenOrientation="portrait"/>

在补充一点   在gradle 里加入glide  获取第一帧图片需要  速度快

implementation 'com.github.bumptech.glide:glide:4.9.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值