上图的效果想必大家在很多VR App中都见过,下面介绍一种简单的实现方法:
大家可以看到,其实只不过是一个展示图片效果的列表,但是当你滑动到某个位置停止时,对应的条目被激活了,这张图片的全景图便开始动态的显示活动了,原理就是这么简单,那我们如何实现呢?
思路:
1. 首先我们需要一个展示图片的列表(ListView、RecycleView)
2. 列表的每个item展示对应的平面图
3. 对列表进行滑动监听,停止滑动时对应条目的全景图激活显示
4. 页面加载,默认首先要显示第一个item的全景图
思路就是这么简单,这里的全景图我们需要对应的全景图控件来显示,google vr 中有对应的全景图控件VrPanoramaView,google vr 的核心是其父类VrWidgetView,VrPanoramaView和VrVideoView是一对孪生兄弟,分别用来显示全景图和播放全景视频(google vr 视频播放即VrVideoView的使用请参考google vr 入门之制作简易的VR播放器及去除界面控制按钮),今天我们用到的是全景图的显示功能,使用VrPanoramaView非常简单:
// 使用google vr VrPanoramaView 添加的
compile 'com.google.vr:sdk-audio:1.40.0'
compile 'com.google.vr:sdk-base:1.40.0'
compile 'com.google.vr:sdk-panowidget:1.40.0'
我们要用的VrPanoramaView在sdk-panowidget库中,这里使用1.40.0版本是为了去除android 7.0手机没有使用google vr服务弹出警告对话框的问题( 详情请点击)。
展示图片的列表我这里使用RecycleView,activity_main.xml
mRecyclerView = (RecyclerView) findViewById(R.id.recycleview);
//全景图控件初始化
vrPanoramaView = new VrPanoramaView(this);
vrPanoramaView.setStereoModeButtonEnabled(false);//眼镜模式按钮禁掉
vrPanoramaView.setFullscreenButtonEnabled(false); //全屏模式按钮禁掉
vrPanoramaView.setInfoButtonEnabled(false); //信息按钮禁掉
vrPanoramaView.setTouchTrackingEnabled(true); //开启手触模式
options = new VrPanoramaView.Options();
options.inputType = VrPanoramaView.Options.TYPE_MONO;
为RecycleView准备数据,设置适配器
//准备数据,这里模拟假数据
mDatas = new ArrayList();
for (int i = 'A'; i < 'M'; i++) {
mDatas.add(String.valueOf((char) i));
}
mSize = mDatas.size();
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mAdapter = new HomeAdapter();
mRecyclerView.setAdapter(mAdapter);
看看HomeAdapter的内容:
class HomeAdapter extends RecyclerView.Adapter {
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == PANORAMA_ITEM) {
return new VrPanoramaViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_panorama, parent, false));
} else {
return new FooterHolder(LayoutInflater.from(mContext).inflate(R.layout.item_footer, parent, false));
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (mSize - position > 2) {
VrPanoramaViewHolder h = (VrPanoramaViewHolder) holder;
h.tv.setText(mDatas.get(position));
h.iv.setBackgroundResource(plan[position]);
if (isFirstTime) {//首次进入要显示第一个item的全景
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), vr[0]);
vrPanoramaView.loadImageFromBitmap(bitmap, options);
h.iv.addView(vrPanoramaView);
currentPos = 0;
isFirstTime = false;
}
} else if (position == mSize - 1) {
FooterHolder h = (FooterHolder) holder;
h.tv.setText("到底啦...");
}
Log.e(TAG, "onBindViewHolder: " + position);
}
@Override
public int getItemCount() {
return mSize;
}
@Override
public int getItemViewType(int position) {
if (mSize - position > 2) {
return PANORAMA_ITEM;//全景图类型的item
} else {
return FOOTER_ITEM;//底部填充的两个item
}
}
class VrPanoramaViewHolder extends RecyclerView.ViewHolder {//全景图的Holder
FrameLayout iv;
TextView tv;
public VrPanoramaViewHolder(View view) {
super(view);
iv = (FrameLayout) view.findViewById(R.id.iv_content);
tv = (TextView) view.findViewById(R.id.content_name);
}
}
class FooterHolder extends RecyclerView.ViewHolder {//底部填充的Holder
TextView tv;
public FooterHolder(View view) {
super(view);
tv = (TextView) view.findViewById(R.id.footer_tv);
}
}
}
对RecycleView添加滚动监听:
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
int lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition();
int pos = lastVisibleItemPosition - 2;
if (RecyclerView.SCROLL_STATE_IDLE == newState) {//停止滑动
showPanorama(pos);
} else {
if (pos == currentPos || currentPos < 0)//目前正在显示不能移除
return;
ViewParent parent = vrPanoramaView.getParent();
if (parent != null) {//防止在滑动过程中发现被复用的全景图控件
ViewGroup viewGroup = (ViewGroup) parent;
viewGroup.removeView(vrPanoramaView);
currentPos = -1;//此处赋值表示全景图控件没有处在任何item的位置
}
}
super.onScrollStateChanged(recyclerView, newState);
}
这是RecycleView滚动监听回调的方法,在回调中我们得到屏幕中最后一个可见的item的postion,即lastVisibleItemPosition,我们关心的不是这个条目,而是它的上上一个条目,故而才有了int pos = lastVisibleItemPosition - 2;这句代码,pos才是我所关心的(默认一屏幕最少显示三个item最多显示4个item,当前屏幕中最后一个可见的item位于屏幕中第3个或者是第4个的位置,该位置的上上一个item位于屏幕的中间靠上区域内,这个区域的条目显示全景图,用户看着会比较舒服),当停止滑动后,显示全景图:
private void showPanorama(int pos) {
if (pos == currentPos)//防止重复,做无用功
return;
HomeAdapter.VrPanoramaViewHolder childViewHolder = (HomeAdapter.VrPanoramaViewHolder) mRecyclerView.findViewHolderForAdapterPosition(pos);
ViewParent parent = vrPanoramaView.getParent();
if (parent != null) {
ViewGroup viewGroup = (ViewGroup) parent;
viewGroup.removeView(vrPanoramaView);
}
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), vr[pos]);
vrPanoramaView.loadImageFromBitmap(bitmap, options);
childViewHolder.iv.addView(vrPanoramaView);
currentPos = pos;
}
该方法是显示RecycleView对应位置item的全景图,全景图也是一个View,只需要通过addView的形式将其添加到对应的item上即可,添加之前加载该item的全景图片。
.apk下载
源码下载