google vr 入门之制作简易的VR播放器及去除界面控制按钮

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/github_2011/article/details/62894243


创建一个Android项目,对Android studio以及项目的配置有如下的要求:

Android Studio, 2.2.2 or higher.
Version 25 of the Android SDK.
Gradle 23.0.1 or higher. Android Studio will allow you to upgrade if your installed version is too low.
A physical Android device running Android 4.4 (KitKat) or higher for Cardboard apps or a Daydream Ready phone.

这些要求是编译GVR(google vr 后面统一叫GVR) sdk sample工程的要求,基本上也是我们创建VR项目的要求(稍低一些也可以),这里需要注意一下minSdkVersion要19及以上。

在项目的根build.gradle文件中添加如下配置:

allprojects {
    repositories {
        jcenter()
        maven {
            //use google vr
            url "http://google.bintray.com/googlevr"
        }
        // You can also use a local Maven repository if desired.
        mavenLocal()
    }
}
在moudle app 的build.gradle文件中 dependencies 节点下添加如下配置:
// 使用google vr 添加的
    compile 'com.google.vr:sdk-base:1.10.0'
    compile 'com.google.vr:sdk-audio:1.10.0'
    compile 'com.google.vr:sdk-videowidget:1.10.0'//使用播放控件需要添加(VrVideoView)
做好如上配置之后,便可以写代码了。
在布局文件中添加播放器控件VrVideoView

<com.google.vr.sdk.widgets.video.VrVideoView
        android:id="@+id/video_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
在Activity中找到控件,设置播放资源即可实现播放。
mVideoView = (VrVideoView) findViewById(R.id.video_view);
VrVideoView.Options option = new VrVideoView.Options();
try {
    mVideoView.loadVideo(uri, option);
} catch (IOException e) {
    e.printStackTrace();
}
到这我们的播放器就可以播放了,最基本的播放功能就已经实现了。不过这种太简陋了,我们的播放器应该有一些控制按钮,而且是VR播放器应该有VR模式才行,不能是简单的全屏就完了。
下面我们开始正式制作我们的简易版播放器:

activity_player.xml

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

    <!--将VrVideoView设置为全屏大小-->
    <com.google.vr.sdk.widgets.video.VrVideoView
        android:id="@+id/video_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <!--控制版容器-->
    <LinearLayout
        android:id="@+id/video_progress_container"
        android:layout_width="@dimen/y1000"
        android:layout_height="@dimen/y100"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="@dimen/y100"
        android:background="@drawable/video_progress_shape"
        android:visibility="gone">

        <!--播放暂停按钮-->
        <ImageView
            android:id="@+id/play"
            android:layout_width="@dimen/x90"
            android:layout_height="@dimen/y90"
            android:layout_gravity="center_vertical"
            android:src="@mipmap/stop"/>
        <!--进度条-->
        <SeekBar
            android:id="@+id/video_progress"
            style="@style/Widget.AppCompat.ProgressBar.Horizontal"
            android:layout_width="@dimen/x600"
            android:layout_height="@dimen/y100"
            android:layout_marginLeft="@dimen/x_20"/>
        <!--时间进度 / 总时长-->
        <TextView
            android:id="@+id/video_duration"
            android:layout_width="@dimen/x240"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:textColor="@color/colorFFFFFF"/>
        <!--VR模式按钮-->
        <ImageView
            android:id="@+id/video_vr"
            android:layout_width="@dimen/x90"
            android:layout_height="@dimen/x90"
            android:layout_gravity="center_vertical"
            android:src="@mipmap/icon_vr"/>
    </LinearLayout>
