视频播放器+控制器--封装

效果图:

1.切换横竖屏

2.重力感应切换横竖屏

3.判断网络状态

 

videoview布局:

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

    <com.easefun.polyvsdk.ijk.IjkVideoView
        android:id="@+id/videoView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true" />

    <RelativeLayout
        android:id="@+id/controllerLayout"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_alignParentBottom="true"
        android:background="#aa000000">

        <ImageView
            android:id="@+id/play"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:paddingLeft="6dp"
            android:paddingRight="6dp"
            tools:src="@drawable/icon_play" />

        <TextView
            android:id="@+id/currentTime"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_toRightOf="@id/play"
            android:gravity="center_vertical"
            android:paddingLeft="5dp"
            android:paddingRight="5dp"
            android:textColor="@color/white"
            android:textSize="12sp"
            tools:text="00:12" />

        <SeekBar
            android:id="@+id/seekBar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginRight="80dp"
            android:layout_toRightOf="@id/currentTime"
            android:maxHeight="40dp"
            android:minHeight="40dp"
            android:progressDrawable="@drawable/player_setting_bright_progressbar"
            android:thumb="@drawable/seekbar_button"
            android:thumbOffset="0dp" />

        <ImageView
            android:id="@+id/changeScreen"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_alignParentRight="true"
            android:paddingLeft="6dp"
            android:paddingRight="6dp"
            android:src="@drawable/icon_fullscreen" />

        <TextView
            android:id="@+id/totalTime"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_toLeftOf="@id/changeScreen"
            android:gravity="center_vertical"
            android:paddingLeft="5dp"
            android:paddingRight="5dp"
            android:textColor="@color/white"
            android:textSize="12sp"
            tools:text="05:17" />

    </RelativeLayout>
</RelativeLayout>
XueHuVideoView
public class XueHuVideoView extends RelativeLayout {
    @BindView(R.id.videoView)
    IjkVideoView videoView;
    @BindView(R.id.play)
    ImageView play;
    @BindView(R.id.currentTime)
    TextView currentTime;
    @BindView(R.id.seekBar)
    SeekBar seekBar;
    @BindView(R.id.changeScreen)
    ImageView changeScreen;
    @BindView(R.id.totalTime)
    TextView totalTime;
    @BindView(R.id.controllerLayout)
    RelativeLayout controllerLayout;

    private ConnectStateReceiver receiver;
    private Timer timer;
    private String videoName;
    private Handler mHandler;
    private SimpleDateFormat dateFormat = new SimpleDateFormat("mm:ss");
    private String videoPath;
    private final int FULL = 0;
    private final int MIN = 1;
    private int duration;
    public int webStatus = -1;

    public XueHuVideoView(Context context) {
        super(context);
        initView(context);
    }

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

