最近要抽取之前做的 VR 看房的相关功能至单独的仓库,借此机会对其中利用陀螺仪滑动图片的处理方式,以及平滑过渡至 VR 看房场景的过程做一个说明,也能让接手 VR 相关模块的同事更快了解其实现方式。
首先解析利用陀螺仪滑动图片的处理方式。
在这里主要说一说自己的思路,具体代码会在最后给出 Demo 以供参考。
为试验该效果,自定义了一个继承自ImageView
的GyroscopeImageView
,再写一个实现SensorEventListener
接口的GyroscopeManager
,在其onSensorChanged
回调中利用陀螺仪传感器角度的变化来换算出图片需要滑动的距离,并调用GyroscopeImageView
的invalidate()
方法,在GyroscopeImageView
的onDraw
方法中利用canvas.translate
来改变图片的位置。效果如下:
在开发过程中注意到的问题:
1. 图片的尺寸要合理,宽高略大于控件能有较好的视觉效果。
2. 监听陀螺仪传感器事件耗电,要在合适的时候注册和注销对传感器的监听。
3. 当应用内有多个GyroscopeImageView
时,要对它们进行合理的管理。
作为一个写在独立仓库中的功能,要让业务方能够方便地接入使用。
但关于上面提到的前两个问题,还需业务方的配合来解决:
- 业务方在使用
GyroscopeImageView
控件时,需要告知我们控件的宽高,这样我们能合理地对图像尺寸做处理。
//例如用 Picasso加载图像 可将控件的宽高传入自定义的 TransFormation 来处理图像大小
Picasso.get()
.load(picUrl)
.transform(new GyroscopeTransFormation(width, height))
.into(gyroscopeImageView);
- 业务方需要在相关
Activity
的onResume
和onPause
中对陀螺仪传感器进行注册和注销监听。
@Override protected void onResume() {
super.onResume();
//利用我们的 GyroscopeManager 来注册传感器监听
GyroscopeManager.getInstance().register(this);
}
@Override protected void onPause() {
super.onPause();
//利用我们的 GyroscopeManager 来注销监听
GyroscopeManager.getInstance().unregister(this);
}
这样看起来业务方接入还是比较简单的。
重点分析一下第三个问题:
如何对应用内的GyroscopeImageView
进行合理的管理。
3. 当有多个GyroscopeImageView
存在时,GyroscopeManager
应只对当前Activity
中正在展示的GyroscopeImageView
进行刷新。
想到View
在添加至Window
时会调用 View.onAttachedToWindow()
,之后才进行绘制,当View
与Window
分离时会调用onDetachedFromWindow()
。
由此得出的初步想法:在GyroscopeManager
中维护一个GyroscopeImageView
的集合。在GyroscopeImageView
的onAttachedToWindow
方法中将自身加入该集合,onDetachedFromWindow
时将自身从集合中移除。而GyroscopeManager
在传感器的回调中,去通知集合内的 view 进行刷新。
调试中发现该想法考虑不周,如果在一个包含GyroscopeImageView
的页面 Activity1 之上开启一个 Activity2,此时 Activity1 内的GyroscopeImageView
还在PhoneWindow
内没有调用onDetachedFromWindow()
,因此该View
还在 GyroscopeManager
所维护的集合中。
若 Activity2 中也有GyroscopeImageView
,则会注册传感器监听,且刷新集合内所有GyroscopeImageView
。这样一来被覆盖在下层的 Activity1 内的GyroscopeImageView
也会被通知刷新,这是没有必要的,因为此时它并不可见。所以还需维护一个需要传感器处理的 Activity, 只通知该 Activity 内的GyroscopeImageView
进行刷新。
最后针对GyroscopeImageView
维护了一个Map
:
Map<GyroscopeImageView, Boolean>
其 value 为Boolean
标识该GyroscopeImageView
是否需要被GyroscopeManager
通知刷新。通过 Activity 的状态变化来设置Map
中各个GyroscopeImageView
对应的 value,进行合理控制。
接下来说说从房源详情页平滑过渡至 VR 场景页的动画。
同样先谈一谈实现该效果的思路,最后会给出 Demo 以供参考。
此处从房源详情页 Activity 跳转至 VR 场景的 Activity,不同 Activity 内的 View 之间做动画可以使用 Activity 转场动画。
但是我们GyroscopeImageView
内的图片是可滑动的,使用 Activity 转场动画会发现控件内的图片会瞬间居中回正,然后再扩展到全屏,该居中的过程较为突兀,有明显的闪动感。
更加平滑的效果是使GyroscopeImageView
内的图片直接从当前的状态扩展至全屏,最终放弃Activity转场动画,并提出如下解决方案:
1. 首先记录第一个 Activity 中
GyroscopeImageView
控件的位置和其内部图片的偏移量。
2. 在第二个 Activity 中放置一个相同位置的ImageView
, 并展示相同偏移量的图片,用来覆盖住上个页面的GyroscopeImageView
控件。然后将该ImageView
扩展至全屏做动画。
3. 把第二个 Activity 的背景设为透明。