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();}}
@Overridepublic 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;@Overrideprotected 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;}}@Overridepublic 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:}}@Overrideprotected 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;@Overrideprotected 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*/@NonNullprivate 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() {@Overridepublic 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();//停止刷新UImHandler.removeMessages(UPDATE_UI);} else {mBtStartPause.setText("Pause");mVvVideoView.start();//开启刷新UImHandler.sendEmptyMessage(UPDATE_UI);}break;}}
    @Overrideprotected void onPause() {super.onPause();//停止刷新UImHandler.removeMessages(UPDATE_UI);}

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

    private void synchScrollSeekBarAndTime() {mSbProgressSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {//进度改变的时候同步Time@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {updateTime(mTvTimeCurrent, progress);}//拖动的时候关闭刷新机制@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {mHandler.removeMessages(UPDATE_UI);}//拖动停止同步VideoView和开启刷新机制@Overridepublic 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*/@Overridepublic 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转为pxelse {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*/@Overridepublic 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);//横屏的时候为trueisFullScreen = true;}//当屏幕方向是竖屏的时候,竖屏的时候的高我们需要把dp转为pxelse {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);}@Overrideprotected 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.MyVideoViewandroid: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() {@Overridepublic 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

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

VideoView实现安卓视频播放相关推荐

  1. 安卓视频播放器(VideoView)

    VideoView是安卓自带的视频播放器类,该类集成有显示和控制两大部分,在布局文件中添加VideoView然后在java文件中简单的调用控制命令,即可实现本地或者网络视频的播放.本章实现视频的居中播 ...

  2. unity android 播放器,Unity3D 安卓视频播放插件 WRP Android Video Player Pro

    通过这个安卓视频播放插件,你可以在你的Unity3D 项目中针对很容的播放视频. Easily play videos in your Unity Android Projects with this ...

  3. Android Video Player. 安卓视频播放器,封装 MediaPlayer、ExoPlayer、IjkPlayer。模仿抖音,悬浮播放,广告播放,列表播放,弹幕

    DKVideoPlayer 项目地址:dueeeke/DKVideoPlayer 简介: Android Video Player. 安卓视频播放器,封装 MediaPlayer.ExoPlayer. ...

  4. android电影播放器,安卓视频播放器哪个好 五款主流视频播放器对比

    如今手机的流行趋势是屏幕越来越大,从3.5寸到3.7寸再到现在不少主流的安卓手机所采用的4.3寸屏幕,用手机来看电影已经是一项在我们日常生活中使用频率非常高的功能.而手机处理器性能的进化也让我们可以轻 ...

  5. 安卓视频播放器 一行代码快速实现视频播放,Android视频播放,AndroidMP3播放,安卓视频播放一行代码搞定,仿今日头条 Android视频播放器

    一行代码快速实现视频播放,Android视频播放,AndroidMP3播放,安卓视频播放一行代码搞定,真正实现Android的全屏功能 github地址:https://github.com/qius ...

  6. android关于VideoView或Vitamio视频播放器横竖屏切换

    之前在网上寻求帮助时,留有QQ,好多人加我问过这个问题,现在为了方便大家,我将android关于VideoView或Vitamio视频播放器横竖屏切换的代码放在这里需要的朋友可以看一下,当然我做的这个 ...

  7. 安卓视频播放器(TV)

    最近写了一款视频播放器的Demo,基于安卓智能电视的(电视与手机的程序大同小异),各种功能都已经完善,特此分享给大家. https://github.com/Vashonisonly/VideoPla ...

  8. 利用VideoView简单实现视频播放 包括 横竖屏切换 声音 亮度 暂停

    利用Google给的原组件VideoView来简单的实现视频播放.包括播放,暂停,横竖屏切换,声音的改变,屏幕亮度的改变,当声音改变时,会自动调用系统给的声音条,而当滑动亮度的时候需要自己给写个see ...

  9. MX Player Pro 1.16.5 去广告版 — 安卓视频播放器

    公众号:潮软件 软件介绍 MX Player Pro是一款安卓版的视频播放器,功能十分强大,是安卓设备上功能最为强大的一款播放器.并且具有多核心的译码能力来处理你的影片档案和字幕.多核译码功能,它能够 ...

最新文章

  1. python lxml 安装及应用
  2. excel 等额本息还款每期本息计算_零基础入门融资租赁计算(第三讲)——设计租金方案...
  3. php扩展兼容,PHP扩展迁移为PHP7扩展兼容性问题记录,php7兼容性_PHP教程
  4. 课程设计---图书登记管理系统
  5. signature=42f2498bc8fd40eb63568566c79f37e7,新思维综合英语Ⅰ学习指导
  6. WinForm窗体拖动代码
  7. 线段树位运算的三种操作(|,^,)
  8. python面试题汇总(1)
  9. anaconda3安装_Anaconda3软件安装教程
  10. [地产]“用90%的时间考虑失败”——李嘉诚(长江实业集团董事长)
  11. CVPR 2020|超越H.265,中科大使用多帧数据改进视频压缩新方法
  12. Oracle 11G 64位发布出现错误
  13. c语言open不同的编码格式,C语言中open与fopen的区别
  14. 判断一个单链表是否有环及环的链接点
  15. 看看人家那后端API接口写得,那叫一个牛逼,再看看我的,像坨屎!
  16. C语言的新扩展typeof
  17. 计算机中职课程表,计算机专业课程表
  18. 深度置信网络基础知识及程序代码
  19. 2020总结 2021规划
  20. win10使用电池时关闭自动调节亮度

热门文章

  1. 机器学习算法:UMAP 深入理解
  2. 《大学之路》 学习笔记
  3. java centos 缩略图_使用 Nginx 的 image_filter 模块来构建动态缩略图服务器
  4. 集成学习5-Xgboost原理与调参
  5. Kindle电子书的用到的几种文件
  6. 数组中最大第K元素(快排思想)
  7. 特别好用,在线就能画原型的工具!
  8. window.print()打印html网页的两种方法
  9. gem5和NVM的搭建(完整版)
  10. 什么是网络运维工程师? 就业前景好吗?