1. VideoView簡介
- Android實作視訊播放主要是使用VideoView類來實作的。
- VideoView背後是使用MediaPlayer來對視訊檔案進行控制的。
- 隻支援mp4、avi、3gp格式的視訊,支援格式單一。
2. VideoView常用方法:
- setVideoPath:設定要播放的視訊檔案的位置
- start:開始或繼續播放視訊
- pause:暫停播放視訊
- resume:将視訊從頭開始播放
- seekTo:從指定的位置開始播放視訊
- isPlaying:判斷目前是否正在播放視訊
- getDuration:擷取載入的視訊檔案的時長
3. 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>
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}, );
} else {
initVideoPath();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case :
if (grantResults.length > && grantResults[] == PackageManager.PERMISSION_GRANTED) {
initVideoPath();
} else {
Toast.makeText(this, "拒絕權限将無法使用程式", Toast.LENGTH_SHORT).show();
finish();
}
break;
default:
}
}
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}, );
} 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 :
if (grantResults.length > && grantResults[] == 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小栗子
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<?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>
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 / ; //總共換算的秒
int hh = second / ; //小時
int mm = second % / ; //分鐘
int ss = second % ; //時分秒中的秒的得數
String str = null;
if (hh != ) {
//如果是個位數的話,前面可以加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 = ;
/**
* 定義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, );
}
}
};
12. 初始化播放和重新整理時間機制
private void initVideoPlay() {
mVvVideoView.start();
//第一個參數是标志,第二個參數是重新整理間隔時間
mHandler.sendEmptyMessageDelayed(UPDATE_UI, );
}
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. 自動橫豎屏切換
//定義兩個變量:代表目前螢幕的寬和螢幕的高
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,));
}
}
/**
* 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 + f);
}
/**
* 根據手機的分辨率從 px(像素) 的機關 轉成為 dp
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + f);
}
/**
* 将px值轉換為sp值,保證文字大小不變
*/
public static int px2sp(Context context, float pxValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (pxValue / fontScale + f);
}
/**
* 将sp值轉換為px值,保證文字大小不變
*/
public static int sp2px(Context context, float spValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + f);
}
}
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, ));
//豎屏的時候吟唱
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 = ;
private int screen_height = ;
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 / ) {
/**
* 調節亮度
*/
if (moveY > ) {
//降低亮度
} else {
//升高亮度
}
changeBright(-moveY);
//手勢在右邊
} else {
Log.e("Emove", "onTouch: " + "手勢在右邊");
/**
* 調節音量
*/
if (moveY > ) {
//減小音量
} 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 * );
int volume = Math.max(current + index, );
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, );
mSbVolSeekbar.setProgress(volume);
}
/**
* 調節亮度:
*/
private void changeBright(float moveY) {
WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
mBrightness = layoutParams.screenBrightness;
float index = moveY / screen_height / ;
mBrightness += index;
//做臨界值的判斷
if (mBrightness > f) {
mBrightness = f;
}
if (mBrightness < ) {
mBrightness = f;
}
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;
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, ) * (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, ) *mBrightness);
mOperationPercent.setLayoutParams(layoutParams);
21. 不足:
- 音量的手勢調節并非像亮度那般流暢,後續會優化這一部分。
- 橫屏的時候,螢幕的高并沒有填充整個螢幕。
22. Github下載下傳位址,歡迎Star
- Star:https://github.com/OnlyYouMyLove/VideoView