</RelativeLayout>
PlayerActivity
public class PlayerActivity extends Activity implements View.OnClickListener {
    private VrVideoView mVideoView;
    private String mUrl;
    private TextView mVideoDuration;
    private SeekBar mSeekBar;
    private View mVideoPorgressContainer;
    private String mTotalDuration;//视频总时长, 00:08格式
    private ImageView mPlayView;
    private boolean isPlaying;
    private View mVideoVr;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_player);
        Intent intent = getIntent();
        mUrl = intent.getStringExtra("url");//上一个Activity传递过来的url播放地址
        initView();
        initData();
        initListener();
    }

    @Override
    protected void onNewIntent(Intent intent) {
        setIntent(intent);
        //TODO handleIntent
    }

    private void initView() {
        mVideoView = (VrVideoView) findViewById(R.id.video_view);
        mPlayView = (ImageView) findViewById(R.id.play);
        mVideoDuration = (TextView) findViewById(R.id.video_duration);
        mSeekBar = (SeekBar) findViewById(R.id.video_progress);
        mVideoPorgressContainer = findViewById(R.id.video_progress_container);
        mVideoVr = findViewById(R.id.video_vr);

        mVideoView.setInfoButtonEnabled(false);//设置左侧信息原圈不可见
        mVideoView.setFullscreenButtonEnabled(false);//设置全屏按钮不可见
        mVideoView.setStereoModeButtonEnabled(false);//设置立体眼镜模式按钮不可见
    }

    private void initData() {
        Uri uri;
        if ("".equals(mUrl)) {
            uri = Uri.parse("http://resource.vr-store.cn/appfile/6cc160ba38394af0a251d4275ea66c29.mp4");//坝上的云
        } else {
            uri = Uri.parse(mUrl);
        }
        VrVideoView.Options option = new VrVideoView.Options();
        try {
            mVideoView.loadVideo(uri, option);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void initListener() {
        mPlayView.setOnClickListener(this);
        mVideoVr.setOnClickListener(this);
        mSeekBar.setOnSeekBarChangeListener(new SeekBarListener());
        mVideoView.setEventListener(new VrVideoEventListener() {
            @Override
            public void onClick() {
                //处理控制面板的显示和隐藏
                int visibility = mVideoPorgressContainer.getVisibility();
                mVideoPorgressContainer.setVisibility(visibility == View.VISIBLE ? View.GONE : View.VISIBLE);
            }

            /**
             * Make the video mPlayView in a loop. This method could also be used to move to the next video in
             * a playlist.
             */
            @Override
            public void onCompletion() {
                //播放完成后的操作
                //mVideoView.seekTo(0);//循环播放效果
            }

            @Override
            public void onNewFrame() {
                updateVideoProgress();
            }

            @Override
            public void onLoadSuccess() {
                long duration = mVideoView.getDuration();//视频总时长,毫秒
                mTotalDuration = RegularExpress.parseDuration(duration);
                mSeekBar.setMax((int) duration);
            }

            @Override
            public void onLoadError(String errorMessage) {
                super.onLoadError(errorMessage);
                Toast.makeText(PlayerActivity.this, "加载视频失败", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onDisplayModeChanged(int newDisplayMode) {
                super.onDisplayModeChanged(newDisplayMode);
            }
        });
    }

    /**
     * 更新播放进度
     */
    private void updateVideoProgress() {
        long currentPosition = mVideoView.getCurrentPosition();
        String currentPos = RegularExpress.parseDuration(currentPosition);
        mSeekBar.setProgress((int) (currentPosition));//更新播放进度
        StringBuilder sb = new StringBuilder();
        sb.append(currentPos);
        sb.append(" / ");
        sb.append(mTotalDuration);
        mVideoDuration.setText(sb);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.play:
                performClickPlay();
                break;
            case R.id.video_vr:
                performClickVideoVr();
                break;

            default:
                break;
        }
    }

    private void performClickVideoVr() {
        mVideoView.setDisplayMode(3);//enterStereoMode,眼镜模式
    }

    /**
     * 播放暂停切换
     */
    private void performClickPlay() {
        if (isPlaying) {
            mVideoView.pauseVideo();
            mPlayView.setImageResource(R.mipmap.play);
            isPlaying = false;
        } else {
            mVideoView.playVideo();
            mPlayView.setImageResource(R.mipmap.stop);
            isPlaying = true;
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        // Prevent the view from rendering continuously when in the background.
        mVideoView.pauseRendering();
        // If the video is playing when onPause() is called, the default behavior will be to pause
        // the video and keep it paused when onResume() is called.
        isPlaying = false;
    }

    @Override
    protected void onResume() {
        super.onResume();
        // Resume the 3D rendering.
        mVideoView.resumeRendering();
    }

    @Override
    protected void onDestroy() {
        //https://developers.google.com/vr/android/reference/com/google/vr/sdk/widgets/common/VrWidgetView#shutdown()
        mVideoView.shutdown();
        super.onDestroy();
    }

    /**
     * 播放器进度条监听
     */
    private class SeekBarListener implements SeekBar.OnSeekBarChangeListener {

        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            if (fromUser) {
                mVideoView.seekTo(progress);
            }
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
            //手指离开进度条,三秒钟后隐藏控制面板
        }
    }
}
由于代码逻辑都比较简单,也都有注释,这里不再做过多的阐述。
下面介绍一下如何去除VR模式下界面上google自带的退出按钮和设置按钮。

集成过google vr或者玩过google vr sdk中提供的sample工程的童鞋都见过下面的两个按钮,在VR模式下,这两个按钮分别在页面的左上角和右上角。


去除方式多种多样,这里介绍比较简单实用的一种

在自己项目res/values/style.xml文件中添加如下样式:

<!--隐藏返回和设置按钮-->
    <style name="UiButton">
        <item name="android:layout_width">0dp</item>
        <item name="android:layout_height">0dp</item>
        <item name="android:background">@null</item>
        <item name="android:padding">0dp</item>
        <item name="android:visibility">gone</item>
    </style>
那么中间的竖线如何去掉呢?
在自己项目的res/values/values.xml文件中添加如下代码:

<!-- vr/gvr/platform/common/android/res/values/dimens.xml -->
    <eat-comment/>
    <dimen name="alignment_marker_height">0mm</dimen>
    <dimen name="alignment_marker_thickness">0dip</dimen>
    <dimen name="transition_bottom_bar_height">112dip</dimen>
效果如下:

为什么我们写一个style属性就能去掉返回和设置按钮呢?

查找源码发现com.google.vr:sdk-base1.10.0中layout文件夹下面有几个布局文件

back_button.xml

<?xml version="1.0" encoding="utf-8"?>

<!-- Back button for portrait orientation with left-to-right locale -->
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
  style="@style/UiButton"
  android:src="@drawable/quantum_ic_close_white_24"
  android:layout_alignParentRight="true"
  android:layout_alignParentTop="true"/>
settings_button.xml
<?xml version="1.0" encoding="utf-8"?>

<!-- Settings button for portrait orientation with left-to-right locale -->
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
  style="@style/UiButton"
  android:src="@drawable/quantum_ic_settings_white_24"
  android:contentDescription="@string/settings_button_content_description"
  android:layout_alignParentRight="true"
  android:layout_alignParentBottom="true"/>
这两个布局分别是返回键和设置按钮的布局文件,它们都共用同一个style样式:UiButton
ui_layer_with_portrait_support.xml

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

    <!-- UiLayer layout when portrait support is enabled.
         See corresponding file in layout-land/ui_layer_with_portrait_support.xml

         Portrait orientation while in VR mode should be reserved for special cases,
         such as widgets for fast transitions between embed and full-screen VR.
         See b/26580727 for details.
    -->

    <include layout="@layout/back_button" android:id="@+id/ui_back_button"/>

    <RelativeLayout
      android:id="@+id/ui_alignment_marker"
      android:layout_height="@dimen/alignment_marker_thickness"
      android:layout_width="@dimen/alignment_marker_height"
      android:layout_centerVertical="true"
      android:layout_alignParentLeft="true"
      android:background="@color/alignment_marker_color">
    </RelativeLayout>

    <include layout="@layout/settings_button" android:id="@+id/ui_settings_button"/>

</RelativeLayout>
这个布局文件是VR模式下GVR控制面板的布局(一个返回按钮、一个设置按钮、中间竖线)
中间竖线的宽高分别是alignment_marker_height和alignment_marker_thickness,这就是我们在values.xml文件中把它们俩都写为0的原因。

这种处理隐藏GVR按钮的方式在原生的Android GVR项目中适用,同时在Unity导出的Android GVR项目中也同样适用。

源码下载地址


应用下载地址(Android apk应用)


这篇只是Google VR入门的博客,想要了解更多吗?

google vr 入门之制作简易的VR播放器(二)

没有更多推荐了,返回首页