    public XueHuVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context);
    }

    private void initView(Context context) {
        View view = LayoutInflater.from(context).inflate(R.layout.xuehu_videoview_layout, null);
        ButterKnife.bind(this, view);
        addView(view);
    }

    private void setVideoListener() {
        videoView.setOnPreparedListener(new OnPreparedListener() {
            @Override
            public void onPrepared(IMediaPlayer iMediaPlayer) {
                duration = videoView.getDuration();
                seekBar.setMax(duration);
                currentTime.setText("00:00");
                totalTime.setText(dateFormat.format(new Date(duration)));
            }
        });
        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                if (fromUser) {
                    videoView.seekTo(progress);
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });

        videoView.setOnCompletionListener(new IMediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(IMediaPlayer iMediaPlayer) {
                if (duration > 0) {
                    EventBus.getDefault().post(new VideoCompleteEvent());
                    videoPause();
                }
            }
        });
    }

    public void videoPause() {
        if (null != timer) {
            timer.cancel();
        }
        videoView.pause();
        play.setImageResource(R.drawable.icon_play);
        LoginResponseEntity.Data user = UserData.getUser();
        if (null != user && !TextUtils.isEmpty(videoName)) {
            int currentPosition = videoView.getCurrentPosition();
            UserData.saveVideoPlayRecord(String.valueOf(user.getUserId()), videoName.trim(), currentPosition);
        }
    }

    public void videoStart() {
        if (!TextUtils.isEmpty(videoName)) {
            if (null != timer) {
                timer.cancel();
            }
            timer = new Timer();
            setVideoPlayRecord();
            videoView.start();
            play.setImageResource(R.drawable.icon_suspended);
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            seekBar.setProgress(videoView.getCurrentPosition());
                            currentTime.setText(dateFormat.format(new Date(videoView.getCurrentPosition())));
                        }
                    });
                }
            }, 0, 1000);
        }
    }

    public void setVideoPath(String url) {
        setVideoListener();
        videoPath = url;
        videoView.setVideoPath(url);
        videoStart();
        if (videoPath.startsWith("http")) {
            registerReceiver();
        }
        videoName = url.substring(url.lastIndexOf(File.separator) + 1);
    }

    public void registerReceiver() {
        if (null == receiver) {
            receiver = new ConnectStateReceiver(this);
            IntentFilter filter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");
            getContext().registerReceiver(receiver, filter);
        }
    }

    public void unRegisterReceiver() {
        if (null != receiver) {
            getContext().unregisterReceiver(receiver);
            receiver = null;
        }
    }

    public void setVideoPlayRecord() {
        LoginResponseEntity.Data user = UserData.getUser();
        if (null != user && !TextUtils.isEmpty(videoName)) {
            int record = UserData.getVideoPlayRecord(String.valueOf(user.getUserId()), videoName.trim());
            if (record > 0) {
                videoView.seekTo(record);
                seekBar.setProgress(record);
            }
        }
    }

    @OnClick({R.id.root_layout, R.id.play, R.id.changeScreen})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.root_layout:
                controllerLayout.setVisibility(controllerLayout.getVisibility() == View.VISIBLE ? View.INVISIBLE : View.VISIBLE);
                break;
            case R.id.play:
                if (videoView.isPlaying()) {
                    videoPause();
                } else {
                    videoStart();
                }
                break;
            case R.id.changeScreen:
                if (!TextUtils.isEmpty(videoName)) {
                    int tag = (int) (changeScreen.getTag());
                    switch (tag) {
                        case FULL:
                            VideoPlayFullActivity.start(getContext(), videoPath);
                            break;
                        case MIN:
                            EventBus.getDefault().post(new FullVideoCloseEvent());
                            break;
                    }
                }
                break;
        }
    }

    public void setHandler(Handler mHandler) {
        this.mHandler = mHandler;
    }

    public void setChangeScreenMin() {
        changeScreen.setImageResource(R.drawable.icon_switch_panel);
        changeScreen.setTag(MIN);
    }

    public void setChangeScreenFull() {
        changeScreen.setImageResource(R.drawable.icon_fullscreen);
        changeScreen.setTag(FULL);
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        unRegisterReceiver();
    }
}
调用(拿全屏播放举例):
public class VideoPlayFullActivity extends Activity {

    @Autowired
    String videoPath;
    @BindView(R.id.videoView)
    XueHuVideoView videoView;

    private Handler mHandler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ARouter.getInstance().inject(this);
        setContentView(R.layout.activity_video_full);
        ButterKnife.bind(this);
        EventBusHelp.register(this);

        videoView.setHandler(mHandler);
        videoView.setVideoPath(videoPath);
        videoView.setChangeScreenMin();
        ScreenRotateUtil.getInstance(this).start(this);
        videoView.videoStart();
    }

    @Override
    protected void onStart() {
        super.onStart();
        videoView.videoStart();
    }

    public static void start(Context context, String videoPath) {
        Intent starter = new Intent(context, VideoPlayFullActivity.class);
        starter.putExtra(IntentKey.videoPath, videoPath);
        if (!(context instanceof Activity)) {
            starter.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        }
        context.startActivity(starter);
    }

    @Override
    protected void onPause() {
        super.onPause();
        videoView.videoPause();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (null != mHandler) {
            mHandler.removeCallbacksAndMessages(null);
            mHandler = null;
        }
        ScreenRotateUtil.getInstance(this).stop();
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent(FullVideoCloseEvent event) {
        finish();
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent(VideoCompleteEvent event) {
        finish();
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent(VideoCloseEvent event) {
        finish();
    }
} 

网络状态监听广播

public class ConnectStateReceiver extends BroadcastReceiver {

    private XueHuVideoView videoView;

    public ConnectStateReceiver() {
        super();
    }

    public ConnectStateReceiver(XueHuVideoView videoView) {
        this.videoView = videoView;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Service.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = cm.getActiveNetworkInfo();
        if (null != networkInfo) {
            int type = networkInfo.getType();
            if (type != videoView.webStatus) {
                videoView.webStatus = type;
                if (type == ConnectivityManager.TYPE_MOBILE) {
                    NetConnectDialog.show(context, videoView);
                }
            }
        }
    }
}

提示对话框

public class NetConnectDialog {

    public static void show(Context context, final XueHuVideoView videoView) {
        videoView.videoPause();
        AlertDialog alertDialog = new AlertDialog(context).builder();
        alertDialog.setCancelable(false);
        alertDialog.setTitle("流量使用提示");
        alertDialog.setMsg("当前网络无Wi-Fi,继续使用可能会被运营商收取流量费用");
        alertDialog.setPositiveButton("继续使用", new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                videoView.videoStart();
            }
        });
        alertDialog.setNegativeButton("停止使用", new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                EventBus.getDefault().post(new VideoCloseEvent());
            }
        });
        alertDialog.show();
    }
}

