天天看點

VideoView實作安卓視訊播放

1. VideoView簡介

  • Android實作視訊播放主要是使用VideoView類來實作的。
  • VideoView背後是使用MediaPlayer來對視訊檔案進行控制的。
  • 隻支援mp4、avi、3gp格式的視訊,支援格式單一。

2. VideoView常用方法:

  • setVideoPath:設定要播放的視訊檔案的位置
  • start:開始或繼續播放視訊
  • pause:暫停播放視訊
  • resume:将視訊從頭開始播放
  • seekTo:從指定的位置開始播放視訊
  • isPlaying:判斷目前是否正在播放視訊
  • getDuration:擷取載入的視訊檔案的時長

3. VideoView播放視訊的小栗子:

  • 添加網絡和SD卡權限:
  • 添加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}, );
        } 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:
        }
    }
           
  • 初始化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}, );
        } 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小栗子

  • 添加網絡和SD卡權限:
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
           
  • 添加VideoView布局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <VideoView
        android:id="@+id/vv_videoView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</RelativeLayout>
           
  • 初始化本地或網絡播放路徑
private void initVideoPath() {
        String path_local = Environment.getExternalStorageDirectory().getAbsolutePath() + "/vivo.mp4";
        //本地播放
        mVvVideoView.setVideoPath(path_local);
        //網絡播放
//      mVvVideoView.setVideoURI(Uri.parse(...);
    }

提示:網絡測試的話Tomcat下webapps下面放vivo.mp4
           
  • videoView和MediaController進行綁定
private void initBind() {
        MediaController mediaController = new MediaController(this);
        mVvVideoView.setMediaController(mediaController);
        mediaController.setMediaPlayer(mVvVideoView);
    }

注意:必須互相設定進行綁定
           
  • 暫時設定為橫屏:
<activity
        android:name=".MainActivity"
        android:screenOrientation="landscape">
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>

            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
    </activity>
           
  • Activity完整代碼:
public class MainActivity extends AppCompatActivity {

    @BindView(R.id.vv_videoView)
    VideoView mVvVideoView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        initVideoPath();
        initBind();
    }

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

    /**
     * 初始化本地或網絡播放路徑
     */
    private void initVideoPath() {
//        mVvVideoView.setVideoPath(getLocalPath());
        mVvVideoView.setVideoURI(Uri.parse("http://192.168.0.108:8080/video/vivo.mp4"));
    }

    /**
     * 擷取本地路徑
     *
     * @return
     */
    @NonNull
    private String getLocalPath() {
        return new File(Environment.getExternalStorageDirectory(), "vivo.mp4").getPath();
    }

}
           

6. 自定義UI界面

  • VideoView需要添加播放暫停快進快退進度條等等按鈕,而MdeiaController自帶了播放暫停快進快退進度條等UI界面,但是這些都不是我們想要的,因為UI太過于簡陋,是以為了美觀,以及需求的多樣化,我們需要定義我們自己的UI,通過VideoView自身的邏輯來實作視訊播放。
  • 修改布局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <RelativeLayout
        android:id="@+id/rl_videolayout"
        android:layout_width="wrap_content"
        android:layout_height="match_parent">

        <VideoView
            android:id="@+id/vv_videoView"
            android:layout_width="match_parent"
            android:layout_height="240dp"
            />

        <LinearLayout
            android:id="@+id/ll_controllerBar_layout"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:layout_alignParentBottom="true"
            android:orientation="vertical">
            <!--進度條-->
            <SeekBar
                android:id="@+id/sb_progress_seekbar"
                android:layout_width="match_parent"
                android:layout_height="5dp"
                android:layout_marginLeft="-20dp"
                android:layout_marginRight="-20dp"
                android:indeterminate="false"
                android:max="100"
                android:progress="20"
                android:progressDrawable="@drawable/seekbar_style_pro"
                />

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="#101010"
                android:gravity="center_vertical">

                <LinearLayout
                    android:id="@+id/ll_left_layout"
                    android:layout_width="wrap_content"
                    android:layout_height="match_parent"
                    android:gravity="center_vertical"
                    android:orientation="horizontal">
                    <!--播放暫停-->
                    <Button
                        android:id="@+id/bt_start_pause"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="10dp"
                        android:text="Pause"/>
                    <!--現在的時間-->
                    <TextView
                        android:id="@+id/tv_time_current"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="10dp"
                        android:text="00:00:00"
                        android:textColor="#FFF"
                        android:textSize="20sp"/>
                    <!--斜杠-->
                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="5dp"
                        android:text="/"
                        android:textColor="#4C4C4C"
                        android:textSize="5dp"/>
                    <!--總共的時間-->
                    <TextView
                        android:id="@+id/tv_time_total"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="5dp"
                        android:text="00:00:00"
                        android:textColor="#4C4C4C"
                        android:textSize="20sp"/>
                </LinearLayout>

                <LinearLayout
                    android:id="@+id/ll_right_layout"
                    android:layout_width="wrap_content"
                    android:layout_height="match_parent"
                    android:layout_alignParentRight="true"
                    android:layout_marginRight="10dp"
                    android:layout_toRightOf="@id/ll_left_layout"
                    android:gravity="center_vertical|right"
                    android:orientation="horizontal">

                    <TextView
                        android:id="@+id/tv_vol_name"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="20dp"
                        android:text="Vol"
                        android:textColor="#FFF"
                        android:textSize="20sp"
                        android:visibility="gone"/>
                    <!--音量-->
                    <SeekBar
                        android:id="@+id/sb_vol_seekbar"
                        android:layout_width="143dp"
                        android:layout_height="5dp"
                        android:layout_marginLeft="5dp"
                        android:indeterminate="false"
                        android:max="100"
                        android:progress="20"
                        android:progressDrawable="@drawable/seekbar_style_pro"
                        android:visibility="gone"/>

                    <View
                        android:id="@+id/v_line"
                        android:layout_width="1dp"
                        android:layout_height="match_parent"
                        android:layout_marginBottom="5dp"
                        android:layout_marginLeft="5dp"
                        android:layout_marginTop="5dp"
                        android:background="#1E1E1E"
                        android:visibility="gone"></View>
                    <!--橫豎屏切換-->
                    <Button
                        android:id="@+id/bt_switch"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="5dp"
                        android:text="Switch"/>
                </LinearLayout>
            </RelativeLayout>
        </LinearLayout>
    </RelativeLayout>
