zoukankan      html  css  js  c++  java
  • 视频播放器+控制器--封装

    效果图:

    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,才会弹框

  • 相关阅读:
    委托&指针函数&回调函数
    Unity animation笔记1
    hadoop源码编译
    protocbuf的安装
    学习hadoop不错的一些文章
    moven的安装
    在Linux上安装与配置Hadoop
    linux tar命令详解
    How to contribute to hadoop common
    Ubuntu下SVN的安装
  • 原文地址:https://www.cnblogs.com/anni-qianqian/p/7453519.html
Copyright © 2011-2022 走看看