VR小项目(三)
17.打开上面上面创建好的VrVideoFragment并继承我们自己写的BaseFragment并实现他的两个方法
/** * Date:2017/3/18 * author:陈箫阳ChenXiaoYang * furction: */ public class VrVideoFragment extends BaseFragment { private RecyclerView recyclerView; @Override public RecyclerView.LayoutManager getLayoutManager() { //VR视频使用GridView的样式,参数 1.上下文 2.决定一行几列 return new GridLayoutManager(getActivity(), 2); } //在这里我们要复写onViewCreated方法 @Override public void onViewCreated(View view, Bundle savedInstanceState) { recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view); //从接口中拿到LayoutManager RecyclerView.LayoutManager loaderManager = getLayoutManager(); //设置LayoutManager,让子类不用再设置 recyclerView.setLayoutManager(loaderManager); //使用开源框架OKGO,加载网址 OkGo.get(ApiUrls.URL_Query) } @Override protected RecyclerView.Adapter getAdapter() { return null; } }
在这里ApiUrls会报红,我们需要使用快捷键创建ApiUrls类用于存放网络数据,然后将Apiurls放入utli包中
/** * Date:2017/3/19 * author:陈箫阳ChenXiaoYang * furction: */ public class ApiUrls { public static String API_HOST = "api.youkes.com"; public static int API_PORT = 8081; public static String URL_Query = "http://" + API_HOST + ":" + API_PORT + "/api/video/query"; }
此时回到VrVideoFragment类
在这里我们要使用JSON需要导入:com.alibaba:fastjson:1.2.29包
步骤:按住control+alt+shift+s会弹出一个Project Structure点击所要添加的项目,点击Dependencies,点击+号,选择Library Dependency
步骤:按住control+alt+shift+s会弹出一个Project Structure点击所要添加的项目,点击Dependencies,点击+号,选择Library Dependency
.cacheKey(ApiUrls.URL_Query)//默认加载键 .cacheMode(CacheMode.DEFAULT)//采用默认格式 .execute(new StringCallback() { //网络请求成功的回调 @Override public void onSuccess(String s, Call call, Response response) { try { //创建原生的JSONObject来解析数据 JSONObject obj = new JSONObject(s); String content = obj.getString("content"); //然后报解析的数据先放在Bean类,再放到集合里,这里我们用Gson,fastJson会更方便 List<VideoItem> videoItems = JSON.parseArray(content, VideoItem.class); //给RecyclerView设置适配器,把数据集合传到适配器里 recyclerView.setAdapter(new VrVideoAdapter(videoItems)); } catch (JSONException e) { e.printStackTrace(); } } });在这里List集合的VideoItem会报红,使用快捷键创建VideoItem类, VrVideoAdapter也会报红,使用快捷键创建VrVideoAdapter适配器类实现一个内部抽象方法然后在让他调用父类的方法
/** * Date:2017/3/19 * author:陈箫阳ChenXiaoYang * furction: */ public class VideoItem { public String vtype; public String id; public String key; public String play; public String title; public String img; public String url; public String isbin; public String t0; public String t1; public String t2; public String userName; public String userNick; public String userPhoto; public String vclass; public int width; public int height; public float score0; public long date; public String _id; public String userId; public String[] tags; public int cnt; public String type; public String videoChannelName; public String videoChannelId; public String text; public String textSimple; public String dateCnSimple; }
/** * Date:2017/3/17 * author:陈箫阳ChenXiaoYang * furction:VR视频模块RecyclerView所需适配器 */ public class VrVideoAdapter extends BaseQuickAdapter<VideoItem> { //通过构造方法,加载一个默认布局及获取传过来的数据 public VrVideoAdapter(List<VideoItem> videoItems) { super(R.layout.vr_video_list_item, videoItems); } @Override protected void convert(BaseViewHolder helper, VideoItem item) { } }
在这里R.layout.vr_video_list_item会报红,使用快捷键创建vr_video_list_item布局文件,显示一些简单的布局,用到了一些颜色需要以下步骤
1.打开res下的values包再打开colors布局文件添加一些颜色
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
<!--vr_video_list_item里自定义的颜色-->
<color name="white_light">#f2f2f2</color>
<color name="white">#fafafa</color>
<color name="gray_dark_blue">#5A6377</color>
<color name="gray_light">#efefef</color>
<color name="black">#ff000000</color>
<color name="gray_less_light">#666666</color>
<color name="gray">#777</color>
<color name="gray_dark">#383838</color>
<color name="green_light">#8e9ea4</color>
<color name="green">#34515c</color>
<color name="green_dark">#1e3e4a</color>
<color name="night_mask">#90000000</color>
<color name="gray_darker">#2b2b2b</color>
<color name="gray_lighter">#efefef</color>
<color name="gray_alpha_light">#90999999</color>
<color name="semitransparent_light">#20000000</color>
<color name="white_dark">#f3f3f3</color>
</resources>
2.在drawable下创建select_button.xml布局文件
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:drawable="@color/white_light"></item> <item android:drawable="@color/white"></item> </selector>
回到VrVideoAdapter适配器类,
注意:这里特别要注意的是SimpleDateFormat
com.ibm.icu.text.SimpleDateFormat与java.text.SimpleDateFormat比较
1.fomat()方法,前者比后者大一小时
2.parse()方法,前者比后者小一小时
3.当然 java.text.SimpleDateFormat得到的结果是正确的
com.ibm.icu.text.SimpleDateFormat是ICU4J中的一个类。ICU4J 是IBM的国际化开发组件ICU 的Java语言实现版本。为何会发生这种怪异现象,我也没搞清楚,初步猜测是因为ibm这个包的默认时区不同。所以大家用ICU这个SimpleDateFormat时应该注意导入是哪个包
//标签TextView的控件Id private int[] ids = {R.id.tag0, R.id.tag1, R.id.tag2}; @Override protected void convert(BaseViewHolder helper, VideoItem item) { //设置Item的标题,从bean类里拿对象的数据,直接就可以根据控件ID设置进去,这是开源框架的便利 helper.setText(R.id.topic_init_title, item.title); //设置Item的时间,使用simpleDateFormat(java的知识点),对时间数据进行格式化 helper.setText(R.id.date_text, new SimpleDateFormat("MM/DD/yyyy").format(item.date)); //根据控件ID获取到控件对象 ImageView topicImg = helper.getView(R.id.topic_init_img); //使用开源框架,根据网址加载图片 Glide.with(helper.getConvertView().getContext())//获取上下文 .load(item.img)//网址 .into(topicImg);//ImageView控件对象 //从bean类里拿到标签 String[] tags = item.tags; //根据标签的数量,做对应的循环操作,把标签设置到TextView文本里 for (int x = 0; x < tags.length; x++) { //设置文本,参数 1 数组里的ID 2 要塞入的数据 helper.setText(ids[x], tags[x]); } }到此VR视屏的页面就制作好了
效果图
18.
接下来就要编写VR视屏的点击事件了
首先在convert方法中获取item并设置点击事件
//B.获取到item容器的ID值,设置点击事件 View view = helper.getView(R.id.video_layout); //使用设置标签的形式,以控件为容器,把数据放入其中,进行传递 view.setTag(item); //设置点击事件 view.setOnClickListener(listener);
在这里listener会报红,这时我们需要在convert方法外创建私有的listener点击事件
//B.点击事件的处理 private View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View v) { //从控件里取出数据,进行强类型转换 VideoItem item = (VideoItem) v.getTag(); //创建Intent对象, 参数 1.上下文 2.类字节,显示播放详情类 Intent intent = new Intent(v.getContext(), VideoDeTailActivity.class); //需要传递的数据,把数据放入Intent,进行传输 intent.putExtra("title", item.title); intent.putExtra("img", item.img); intent.putExtra("text", item.text); intent.putExtra("play", item.play); //获取上下文,开启Activity的跳转 v.getContext().startActivity(intent); } };
19.在这里VideoDeTailActivity类会报红,因为还没有这个跳转的类,所以我们要使用快捷键创建VideoDeTailActivity类并创建activity_video_de_tail布局文件
首先编写布局文件
首先编写布局文件
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/video_detail" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/white"> <ScrollView android:layout_width="fill_parent" android:layout_height="fill_parent"> <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> <LinearLayout android:id="@+id/control_bar" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal" android:padding="2dip"> <TextView android:id="@+id/title_text" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_margin="8dip" android:text="" android:textSize="16sp"/> </LinearLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="200dp" android:layout_margin="4dp" android:background="@color/gray_lighter" android:orientation="horizontal"> <ImageView android:id="@+id/detail_img_view" android:layout_width="match_parent" android:layout_height="200dp" android:background="@color/black" android:clickable="true" android:scaleType="centerCrop" android:src="@mipmap/pictures_no"/> <TextView android:id="@+id/video_type" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@color/semitransparent_light" android:padding="8dp" android:text="视频" android:textColor="@color/white" android:textStyle="bold"/> <ImageButton android:id="@+id/play_link" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:background="#22222222" android:contentDescription="播放语音" android:gravity="center" android:src="@drawable/mediacontroller_play_button"/> </RelativeLayout> <TextView android:id="@+id/detail_text" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_margin="8dip" android:textColor="@color/gray_less_light" android:textSize="16sp"/> </LinearLayout> </ScrollView> </FrameLayout>
在这里mediacontroller_play_button会报红,使用快捷键创建布局文件,在这里有一些图片
在drawable下创建mediacontroller_play_button.xml布局文件
在drawable下创建mediacontroller_play_button.xml布局文件
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:drawable="@mipmap/mediacontroller_play02"/> <item android:drawable="@mipmap/mediacontroller_play01"/> </selector>
20.回到VideoDeTailActivity类,接收跳转数据
public class VideoDeTailActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_video_de_tail); init(); } //初始化数据 private void init() { //拿到传过来的Intent对象 final Intent intent = getIntent(); //从Intent对象里,根据键值,拿到我们所需要的数据 String title = intent.getStringExtra("title"); String img = intent.getStringExtra("img"); String text = intent.getStringExtra("text"); final String play = intent.getStringExtra("play"); //方别把拿到的数据放入对应的控件里,设置标题 TextView mTextView = (TextView) findViewById(R.id.title_text); mTextView.setText(title); //用Glide设置图片 ImageView ivImg = (ImageView) findViewById(R.id.detail_img_view); Glide.with(this)//上下文 .load(img)//UI地址 .into(ivImg);//ImageView控件对象 //设置影片介绍文本 TextView tv_detail = (TextView) findViewById(R.id.detail_text); tv_detail.setText(text); //找到ImageButton控件对象,设置点击事件 findViewById(R.id.play_link).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent vrIntent = new Intent(VideoDeTailActivity.this, VideoPlayerActivity.class); //使用Intent传输所要传的值 vrIntent.putExtra("play", play); //开启界面 startActivity(vrIntent); } }); } }
在这里VideoPlayerActivity会报红因为没有这个跳转类,所以创建VideoPlayerActivity类并创建activity_video_player.xml布局文件
首先编写activity_video_player.xml
首先编写activity_video_player.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.google.vr.sdk.widgets.video.VrVideoView android:id="@+id/vr_video" android:layout_width="match_parent" android:layout_height="250dp"/> <android.support.v7.widget.AppCompatSeekBar android:id="@+id/seek_bar" android:layout_width="match_parent" android:layout_height="wrap_content" /> <TextView android:id="@+id/tv_progress" android:textSize="20dp" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout>
VR视屏要导入VR资源包
打开gvr-android-sdk-master打开libraries
打开gvr-android-sdk-master打开libraries
在Studio中导入
步骤:点击File<New<Improp module粘贴文件路径点击Finish
步骤:点击File<New<Improp module粘贴文件路径点击Finish
然后按住control+alt+shift+s会弹出一个Project Structure点击所要添加的项目,点击Dependencies,点击+号,选择Module Dependency
最后回到VideoPlayerActivity类获取跳转内容,并编写VR视屏界面
public class VideoPlayerActivity extends AppCompatActivity { private VrVideoView vr_video; private SeekBar seek_bar; private TextView tv_progress; private VideoLoadTask mVideoLoadTask; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_video_player); //获取传过来的Intent Intent intent = getIntent(); //从Intent里拿到我们的数据 String play = intent.getStringExtra("play"); //A.进行控件的初始化 vr_video = (VrVideoView) findViewById(R.id.vr_video); seek_bar = (SeekBar) findViewById(R.id.seek_bar); tv_progress = (TextView) findViewById(R.id.tv_progress); //隐藏VR效果左下角的信息按钮显示 vr_video.setInfoButtonEnabled(false); //切换VR的模式 参数:VrVideoView.DisplayMode.FULLSCREEN_STEREO:设备模式(手机横着放试试)VrVideoView.DisplayMode..FULLSCREEN_MONO手机模式 vr_video.setDisplayMode(VrVideoView.DisplayMode.FULLSCREEN_STEREO); //D.对VR视频进行事件监听 vr_video.setEventListener(new MyEventListener()); //B.播放VR效果,只需执行异步任务即可 mVideoLoadTask = new VideoLoadTask(); mVideoLoadTask.execute(play); } //B.由于VR资源数据量大,获取需要时间,故把加载视频放到子线程中进行,主线程来显示,可以使用一个异步线程AsyncTask或EventBus技术完成. //B.自定义一个类继承AsyncTask,只使用我们需要的方法.完成在子线程加载图片资源,在主线程显示 private class VideoLoadTask extends AsyncTask<String, Void, Void> { //B.该方法在子线程运行,从本地文件中把资源加载到内存中 @Override protected Void doInBackground(String... strings) { //创建VrVideoView.Options对象,决定VR是普通的效果,还是立体效果 VrVideoView.Options options = new VrVideoView.Options(); //立体模式 options.inputType = VrVideoView.Options.TYPE_STEREO_OVER_UNDER; //处理加载的视频格式 //FORMAT_DEFAULT:默认格式(SD卡或assets) //FORMAT_HLS:流媒体数据格式(直播) options.inputFormat = VrVideoView.Options.FORMAT_DEFAULT; try { //提示:视频加载的方法还做了把视频读取到内存中的操作,所以它有一个矛盾,调用该方法是放在主线程还是子线程(一般我们放在子线程) //使用VR控件对象,从资产目录加载视频数据,显示效果 参数: 1.String对象 2.VrVideoView.Options对象,决定显示效果 //vr_video.loadVideoFromAsset(strings[0], options);//不要管他在爆红,就在子线程里执行 //使用VR控件对象,从网络加载视频数据,显示效果(要加网络权限) 参数: 1.视频网址,String对象 vr_video.loadVideo(Uri.parse(strings[0]), options); } catch (IOException e) { e.printStackTrace(); } return null; } } //C.因为VR很占用内存,所以当界面进入OnPause状态,暂停VR视图显示,进入OnResume状态,继续VR视图显示,进入OnDestroy状态,杀死VR,关闭异步任务 //当失去焦点时,回到 @Override protected void onPause() { super.onPause(); //暂停渲染和显示 vr_video.pauseRendering(); } //当获取焦点时,回调 @Override protected void onResume() { super.onResume(); //继续渲染和显示 vr_video.resumeRendering(); } //当Activity销毁时,回调 @Override protected void onDestroy() { super.onDestroy(); //关闭渲染视图,回调 vr_video.shutdown(); //在退出Activity时,如果异步任务没有取消,则取消 if (mVideoLoadTask != null) { if (!mVideoLoadTask.isCancelled()) { mVideoLoadTask.cancel(true); } } } //VR运行状态监听类,自定义一个类继承VrVideoEventListener,复写里面需要的方法 private class MyEventListener extends VrVideoEventListener { //当VR视图加载成功的时候的回调,此时还未开始播放 @Override public void onLoadSuccess() { super.onLoadSuccess(); //获取视频的长度 long max = vr_video.getDuration(); //设置seekbar的进度最大值 seek_bar.setMax((int) max); } //当VR视图加载失败的时候回调的方法 @Override public void onLoadError(String errorMessage) { super.onLoadError(errorMessage); Toast.makeText(VideoPlayerActivity.this, "播放失败", Toast.LENGTH_SHORT).show(); } //当视频开始播放,每次进入下一帧的时候,回调这个方法(就是播放时,会不停的回调该方法) @Override public void onNewFrame() { super.onNewFrame(); //获取当前视频的播放时间位置 int currentPosition = (int) vr_video.getCurrentPosition(); //设置seekBar的进度条 seek_bar.setProgress(currentPosition); //显示播放的进度数字 tv_progress.setText("播放进度:" + String.format("%.2f", currentPosition / 1000.f)); } //当视频播放结束后的回调 @Override public void onCompletion() { super.onCompletion(); //让视频回到0点 vr_video.seekTo(0); //视频停止 vr_video.pauseVideo(); //让进度条也设置到0点 seek_bar.setProgress(0); //播放完成后,重新设置标签,标签true代表着视频处于暂停的状态. isPaused = false; } //设置一个视频播放状态的标签 private boolean isPaused = true; //重写点击视图的方法,是视频被点击时,播放或者暂停 @Override public void onClick() { super.onClick(); //根据标签,判断当前视频的状态,做对应的逻辑处理 //false是不是代表视频正处于暂停状态, if (isPaused) { //视频暂停 vr_video.pauseVideo(); } //true是不是代表视频正在播放的状态. else { //视频播放 vr_video.playVideo(); } //对标签进行一次操作后,取反 isPaused = !isPaused; } } }
效果图
到这里VR小项目全面完工如有疑问欢迎留言!