美文网首页
不能不会的VideoView视频播放器

不能不会的VideoView视频播放器

作者: 付凯强 | 来源:发表于2018-01-07 15:05 被阅读0次

    1. VideoView简介

    • Android实现视频播放主要是使用VideoView类来实现的。
    • VideoView背后是使用MediaPlayer来对视频文件进行控制的。
    • 只支持mp4、avi、3gp格式的视频,支持格式单一。

    2. VideoView常用方法:

    • setVideoPath:设置要播放的视频文件的位置
    • start:开始或继续播放视频
    • pause:暂停播放视频
    • resume:将视频从头开始播放
    • seekTo:从指定的位置开始播放视频
    • isPlaying:判断当前是否正在播放视频
    • getDuration:获取载入的视频文件的时长

    3. VideoView播放视频的小栗子:

    • 添加网络和SD卡权限:
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
    
    • 添加VideoView布局:
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <VideoView
            android:id="@+id/vv_VideoView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
    
            <Button
                android:id="@+id/play"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Play"/>
    
            <Button
                android:id="@+id/pause"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Pause"/>
    
            <Button
                android:id="@+id/replay"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Replay"/>
        </LinearLayout>
    
    </LinearLayout>
    
    • 添加ButterKnife:
        compile 'com.jakewharton:butterknife:8.8.1'
        annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
    
    • 初始化权限:
    private void requestSDpermission() {
            if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
            } else {
                initVideoPath();
            }
        }
    
    @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            switch (requestCode) {
                case 1:
                    if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                        initVideoPath();
                    } else {
                        Toast.makeText(this, "拒绝权限将无法使用程序", Toast.LENGTH_SHORT).show();
                        finish();
                    }
                    break;
                default:
            }
        }
    
    • 初始化VideoPath:
    private void initVideoPath() {
            File file = new File(Environment.getExternalStorageDirectory(), "vivo.mp4");
            vvVideoView.setVideoPath(file.getPath());
        }
    
    • 完整代码:
    /**
     * VideoView
     * fu kai qiang 2017/17/31
     */
    public class MainActivity extends AppCompatActivity {
    
        @BindView(R.id.vv_VideoView)
        VideoView vvVideoView;
        Unbinder mUnbinder;
        @BindView(R.id.play)
        Button play;
        @BindView(R.id.pause)
        Button pause;
        @BindView(R.id.replay)
        Button replay;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mUnbinder = ButterKnife.bind(this);
            requestSDpermission();
            initVideoPath();
        }
    
        private void requestSDpermission() {
            if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
            } else {
                initVideoPath();
            }
        }
    
        private void initVideoPath() {
            File file = new File(Environment.getExternalStorageDirectory(), "vivo.mp4");
            vvVideoView.setVideoPath(file.getPath());
        }
    
        @OnClick({R.id.play, R.id.pause, R.id.replay})
        public void onViewClicked(View view) {
            switch (view.getId()) {
                case R.id.play:
                    if (!vvVideoView.isPlaying()) {
                        vvVideoView.start();
                    }
                    break;
                case R.id.pause:
                    if (vvVideoView.isPlaying()) {
                        vvVideoView.pause();
                    }
                    break;
                case R.id.replay:
                    if (vvVideoView.isPlaying()) {
                        vvVideoView.resume();
                    }
                    break;
            }
        }
    
        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            switch (requestCode) {
                case 1:
                    if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                        initVideoPath();
                    } else {
                        Toast.makeText(this, "拒绝权限将无法使用程序", Toast.LENGTH_SHORT).show();
                        finish();
                    }
                    break;
                default:
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            mUnbinder.unbind();
            if (vvVideoView != null) {
                vvVideoView.suspend();
            }
        }
    }
    

    4. MediaController简介

    • 从上文可知VideoView自身可以实现视频播放的逻辑,但是我们需要去写布局来操作视频的播放暂停等,那能不能不写布局,就能实现呢?当然可以:VideoView可以借助MediaController实现视频播放的逻辑。MediaController是一个多媒体的类,它提供了丰富的Api,支持快进、快退、上一个、下一个等多媒体操作。

    5. MediaController小栗子

    • 添加网络和SD卡权限:
        <uses-permission android:name="android.permission.INTERNET"></uses-permission>
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
    
    • 添加VideoView布局:
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >
    
        <VideoView
            android:id="@+id/vv_videoView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            />
    
    </RelativeLayout>
    
    • 初始化本地或网络播放路径
        private void initVideoPath() {
            String path_local = Environment.getExternalStorageDirectory().getAbsolutePath() + "/vivo.mp4";
            //本地播放
            mVvVideoView.setVideoPath(path_local);
            //网络播放
    //      mVvVideoView.setVideoURI(Uri.parse(...);
        }
    
    提示:网络测试的话Tomcat下webapps下面放vivo.mp4
    
    • videoView和MediaController进行绑定
        private void initBind() {
            MediaController mediaController = new MediaController(this);
            mVvVideoView.setMediaController(mediaController);
            mediaController.setMediaPlayer(mVvVideoView);
        }
    
    注意:必须互相设置进行绑定
    
    • 暂时设置为横屏:
        <activity
            android:name=".MainActivity"
            android:screenOrientation="landscape">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
    
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    
    • Activity完整代码:
    public class MainActivity extends AppCompatActivity {
    
        @BindView(R.id.vv_videoView)
        VideoView mVvVideoView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ButterKnife.bind(this);
            initVideoPath();
            initBind();
        }
    
        /**
         * videoView和MediaController绑定
         */
        private void initBind() {
            MediaController mediaController = new MediaController(this);
            mVvVideoView.setMediaController(mediaController);
            mediaController.setMediaPlayer(mVvVideoView);
        }
    
        /**
         * 初始化本地或网络播放路径
         */
        private void initVideoPath() {
    //        mVvVideoView.setVideoPath(getLocalPath());
            mVvVideoView.setVideoURI(Uri.parse("http://192.168.0.108:8080/video/vivo.mp4"));
        }
    
        /**
         * 获取本地路径
         *
         * @return
         */
        @NonNull
        private String getLocalPath() {
            return new File(Environment.getExternalStorageDirectory(), "vivo.mp4").getPath();
        }
    
    }
    

    6. 自定义UI界面

    • VideoView需要添加播放暂停快进快退进度条等等按钮,而MdeiaController自带了播放暂停快进快退进度条等UI界面,但是这些都不是我们想要的,因为UI太过于简陋,所以为了美观,以及需求的多样化,我们需要定义我们自己的UI,通过VideoView自身的逻辑来实现视频播放。

    • 修改布局:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >
    
        <RelativeLayout
            android:id="@+id/rl_videolayout"
            android:layout_width="wrap_content"
            android:layout_height="match_parent">
    
            <VideoView
                android:id="@+id/vv_videoView"
                android:layout_width="match_parent"
                android:layout_height="240dp"
                />
    
            <LinearLayout
                android:id="@+id/ll_controllerBar_layout"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:layout_alignParentBottom="true"
                android:orientation="vertical">
                <!--进度条-->
                <SeekBar
                    android:id="@+id/sb_progress_seekbar"
                    android:layout_width="match_parent"
                    android:layout_height="5dp"
                    android:layout_marginLeft="-20dp"
                    android:layout_marginRight="-20dp"
                    android:indeterminate="false"
                    android:max="100"
                    android:progress="20"
                    android:progressDrawable="@drawable/seekbar_style_pro"
                    />
    
                <RelativeLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:background="#101010"
                    android:gravity="center_vertical">
    
                    <LinearLayout
                        android:id="@+id/ll_left_layout"
                        android:layout_width="wrap_content"
                        android:layout_height="match_parent"
                        android:gravity="center_vertical"
                        android:orientation="horizontal">
                        <!--播放暂停-->
                        <Button
                            android:id="@+id/bt_start_pause"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginLeft="10dp"
                            android:text="Pause"/>
                        <!--现在的时间-->
                        <TextView
                            android:id="@+id/tv_time_current"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginLeft="10dp"
                            android:text="00:00:00"
                            android:textColor="#FFF"
                            android:textSize="20sp"/>
                        <!--斜杠-->
                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginLeft="5dp"
                            android:text="/"
                            android:textColor="#4C4C4C"
                            android:textSize="5dp"/>
                        <!--总共的时间-->
                        <TextView
                            android:id="@+id/tv_time_total"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginLeft="5dp"
                            android:text="00:00:00"
                            android:textColor="#4C4C4C"
                            android:textSize="20sp"/>
                    </LinearLayout>
    
                    <LinearLayout
                        android:id="@+id/ll_right_layout"
                        android:layout_width="wrap_content"
                        android:layout_height="match_parent"
                        android:layout_alignParentRight="true"
                        android:layout_marginRight="10dp"
                        android:layout_toRightOf="@id/ll_left_layout"
                        android:gravity="center_vertical|right"
                        android:orientation="horizontal">
    
                        <TextView
                            android:id="@+id/tv_vol_name"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginLeft="20dp"
                            android:text="Vol"
                            android:textColor="#FFF"
                            android:textSize="20sp"
                            android:visibility="gone"/>
                        <!--音量-->
                        <SeekBar
                            android:id="@+id/sb_vol_seekbar"
                            android:layout_width="143dp"
                            android:layout_height="5dp"
                            android:layout_marginLeft="5dp"
                            android:indeterminate="false"
                            android:max="100"
                            android:progress="20"
                            android:progressDrawable="@drawable/seekbar_style_pro"
                            android:visibility="gone"/>
    
                        <View
                            android:id="@+id/v_line"
                            android:layout_width="1dp"
                            android:layout_height="match_parent"
                            android:layout_marginBottom="5dp"
                            android:layout_marginLeft="5dp"
                            android:layout_marginTop="5dp"
                            android:background="#1E1E1E"
                            android:visibility="gone"></View>
                        <!--横竖屏切换-->
                        <Button
                            android:id="@+id/bt_switch"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginLeft="5dp"
                            android:text="Switch"/>
                    </LinearLayout>
                </RelativeLayout>
            </LinearLayout>
        </RelativeLayout>
    </RelativeLayout>
    
    <!--seekbar_style_pro:-->
    
    <?xml version="1.0" encoding="utf-8"?>
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    
        <item android:id="@android:id/background">
            <shape>
                <solid android:color="#707070"></solid>
                <size android:height="5dp"></size>
            </shape>
        </item>
        <item android:id="@android:id/progress">
            <clip>
                <shape>
                    <solid android:color="#B94310"></solid>
                    <size android:height="5dp"></size>
                </shape>
            </clip>
        </item>
    </layer-list>
    
    <!--seekbar_style_vol-->
    
    <?xml version="1.0" encoding="utf-8"?>
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    
        <item android:id="@android:id/background">
            <shape>
                <solid android:color="#101010"></solid>
            </shape>
        </item>
        <item android:id="@android:id/progress">
            <clip>
                <shape>
                    <solid android:color="#ffb97244"></solid>
                </shape>
            </clip>
        </item>
    </layer-list>
    

    7. 删除以下MediaController的逻辑:

        /**
         * videoView和MediaController绑定
         */
        private void initBind() {
            MediaController mediaController = new MediaController(this);
            mVvVideoView.setMediaController(mediaController);
            mediaController.setMediaPlayer(mVvVideoView);
        }
    

    8. 初始化控件:

        //需要竖屏隐藏的音量title
        @BindView(R.id.tv_vol_name)
        TextView mTvVolName;
        //徐奥竖屏隐藏的音量分割线
        @BindView(R.id.v_line)
        //最外层的布局
        @BindView(R.id.rl_videolayout)
        RelativeLayout mRlVideolayout;
        //VideoView
        @BindView(R.id.vv_videoView)
        VideoView mVvVideoView;
        //进程进度条
        @BindView(R.id.sb_progress_seekbar)
        SeekBar mSbProgressSeekbar;
        //播放 暂停
        @BindView(R.id.bt_start_pause)
        Button mBtStartPause;
        //现在的时间
        @BindView(R.id.tv_time_current)
        TextView mTvTimeCurrent;
        //总共的时间
        @BindView(R.id.tv_time_total)
        TextView mTvTimeTotal;
        //音量进度条
        @BindView(R.id.sb_vol_seekbar)
        SeekBar mSbVolSeekbar;
        //全屏切换开关
        @BindView(R.id.bt_switch)
        Button mBtSwitch;
        //控制区域
        @BindView(R.id.ll_controllerBar_layout)
        LinearLayout mLlControllerBarLayout;
        //控制区域左半边
        @BindView(R.id.ll_left_layout)
        LinearLayout mLlLeftLayout;
        //控制区域右半边
        @BindView(R.id.ll_right_layout)
        LinearLayout mLlRightLayout;
    

    9. 播放和暂停逻辑

    //控制视频的播放和暂停
    case R.id.bt_start_pause:
         if (mVvVideoView.isPlaying()) {
                 mBtStartPause.setText("Start");
                 mVvVideoView.pause();
            } else {
                 mBtStartPause.setText("Pause");
                 mVvVideoView.start();
            }
         break;
    

    10. 定义格式时间的方法

         /**
         * 时间的格式化
         * @param textView
         * @param millisecond
         */
        public void updateTime(TextView textView, int millisecond) {
            int second = millisecond / 1000; //总共换算的秒
            int hh = second / 3600;  //小时
            int mm = second % 3600 / 60; //分钟
            int ss = second % 60; //时分秒中的秒的得数
    
            String str = null;
            if (hh != 0) {
                //如果是个位数的话,前面可以加0  时分秒
                str = String.format("%02d:%02d:%02d", hh, mm, ss);
            } else {
                str = String.format("%02d:%02d", mm, ss);
            }
            textView.setText(str);
        }
    

    11. 自动刷新并设置当前视频时间和视频总时间及同步SeekBar进度

        //刷新机制的标志
        private static final int UPDATE_UI = 1;
         /**
         * 定义Handler刷新时间
         * 得到并设置当前视频播放的时间
         * 得到并设置视频播放的总时间
         * 设置SeekBar总进度和当前视频播放的进度
         * 并反复执行Handler刷新时间
         * 指定标识用于关闭Handler
         */
        private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                if (msg.what == UPDATE_UI) {
    
                    int currentPosition = mVvVideoView.getCurrentPosition();
                    int totalduration = mVvVideoView.getDuration();
    
                    updateTime(mTvTimeCurrent, currentPosition);
                    updateTime(mTvTimeTotal, totalduration);
    
                    mSbProgressSeekbar.setMax(totalduration);
                    mSbProgressSeekbar.setProgress(currentPosition);
    
                    mHandler.sendEmptyMessageDelayed(UPDATE_UI, 500);
    
                }
            }
        };
    

    12. 初始化播放和刷新时间机制

    private void initVideoPlay() {
            mVvVideoView.start();
            //第一个参数是标志,第二个参数是刷新间隔时间
            mHandler.sendEmptyMessageDelayed(UPDATE_UI, 500);
     }
    

    13. 适时关闭和开启刷新机制

        @OnClick(R.id.bt_start_pause)
        public void onViewClicked(View view) {
            switch (view.getId()) {
                //控制视频的播放和暂停
                case R.id.bt_start_pause:
                    if (mVvVideoView.isPlaying()) {
                        mBtStartPause.setText("Start");
                        mVvVideoView.pause();
                        //停止刷新UI
                        mHandler.removeMessages(UPDATE_UI);
                    } else {
                        mBtStartPause.setText("Pause");
                        mVvVideoView.start();
                        //开启刷新UI
                        mHandler.sendEmptyMessage(UPDATE_UI);
                    }
                    break;
            }
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            //停止刷新UI
            mHandler.removeMessages(UPDATE_UI);
        }
    

    14. 拖动SeekBar同步SeekBar和Time和VideoView

        private void synchScrollSeekBarAndTime() {
            mSbProgressSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                //进度改变的时候同步Time
                @Override
                public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                    updateTime(mTvTimeCurrent, progress);
                }
    
                //拖动的时候关闭刷新机制
                @Override
                public void onStartTrackingTouch(SeekBar seekBar) {
                    mHandler.removeMessages(UPDATE_UI);
                }
    
                //拖动停止同步VideoView和开启刷新机制
                @Override
                public void onStopTrackingTouch(SeekBar seekBar) {
                    int progress = seekBar.getProgress();
                    mVvVideoView.seekTo(progress);
                    mHandler.sendEmptyMessage(UPDATE_UI);
                }
            });
        }
    

    15. 自动横竖屏切换

    • 删掉强制横屏的代码:
    android:screenOrientation="landscape"
    
    • 防止横竖屏切换重建Activity:配置文件添加
    android:configChanges="orientation|screenSize|keyboard|keyboardHidden"
    
    • 防止横屏的时候视频右边有大量的空白:
        //定义两个变量:代表当前屏幕的宽和屏幕的高
        private int screen_width, screen_height;
    
        /**
         * 获取屏幕的宽和屏幕的高
         */
        private void initScreenWidthAndHeight() {
            screen_width = getResources().getDisplayMetrics().widthPixels;
            screen_height = getResources().getDisplayMetrics().heightPixels;
        }
    
        /**
         * 设置VideoView和最外层相对布局的宽和高
         * @param width : 像素的单位
         * @param height : 像素的单位
         */
        private void setVideoViewScale(int width, int height) {
            //获取VideoView宽和高
            ViewGroup.LayoutParams layoutParams = mVvVideoView.getLayoutParams();
            //赋值给VideoView的宽和高
            layoutParams.width = width;
            layoutParams.height = height;
            //设置VideoView的宽和高
            mVvVideoView.setLayoutParams(layoutParams);
    
            //同上
            ViewGroup.LayoutParams layoutParams1 = mRlVideolayout.getLayoutParams();
            layoutParams.width = width;
            layoutParams.height = height;
            mRlVideolayout.setLayoutParams(layoutParams1);
        }
    
       /**
         * 监听屏幕方向的改变,横竖屏的时候分别做处理
         *
         * @param newConfig
         */
        @Override
        public void onConfigurationChanged(Configuration newConfig) {
            super.onConfigurationChanged(newConfig);
            //当屏幕方向是横屏的时候,我们应该对VideoView以及包裹VideoView的布局(也就是对整体)进行拉伸
            if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
                setVideoViewScale(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT);
            }
            //当屏幕方向是竖屏的时候,竖屏的时候的高我们需要把dp转为px
            else {
                setVideoViewScale(ViewGroup.LayoutParams.MATCH_PARENT,DensityUtils.dip2px(this,240));
            }
        }
    
        /**
     * Created by FuKaiqiang on 2018-01-06.
     */
    
    public class DensityUtils {
        /**
         * 根据手机的分辨率从 dip 的单位 转成为 px(像素)
         */
        public static int dip2px(Context context, float dpValue) {
            final float scale = context.getResources().getDisplayMetrics().density;
            return (int) (dpValue * scale + 0.5f);
        }
    
        /**
         * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
         */
        public static int px2dip(Context context, float pxValue) {
            final float scale = context.getResources().getDisplayMetrics().density;
            return (int) (pxValue / scale + 0.5f);
        }
    
        /**
         * 将px值转换为sp值,保证文字大小不变
         */
        public static int px2sp(Context context, float pxValue) {
            final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
            return (int) (pxValue / fontScale + 0.5f);
        }
    
        /**
         * 将sp值转换为px值,保证文字大小不变
         */
        public static int sp2px(Context context, float spValue) {
            final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
            return (int) (spValue * fontScale + 0.5f);
        }
    
    }
    

    16. 音量

        //初始化音频管理器
        private AudioManager mAudioManager;
        /**
         * 初始化音频管理器;获取设备最大音量和当前音量并设置
         */
        private void initAudioManager() {
            mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
            int streamMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
            int streamVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
            mSbVolSeekbar.setMax(streamMaxVolume);
            mSbVolSeekbar.setProgress(streamVolume);
        }
    

    17. 手动横竖屏切换

        //  定义一个横竖屏切换的变量
        private boolean isFullScreen = false;
       //  根据横竖屏的变化设置变量值
     /**
         * 监听屏幕方向的改变,横竖屏的时候分别做处理
         *
         * @param newConfig
         */
        @Override
        public void onConfigurationChanged(Configuration newConfig) {
            super.onConfigurationChanged(newConfig);
            //当屏幕方向是横屏的时候,我们应该对VideoView以及包裹VideoView的布局(也就是对整体)进行拉伸
            if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
                setVideoViewScale(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
                //横屏的时候显示
                mTvVolName.setVisibility(View.VISIBLE);
                mVLine.setVisibility(View.VISIBLE);
                mSbVolSeekbar.setVisibility(View.VISIBLE);
                //横屏的时候为true
                isFullScreen = true;
            }
            //当屏幕方向是竖屏的时候,竖屏的时候的高我们需要把dp转为px
            else {
                setVideoViewScale(ViewGroup.LayoutParams.MATCH_PARENT, DensityUtils.dip2px(this, 240));
                //竖屏的时候吟唱
                mTvVolName.setVisibility(View.GONE);
                mVLine.setVisibility(View.GONE);
                mSbVolSeekbar.setVisibility(View.GONE);
                //竖屏的时候为
                isFullScreen = false;
            }
        }
                //手动横竖屏切换
                case R.id.bt_switch:
                    if (isFullScreen) {
                        //切换为竖屏
                        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                    } else {
                        //切换为横屏
                        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                    }
                    break;
    

    18. 自定义VideoView

    • 虽然可以实现了最外面的相对布局在横屏的时候全屏,但是VideoView并未全屏,原因是视频的宽度和高度并没有手机屏幕那么大,所以这个时候依然在VideoView宽留有空白区域,这个时候就应该自定义VideoView:
    /**
     * Created by FuKaiqiang on 2018-01-06.
     */
    
    public class MyVideoView extends VideoView {
    
        private int screen_width = 1920;
        private int screen_height = 1080;
    
        public MyVideoView(Context context) {
            super(context);
        }
    
        public MyVideoView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public MyVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            //得到手机屏幕的宽和高
            DisplayMetrics dm = new DisplayMetrics();
            dm = getResources().getDisplayMetrics();
            int screenWidth = dm.widthPixels; // 屏幕宽(像素,如:3200px)
            int screenHeight = dm.heightPixels; // 屏幕高(像素,如:1280px)
            //最大限度的展示宽和高
            int width = getDefaultSize(screen_width, widthMeasureSpec);
            int height = getDefaultSize(screen_height, heightMeasureSpec);
            
            setMeasuredDimension(width, height);
        }
    }
        <com.best.testvideoview.MyVideoView
                android:id="@+id/vv_videoView"
                android:layout_width="match_parent"
                android:layout_height="240dp"
                />
    
    onConfigurationChanged方法中:
     //当横屏时主动取消半屏,该设置为全屏
     getWindow().clearFlags((WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN));
     getWindow().addFlags((WindowManager.LayoutParams.FLAG_FULLSCREEN));
    //当竖屏时主动取消全屏,该设置为半屏
    getWindow().clearFlags((WindowManager.LayoutParams.FLAG_FULLSCREEN));
    getWindow().addFlags((WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN));
    

    19. 手势调节音量和亮度

         /**
         * 初始化手势
         */
        private void initGesture() {
            mVvVideoView.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    //现在的x,y坐标
                    float x = event.getX();
                    float y = event.getY();
                    
                    switch (event.getAction()) {
                        //手指按下:
                        case MotionEvent.ACTION_DOWN:
                            lastX = x;
                            lastY = y;
                            break;
                        //手指移动:
                        case MotionEvent.ACTION_MOVE:
                            //偏移量
                            float moveX = x - lastX;
                            float moveY = y - lastY;
                            //计算绝对值
                            float absMoveX = Math.abs(moveX);
                            float absMoveY = Math.abs(moveY);
                            //手势合法性的验证
                            if (absMoveX > Num && absMoveY > Num) {
                                if (absMoveX < absMoveY) {
                                    isEMove = true;
                                } else {
                                    isEMove = false;
                                }
                            } else if (absMoveX < Num && absMoveY > Num) {
                                isEMove = true;
                            } else if (absMoveX > Num && absMoveY < Num) {
                                isEMove = false;
                            }
                            /**
                             * 区分手势合法的情况下,区分是去调节亮度还是去调节声音
                             */
                            if (isEMove) {
                                //手势在左边
                                if (x < screen_width / 2) {
                                    /**
                                     * 调节亮度
                                     */
                                    if (moveY > 0) {
                                        //降低亮度
                                    } else {
                                        //升高亮度
                                    }
                                    changeBright(-moveY);
                                    //手势在右边
                                } else {
                                    Log.e("Emove", "onTouch: " + "手势在右边");
                                    /**
                                     * 调节音量
                                     */
                                    if (moveY > 0) {
                                        //减小音量
                                    } else {
                                        //增大音量
                                    }
                                    changeVolume(-moveY);
                                }
                            }
                            lastX = x;
                            lastY = y;
                            break;
                        //手指抬起:
                        case MotionEvent.ACTION_UP:
                            break;
                    }
                    return true;
                }
            });
        }
         /**
         * 调节音量:偏移量和音量值的换算
         */
        private void changeVolume(float moveY) {
            int max = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
            int current = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
            int index = (int) (moveY / screen_height * max * 3);
            int volume = Math.max(current + index, 0);
            mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
            mSbVolSeekbar.setProgress(volume);
        }
    
         /**
         * 调节亮度:
         */
        private void changeBright(float moveY) {
            WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
            mBrightness = layoutParams.screenBrightness;
            float index = moveY / screen_height / 3;
            mBrightness += index;
            //做临界值的判断
            if (mBrightness > 1.0f) {
                mBrightness = 1.0f;
            }
            if (mBrightness < 0.01) {
                mBrightness = 0.01f;
            }
            layoutParams.screenBrightness = mBrightness;
            getWindow().setAttributes(layoutParams);
        }
    

    20. 亮度调节的显示

    • 定义一个名为layout_progress的布局,位置放在屏幕中央
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:id="@+id/fl_content"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:layout_centerInParent="true"
                  android:layout_marginTop="-50dp"
                  android:background="#50000000"
                  android:orientation="vertical"
                  android:visibility="gone">
       // 放置音量或者亮度的图片
        <ImageView
            android:id="@+id/operation_bg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            >
    
        <FrameLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|center_vertical"
            android:layout_marginTop="20dp"
            android:paddingBottom="25dp">
            // 音量或者亮度进度条的背景--进度条拿布局来写的,不是seekbar--都Ok的
            <View
                android:layout_width="94dp"
                android:layout_height="4dp"
                android:layout_gravity="left"
                android:background="#000000"
                android:scaleType="fitXY"
                />
           // 音量或者亮度进度条的进度--进度条拿布局来写的,不是seekbar--都Ok的
            <View
                android:id="@+id/operation_percent"
                android:layout_width="94dp"
                android:layout_height="4dp"
                android:layout_gravity="left"
                android:background="#FFF"
                android:scaleType="fitXY"/>
        </FrameLayout>
    </LinearLayout>
      <include layout="@layout/layout_progress"></include>
    
          @BindView(R.id.operation_percent)
          View mOperationPercent;
          @BindView(R.id.fl_content)
          LinearLayout mFlContent;
    
    • 调节音量方法changeVolume中添加:
     if (mFlContent.getVisibility()==View.GONE) mFlContent.setVisibility(View.VISIBLE);
            mOperationBg.setImageResource(R.mipmap.ic_vol);
            ViewGroup.LayoutParams layoutParams = mOperationPercent.getLayoutParams();
            layoutParams.width = (int) (DensityUtils.dip2px(this, 94) * (float) volume / max);
            mOperationPercent.setLayoutParams(layoutParams);
    
    • 调节亮度方法中添加:
    if (mFlContent.getVisibility()==View.GONE) mFlContent.setVisibility(View.VISIBLE);
            mOperationBg.setImageResource(R.mipmap.bright);
            ViewGroup.LayoutParams layoutParams = mOperationPercent.getLayoutParams();
            layoutParams.width = (int) (DensityUtils.dip2px(this, 94) *mBrightness);
            mOperationPercent.setLayoutParams(layoutParams);
    

    21. 不足:

    • 音量的手势调节并非像亮度那般流畅,后续会优化这一部分。
    • 横屏的时候,屏幕的高并没有填充整个屏幕。

    22. Github下载地址,欢迎Star

    相关文章

      网友评论

          本文标题:不能不会的VideoView视频播放器

          本文链接:https://www.haomeiwen.com/subject/aqzenxtx.html