重力感应

public class ScreenRotateUtil {
    private static final String TAG = ScreenRotateUtil.class.getSimpleName();

    private ScreenRotateUtil() {
    }

    private static ScreenRotateUtil mInstance;

    private Activity mActivity;
    private boolean isClickFullScreen;        // 记录全屏按钮的状态,默认false
    private boolean isOpenSensor = true;      // 是否打开传输,默认打开
    private boolean isLandscape = true;      // 默认是竖屏
    private boolean isChangeOrientation = true;  // 记录点击全屏后屏幕朝向是否改变,默认会自动切换

    private boolean isEffetSysSetting = false;   // 手机系统的重力感应设置是否生效,默认无效,想要生效改成true就好了

    private SensorManager sm;
    private OrientationSensorListener listener;
    private Sensor sensor;


    /**
     * 接收重力感应监听的结果,来改变屏幕朝向
     */
    private Handler mHandler = new Handler(Looper.getMainLooper()) {
        public void handleMessage(Message msg) {

            if (msg.what == 888) {
                int orientation = msg.arg1;

                if (null == mActivity) {
                    return;
                }
                /**
                 * 根据手机屏幕的朝向角度,来设置内容的横竖屏,并且记录状态
                 */
                if (orientation > 45 && orientation < 135) {
                    mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
                    isLandscape = true;
                } else if (orientation > 135 && orientation < 225) {
                    mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
                    isLandscape = false;
                } else if (orientation > 225 && orientation < 315) {
                    mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                    isLandscape = true;
                } else if ((orientation > 315 && orientation < 360) || (orientation > 0 && orientation < 45)) {
                    mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                    isLandscape = false;
                }
            }
        }
    };


    /**
     * 初始化,获取实例
     *
     * @param context
     * @return
     */
    public static ScreenRotateUtil getInstance(Context context) {
        if (mInstance == null) {
            synchronized (ScreenRotateUtil.class) {
                if (mInstance == null) {
                    mInstance = new ScreenRotateUtil(context);
                }
            }
        }
        return mInstance;
    }

    /**
     * 初始化重力感应传感器
     *
     * @param context
     */
    private ScreenRotateUtil(Context context) {
        // 初始化重力感应器
        sm = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
        sensor = sm.getDefaultSensor(Sensor.TYPE_GRAVITY);
        listener = new OrientationSensorListener(mHandler);
    }

    /**
     * 重力感应监听者
     */
    public class OrientationSensorListener implements SensorEventListener {
        private static final int _DATA_X = 0;
        private static final int _DATA_Y = 1;
        private static final int _DATA_Z = 2;

        public static final int ORIENTATION_UNKNOWN = -1;

        private Handler rotateHandler;

        public OrientationSensorListener(Handler handler) {
            rotateHandler = handler;
        }

        public void onAccuracyChanged(Sensor arg0, int arg1) {
        }

        public void onSensorChanged(SensorEvent event) {
            float[] values = event.values;
            int orientation = ORIENTATION_UNKNOWN;
            float X = -values[_DATA_X];
            float Y = -values[_DATA_Y];
            float Z = -values[_DATA_Z];
            float magnitude = X * X + Y * Y;
            // Don't trust the angle if the magnitude is small compared to the y
            // value
            if (magnitude * 4 >= Z * Z) {
                // 屏幕旋转时
                float OneEightyOverPi = 57.29577957855f;
                float angle = (float) Math.atan2(-Y, X) * OneEightyOverPi;
                orientation = 90 - Math.round(angle);
                // normalize to 0 - 359 range
                while (orientation >= 360) {
                    orientation -= 360;
                }
                while (orientation < 0) {
                    orientation += 360;
                }
            }


            /**
             * 获取手机系统的重力感应开关设置,这段代码看需求,不要就删除
             * screenchange = 1 表示开启,screenchange = 0 表示禁用
             * 要是禁用了就直接返回
             */
            if (isEffetSysSetting) {
                try {
                    int isRotate = Settings.System.getInt(mActivity.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION);

                    // 如果用户禁用掉了重力感应就直接return
                    if (isRotate == 0) return;
                } catch (Settings.SettingNotFoundException e) {
                    e.printStackTrace();
                }
            }


            // 只有点了按钮时才需要根据当前的状态来更新状态
            if (isClickFullScreen) {
                if (isLandscape && screenIsPortrait(orientation)) {           // 之前是横屏,并且当前是竖屏的状态
                    updateState(false, false, true, true);
                } else if (!isLandscape && screenIsLandscape(orientation)) {  // 之前是竖屏,并且当前是横屏的状态
                    updateState(true, false, true, true);
                } else if (isLandscape && screenIsLandscape(orientation)) {    // 之前是横屏,现在还是横屏的状态
                    isChangeOrientation = false;
                } else if (!isLandscape && screenIsPortrait(orientation)) {  // 之前是竖屏,现在还是竖屏的状态
                    isChangeOrientation = false;
                }
            }

            // 判断是否要进行中断信息传递
            if (!isOpenSensor) {
                return;
            }

            if (rotateHandler != null) {
                rotateHandler.obtainMessage(888, orientation, 0).sendToTarget();
            }
        }
    }

