当前位置: 58彩票app下载 > 编程技术 > 正文

不能够不会的VideoView录制播放器,简单定制多少

时间:2019-09-18 11:14来源:编程技术
安卓系统提供了VideoView用来播放一些特定格式的视频,与MediaController结合使用可以对视频播放进行简单控制例如:在布局文件中先声明个VideoView: 1. VideoView简介 Android实现视频播放主要

安卓系统提供了VideoView用来播放一些特定格式的视频,与MediaController结合使用可以对视频播放进行简单控制例如:在布局文件中先声明个VideoView:

1. VideoView简介

  • Android实现视频播放主要是使用VideoView类来实现的。
  • VideoView背后是使用MediaPlayer来对视频文件进行控制的。
  • 只支持mp4、avi、3gp格式的视频,支持格式单一。
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:andro android: android:layout_width="match_parent" android:layout_height="match_parent"> <VideoView android: android:layout_width="match_parent" android:layout_height="wrap_content" /></RelativeLayout>

2. VideoView常用方法:

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

然后,在存储卡的根目录下先放置一个命名为“00.MP4”的视频文件

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();
        }
    }
}
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); VideoView videoView = (VideoView) findViewById(R.id.videoView); MediaController mediaController = new MediaController; videoView.setMediaController(mediaController); mediaController.setMediaPlayer(videoView); //为videoView设置视频路径 String path = Environment.getExternalStorageDirectory().getAbsolutePath(); videoView.setVideoPath(path + "/00.mp4"); }

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();
    }

}

图片 1这里写图片描述

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;

图片 2这里写图片描述

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);
    }

图片 3这里写图片描述

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);
 }
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:andro android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <RelativeLayout android: android:layout_width="match_parent" android:layout_height="240dp"> <VideoView android: android:layout_width="match_parent" android:layout_height="240dp" /> <LinearLayout android: android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignBottom="@id/vv_player" android:background="#8768423e" android:orientation="vertical"> <SeekBar android: android:layout_width="match_parent" android:layout_height="wrap_content" android:indeterminate="false" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal"> <LinearLayout android: android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="5dp" android:layout_marginLeft="5dp" android:gravity="center" android:orientation="horizontal"> <ImageView android: android:layout_width="wrap_content" android:layout_height="wrap_content" android:clickable="true" android:src="@drawable/play_btn_style" /> <TextView android: android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="7dp" android:text="00:00:00" android:textColor="#ffffff" android:textSize="15sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=" / " android:textColor="#ffffff" android:textSize="15sp" /> <TextView android: android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="00:00:00" android:textColor="#ef6363" android:textSize="15sp" /> </LinearLayout> <ImageView android: android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginRight="5dp" android:src="@drawable/full_screen" /> <LinearLayout android: android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="10dp" android:layout_toLeftOf="@id/iv_screenSwitch" android:gravity="end" android:orientation="horizontal" android:visibility="gone"> <ImageView android: android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/volume" /> <SeekBar android: android:layout_width="150dp" android:layout_height="wrap_content" android:indeterminate="false" /> </LinearLayout> </RelativeLayout> </LinearLayout> </RelativeLayout> <ListView android:layout_width="match_parent" android:layout_height="wrap_content" /></LinearLayout>

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);
    }

id为“ll_control”的LinearLayout包含了所有的控制View,将之置于VideoView上且设置半透明背景色,音量调节seekBar在竖屏状态下不可见

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);
            }
        });
    }

主要代码如下:初始化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);
    }

}
private void initUI() { videoView = (VideoView) findViewById(R.id.vv_player); sb_play =  findViewById(R.id.sb_play); sb_volume =  findViewById(R.id.sb_volume); iv_playControl = (ImageView) findViewById(R.id.iv_playControl); iv_screenSwitch = (ImageView) findViewById(R.id.iv_screenSwitch); iv_volume = (ImageView) findViewById(R.id.iv_volume); tv_currentTime =  findViewById(R.id.tv_currentTime); tv_totalTime =  findViewById(R.id.tv_totalTime); ll_volumeControl = (LinearLayout) findViewById(R.id.ll_volumeControl); ll_control = (LinearLayout) findViewById(R.id.ll_control); rl_video = (RelativeLayout) findViewById(R.id.rl_video); sb_volume.setMax(audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)); sb_volume.setProgress(audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)); }

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;
private void initEvent() { iv_playControl.setOnClickListener(new View.OnClickListener() { @Override public void onClick { if (videoView.isPlaying { setPauseStatus(); videoView.pause(); uiHandler.removeMessages(UPDATE_TIME); } else { setPlayStatus(); videoView.start(); uiHandler.sendEmptyMessage(UPDATE_TIME); } } }); sb_play.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if  { videoView.seekTo; Utils.updateTimeFormat(tv_currentTime, progress); } } @Override public void onStartTrackingTouch(SeekBar seekBar) { uiHandler.removeMessages(UPDATE_TIME); if (!videoView.isPlaying { setPlayStatus(); videoView.start(); } } @Override public void onStopTrackingTouch(SeekBar seekBar) { uiHandler.sendEmptyMessage(UPDATE_TIME); } }); sb_volume.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, progress, 0); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }); videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { iv_playControl.setImageResource(R.drawable.play_btn_style); videoView.seekTo; sb_play.setProgress; Utils.updateTimeFormat(tv_currentTime, 0); videoView.pause(); uiHandler.removeMessages(UPDATE_TIME); } }); iv_screenSwitch.setOnClickListener(new View.OnClickListener() { @Override public void onClick { if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); iv_screenSwitch.setImageResource(R.drawable.exit_full_screen); } else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); iv_screenSwitch.setImageResource(R.drawable.full_screen); } } }); videoView.setOnTouchListener; }

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);
    }