</RelativeLayout>
           
<!--seekbar_style_pro:-->

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:id="@android:id/background">
        <shape>
            <solid android:color="#707070"></solid>
            <size android:height="5dp"></size>
        </shape>
    </item>
    <item android:id="@android:id/progress">
        <clip>
            <shape>
                <solid android:color="#B94310"></solid>
                <size android:height="5dp"></size>
            </shape>
        </clip>
    </item>
</layer-list>
           
<!--seekbar_style_vol-->

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:id="@android:id/background">
        <shape>
            <solid android:color="#101010"></solid>
        </shape>
    </item>
    <item android:id="@android:id/progress">
        <clip>
            <shape>
                <solid android:color="#ffb97244"></solid>
            </shape>
        </clip>
    </item>
</layer-list>
           

7. 删除以下MediaController的邏輯:

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

8. 初始化控件:

//需要豎屏隐藏的音量title
    @BindView(R.id.tv_vol_name)
    TextView mTvVolName;
    //徐奧豎屏隐藏的音量分割線
    @BindView(R.id.v_line)
    //最外層的布局
    @BindView(R.id.rl_videolayout)
    RelativeLayout mRlVideolayout;
    //VideoView
    @BindView(R.id.vv_videoView)
    VideoView mVvVideoView;
    //程序進度條
    @BindView(R.id.sb_progress_seekbar)
    SeekBar mSbProgressSeekbar;
    //播放 暫停
    @BindView(R.id.bt_start_pause)
    Button mBtStartPause;
    //現在的時間
    @BindView(R.id.tv_time_current)
    TextView mTvTimeCurrent;
    //總共的時間
    @BindView(R.id.tv_time_total)
    TextView mTvTimeTotal;
    //音量進度條
    @BindView(R.id.sb_vol_seekbar)
    SeekBar mSbVolSeekbar;
    //全屏切換開關
    @BindView(R.id.bt_switch)
    Button mBtSwitch;
    //控制區域
    @BindView(R.id.ll_controllerBar_layout)
    LinearLayout mLlControllerBarLayout;
    //控制區域左半邊
    @BindView(R.id.ll_left_layout)
    LinearLayout mLlLeftLayout;
    //控制區域右半邊
    @BindView(R.id.ll_right_layout)
    LinearLayout mLlRightLayout;
           

9. 播放和暫停邏輯

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

10. 定義格式時間的方法

/**
     * 時間的格式化
     * @param textView
     * @param millisecond
     */
    public void updateTime(TextView textView, int millisecond) {
        int second = millisecond / ; //總共換算的秒
        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. 自動橫豎屏切換

  • 删掉強制橫屏的代碼:
  • 防止橫豎屏切換重建Activity:配置檔案添加
  • 防止橫屏的時候視訊右邊有大量的空白:
//定義兩個變量:代表目前螢幕的寬和螢幕的高
    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;
           
  • 調節音量方法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, ) * (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

繼續閱讀