    /**
     * 更新状态
     *
     * @param isLandscape         横屏
     * @param isClickFullScreen   全屏点击
     * @param isOpenSensor        打开传输
     * @param isChangeOrientation 朝向改变
     */
    private void updateState(boolean isLandscape, boolean isClickFullScreen, boolean isOpenSensor, boolean isChangeOrientation) {
        this.isLandscape = isLandscape;
        this.isClickFullScreen = isClickFullScreen;
        this.isOpenSensor = isOpenSensor;
        this.isChangeOrientation = isChangeOrientation;
    }


    /**
     * 当前屏幕朝向是否横屏
     *
     * @param orientation
     * @return
     */
    private boolean screenIsLandscape(int orientation) {
        return ((orientation > 45 && orientation <= 135) || (orientation > 225 && orientation <= 315));
    }

    /**
     * 当前屏幕朝向是否竖屏
     *
     * @param orientation
     * @return
     */
    private boolean screenIsPortrait(int orientation) {
        return (((orientation > 315 && orientation <= 360) || (orientation >= 0 && orientation <= 45))
                || (orientation > 135 && orientation <= 225));
    }

    /**
     * 根据朝向来改变屏幕朝向
     *
     * @param isLandscape
     * @param isNeedChangeOrientation 是否需要改变判断值
     */
    private void changeOrientation(boolean isLandscape, boolean isNeedChangeOrientation) {
        if (isLandscape) {
            // 切换成竖屏
            mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            if (isNeedChangeOrientation) this.isLandscape = false;
        } else {
            // 切换成横屏
            mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            if (isNeedChangeOrientation) this.isLandscape = true;
        }
    }


    /**
     * 开启监听
     * 绑定切换横竖屏Activity的生命周期
     *
     * @param activity
     */
    public void start(Activity activity) {
        mActivity = activity;
        sm.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_UI);
    }

    /**
     * 注销监听
     */
    public void stop() {
        sm.unregisterListener(listener);
        mActivity = null;  // 防止内存泄漏
    }


    /**
     * 当前屏幕的朝向,是否是横屏
     *
     * @return
     */
    public boolean isLandscape() {
        return this.isLandscape;
    }


    /**
     * 设置系统横竖屏按钮是否生效,默认无效
     *
     * @param isEffet
     */
    public void setEffetSysSetting(boolean isEffet) {
        isEffetSysSetting = isEffet;
    }

    /**
     * 旋转的开关,全屏按钮点击时调用
     */
    public void toggleRotate() {

        /**
         * 先判断是否已经开启了重力感应,没开启就直接普通的切换横竖屏
         */
        if (isEffetSysSetting) {
            try {
                int isRotate = Settings.System.getInt(mActivity.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION);

                // 如果用户禁用掉了重力感应就直接切换
                if (isRotate == 0) {
                    changeOrientation(isLandscape, true);
                    return;
                }
            } catch (Settings.SettingNotFoundException e) {
                e.printStackTrace();
            }
        }

        /**
         * 如果开启了重力i感应就需要修改状态
         */
        isOpenSensor = false;
        isClickFullScreen = true;
        if (isChangeOrientation) {
            changeOrientation(isLandscape, false);
        } else {
            isLandscape = !isLandscape;
            changeOrientation(isLandscape, false);
        }
    }

}

有几个需要注意的坑

1.ijkVideoView的setVideoPath方法是直接播放的,但是setOnPreparedListener准备是需要时间的,如果4G进入视频页,弹出提示对话框时需要暂停播放,但此时还没有准备好的话,你是没办法暂停的,所以的我用的蠢办法是直接退出整个界面,看你还怎么播

2.全屏切换横竖屏时,清单文件需要配置android:configChanges="orientation|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"

3.判断网络状态是,因为广播是一直在的,会一直弹出提示对话框,所以需要标识符来记录之前的网络状态,如果变了且是4G,才会弹框

原文地址:https://www.cnblogs.com/anni-qianqian/p/7453519.html