public void onConfigurationChanged(Configuration newConfig)

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);

在此要对View的大小进行调整以适应屏幕

21. 不足:

  • 音量的手势调节并非像亮度那般流畅,后续会优化这一部分。
  • 横屏的时候,屏幕的高并没有填充整个屏幕。
@Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); screenWidth = getResources().getDisplayMetrics().widthPixels; screenHeight = getResources().getDisplayMetrics().heightPixels; if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { setSystemUiHide(); setVideoViewScale(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); iv_screenSwitch.setImageResource(R.drawable.exit_full_screen); ll_volumeControl.setVisibility(View.VISIBLE); } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { setVideoViewScale(ViewGroup.LayoutParams.MATCH_PARENT, Utils.dp2px(MainActivity.this, 240f)); iv_screenSwitch.setImageResource(R.drawable.full_screen); ll_volumeControl.setVisibility(View.GONE); setSystemUiVisible(); } }

22. Github下载地址,欢迎Star

  • Star:https://github.com/OnlyYouMyLove/VideoView

View大小调节

 /** * 设置布局大小 * * @param width 宽度 * @param height 高度 */ private void setVideoViewScale(int width, int height) { ViewGroup.LayoutParams params = rl_video.getLayoutParams(); params.width = width; params.height = height; rl_video.setLayoutParams; ViewGroup.LayoutParams layoutParams = videoView.getLayoutParams(); layoutParams.width = width; layoutParams.height = height; videoView.setLayoutParams(layoutParams); }

此外,为了使视频在全屏播放时更加和谐,调用以下方法对系统状态栏和虚拟按键进行隐藏与显示

private void setSystemUiHide() { if (Build.VERSION.SDK_INT >= 19) { View decorView = getWindow().getDecorView(); decorView.setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); } } private void setSystemUiVisible() { if (Build.VERSION.SDK_INT >= 19) { View decorView = getWindow().getDecorView(); decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); } }

此外,通过手势识别可以对亮度和音量进行调节

private void changeVolume(float offset) { int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC); int index =  (offset / screenHeight * maxVolume); int volume = Math.max(currentVolume + index, 0); volume = Math.min(volume, maxVolume); audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); sb_volume.setProgress; } private void changeBrightness(float offset) { WindowManager.LayoutParams attributes = getWindow().getAttributes(); float brightness = attributes.screenBrightness; float index = offset / screenHeight / 2; brightness = Math.max(brightness + index, WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_OFF); brightness = Math.min(WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_FULL, brightness); attributes.screenBrightness = brightness; getWindow().setAttributes(attributes); }

关于手势识别 OnGestureListener的使用在我的上一篇博客也介绍过了

此外,为了使seekBar的进度能够在用户通过音量键调节音量时也能自动变化,需要再加上一个广播接收器

/** * 音量变化广播接收器 * Created by CZY on 2017/1/31. */public class VolumeReceiver extends BroadcastReceiver { private ImageView iv_volume; private SeekBar seekBar_volume; /** * 音频管理器 */ private AudioManager audioManager; public VolumeReceiver(Context context, ImageView iv_volume, SeekBar seekBar_volume) { this.iv_volume = iv_volume; this.seekBar_volume = seekBar_volume; audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); } @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals("android.media.VOLUME_CHANGED_ACTION")) { int volume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC); if (volume == 0) { iv_volume.setImageResource(R.drawable.mute); } else { iv_volume.setImageResource(R.drawable.volume); } seekBar_volume.setProgress; } }}

为了在屏幕切换时可以不重新创建Activity而只是回调onConfigurationChanged 函数,可以为Activity增添以下属性

android:configChanges="orientation|screenSize|keyboard|keyboardHidden"

且程序要读取存储卡文件,需要申请权限

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

代码中使用到的图标我都是从一个公开图标库中下载的,个人感觉还是挺不错的网址:

这里也提高源代码下载:简单定制一个视频播放器

编辑:编程技术 本文来源:不能够不会的VideoView录制播放器,简单定制多少

关键词: