Android 简单定制一个视频播放器

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

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/content_main2"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

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

</RelativeLayout>

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

@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(this);
        videoView.setMediaController(mediaController);
        mediaController.setMediaPlayer(videoView);
        //为videoView设置视频路径
        String path = Environment.getExternalStorageDirectory().getAbsolutePath();
        videoView.setVideoPath(path + "/00.mp4");
    }

播放效果如下:

这里写图片描述

这里再来自定义视频播放控制界面与控制逻辑,增添音量调节,亮度调节,沉浸式状态栏等功能

竖屏状态效果如下:

这里写图片描述

横屏状态下效果如下:

这里写图片描述

首先要先设计布局样式

<?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">

    <RelativeLayout
        android:id="@+id/rl_video"
        android:layout_width="match_parent"
        android:layout_height="240dp">

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

        <LinearLayout
            android:id="@+id/ll_control"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignBottom="@id/vv_player"
            android:background="#8768423e"
            android:orientation="vertical">

            <SeekBar
                android:id="@+id/sb_play"
                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:id="@+id/ll_playControl"
                    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:id="@+id/iv_playControl"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:clickable="true"
                        android:src="@drawable/play_btn_style" />

                    <TextView
                        android:id="@+id/tv_currentTime"
                        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:id="@+id/tv_totalTime"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="00:00:00"
                        android:textColor="#ef6363"
                        android:textSize="15sp" />

                </LinearLayout>

                <ImageView
                    android:id="@+id/iv_screenSwitch"
                    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:id="@+id/ll_volumeControl"
                    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:id="@+id/iv_volume"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:src="@drawable/volume" />

                    <SeekBar
                        android:id="@+id/sb_volume"
                        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>

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

主要代码如下: 初始化UI

private void initUI() {
        videoView = (VideoView) findViewById(R.id.vv_player);
        sb_play = (SeekBar) findViewById(R.id.sb_play);
        sb_volume = (SeekBar) 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 = (TextView) findViewById(R.id.tv_currentTime);
        tv_totalTime = (TextView) 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));
    }

初始化各种事件

private void initEvent() {
        iv_playControl.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                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 (fromUser) {
                    videoView.seekTo(progress);
                    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(0);
                sb_play.setProgress(0);
                Utils.updateTimeFormat(tv_currentTime, 0);
                videoView.pause();
                uiHandler.removeMessages(UPDATE_TIME);
            }
        });
        iv_screenSwitch.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                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(this);
    }

在横屏和竖屏切换时,会回调以下方法

public void onConfigurationChanged(Configuration newConfig)

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

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

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(params);
        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 = (int) (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(volume);
    }

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

}

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

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

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

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

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

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android开发指南

5.AutoCompleteTextView、自定义广播

2616
来自专栏向治洪

android 特卖列表倒计时卡顿问题

在Android的开发中,我们经常遇见倒计时的操作,通常使用Timer和Handler共同操作来完成。当然也可以使用Android系统控件CountDownTi...

2369
来自专栏向治洪

toggbutton

2013年8月14日Android记录 很多应用都会有用户设置,用户的一些偏好可以由用户来决定那是应用人性化的体现,在实际开发中很多情况都作成可配置的了,本篇博...

2209
来自专栏指尖下的Android

Android WebView 踩坑之路

833
来自专栏向治洪

slidingmenu开源效果

本文将更进一步, 在很多实际的应用场景中我们需要一个更复杂的场景,比如说需要在一个菜单选项中集成多个tab来集中显示信息。这个时候 Viewpager就派上用场...

2255
来自专栏向治洪

仿今日头条滑动评论效果

开发中碰到问题之后实现的,觉得可能有的开发者用的到或则希望独立成一个小功能DEMO,所以就放出来这么一个DEMO。 原本觉得是最后完成后发网站客户端的,可是这样...

1925
来自专栏项勇

笔记40 | Android通讯之Intent的简单使用

1757
来自专栏Android开发指南

6.listview显示不用条目

34010
来自专栏酷玩时刻

Android极速开发之手机屏幕

755
来自专栏向治洪

Android实现仿支付宝流水

今天给大家讲的是如何自定义下拉的ListView实现支付宝账单的效果,月份是需要悬浮的,然后没一个月归为一类,先看一个效果图吧。 ? 场景:后台下发的数据就是一...

3948

扫码关注云+社区