code小生,一个专注 Android 领域的技术分享平台

作者:r17171709
地址:https://www.jianshu.com/p/206713510003
声明:本文来自 r17171709 投稿,转发等请联系原作者授权

今年地产行业兴起VR看房这种模式以提升购房人在带看过程中的体验。近期没有打算购房的朋友可能不知道什么叫VR看房,这里简单做个普及:
这个技术主要有三项核心功能:VR看房、VR讲房、VR带看
VR看房是一种沉浸式看房体验,购房人点击APP上的VR房源,轻触屏幕任意处即可获得包括房屋真实空间的尺寸、朝向、远近等深度信息
VR讲房是在VR看房的基础上,增加语音讲解服务功能。房产经纪人会将房屋最为核心的优势、特色还有一些不足进行主动讲解并录入到系统中。这些录音会随着购房人浏览房源时到达相应位置而进行播放。这个感觉就类似于参观博物馆时配发的讲解器一样,每到一个地点就自动触发语音播放
VR带看则是一种全新的交互场景体验,打破了传统线上看房的固定限制。与伪全息或3D看房不同的是,VR带看可以随意调整自己的位置和视角,获得不同视觉和看房体验,以求达到用户最大的满意度。购房人可与经纪人提前预约看房时间,并实时连线进行交互,改变了传统线上看房“异步传输”的现状(即拍摄后完全没有任何同步互动和反馈)

以上只是一个新概念的普及。本文不会教你如何实现VR看房的功能,因为,我也不会。。。那能教你什么呢?使用陀螺仪移动图片Drawable。打开贝壳找房App就能看到文中所示的效果。

VR看房入口效果

同为地产企业,最近有同事也在开发这个功能,我也顺带学习下这个效果是如何实现的。两年多前已经有大神在github上分享了利用陀螺仪让ImageView自动滚动的功能,功能相对比较单一,只能横或纵方向滚动,链接地址为PanoramaImageView。随后偶然看到贝壳的小伙也基于那个项目对其进行功能扩展,实现了多方向自动滚动的功能,链接地址为GyroscopeImageDemo。两个项目都很优秀,那我们就跟随大神们的脚步,来一步一步分析这个功能是如何实现的吧
本文涉及到的代码都在github上,欢迎star、fork
https://github.com/r17171709/android_demo/tree/master/GyroscopeImageDemo

预备知识

Android系统内置很多传感器组件,陀螺仪就是其中一个。陀螺仪的轴由于陀螺效应始终与初始方向平行,这样就可以通过与初始方向的偏差计算来出实际方向。角度需要角速度与时间积分计算,得到的角度变化量与初始角度相加,就得到目标角度,其中积分时间Dt越小,输出角度越准。
看起来好像很复杂,但实际上到Android Api已经为你封装的好好的。你只要继承SensorEventListener,并将其绑定在SensorManager上,即可让你妥妥的可以实现角度计算功能

SensorManager sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManagerval sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_GAME)

精度与耗电量成正比,所以我们也要选择适合的传感器精度才行。内置四种精度分别为

SensorManager.SENSOR_DELAY_FASTEST(0微秒):最快。最低延迟,一般不是特别敏感的处理不推荐使用,该模式可能在成手机电力大量消耗,由于传递的为原始数据,诉法不处理好会影响游戏逻辑和UI的性能SensorManager.SENSOR_DELAY_GAME(20000微秒):游戏。游戏延迟,一般绝大多数的实时性较高的游戏都是用该级别SensorManager.SENSOR_DELAY_NORMAL(200000微秒):普通。标准延时,对于一般的益智类或EASY级别的游戏可以使用,但过低的采样率可能对一些赛车类游戏有跳帧现象SensorManager.SENSOR_DELAY_UI(60000微秒):用户界面。一般对于屏幕方向自动旋转使用,相对节省电能和逻辑处理,一般游戏开发中不使用

最后就是监听回调。通过两次检测到手机旋转的时间差进行角度累加计算

class GyroscopeAngel : SensorEventListener {    private var timestamp = 0.toLong()

    private val NS2S = 1.0f / 1000000000.0f

    private val angle = longArrayOf(0.toLong(), 0.toLong(), 0.toLong())

    override fun onAccuracyChanged(p0: Sensor?, p1: Int) {

    }

    override fun onSensorChanged(p0: SensorEvent?) {        if (p0?.sensor?.type == Sensor.TYPE_GYROSCOPE) {            if (timestamp == 0L) {                // 从 x、y、z 轴的正向位置观看处于原始方位的设备,如果设备逆时针旋转,将会收到正值;否则,为负值                timestamp = p0.timestamp                return            }            // 得到两次检测到手机旋转的时间差(纳秒),并将其转化为秒            val dT = (p0.timestamp -timestamp) * NS2S            // 将手机在各个轴上的旋转角度相加,即可得到当前位置相对于初始位置的旋转弧度            angle[0] += (p0.values[0] * dT).toLong()            angle[1] += (p0.values[1] * dT).toLong()            angle[2] += (p0.values[2] * dT).toLong()            // 将弧度转化为角度            val anglex = Math.toDegrees(angle[0].toDouble()).toFloat()            val angley = Math.toDegrees(angle[1].toDouble()).toFloat()            val anglez = Math.toDegrees(angle[2].toDouble()).toFloat()            timestamp = p0.timestamp        }    }}

以上代码就是我们今天传感器部分代码的原型,我们在此基础上将它与ImageView关联起来

可移动的Drawable

Drawable在ImageView怎么移动呢?其实很简单,就在onDraw的时候直接移动canvas即可

canvas?.save()canvas?.translate(currentOffsetX, currentOffsetY)super.onDraw(canvas)canvas?.restore()

这里面还有一个别别窍的地方。我们要注意一下ScaleType。ImageView的ScaleType类别很多,但是只有ScaleType.Center是以原图的几何中心点和ImageView的几何中心点为基准,按图片原来的尺寸居中显示并不剪裁。这样的话canvas在移动过程中,之前未显示部分的图片才能被展现出来。如果你选择ScaleType.CENTER_CROP,那多余的部分被剪裁掉,移动过程中就是背景色来占位了
当然这个移动也要有范围约束的,不是随便移的。这里移动范围就是超出控件可视区域部分图片的长与宽

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {    super.onMeasure(widthMeasureSpec, heightMeasureSpec)

    if (drawable != null) {        mWidth = MeasureSpec.getSize(widthMeasureSpec) - paddingLeft - paddingRight        mHeight = MeasureSpec.getSize(heightMeasureSpec) - paddingTop - paddingBottom

        mDrawableWidth = drawable.intrinsicWidth        mDrawableHeight = drawable.intrinsicHeight

        mMaxOffsetX = Math.abs((mDrawableWidth - mWidth) * 0.5f)        mMaxOffsetY = Math.abs((mDrawableHeight - mHeight) * 0.5f)    }}

我们通过接口,将传感器回调过来的X轴Y轴角度比例传到ImageView中,对视图进行刷新

interface GyroscopeImpl {    fun updateProgress(progressX: Float, progressY: Float)}

override fun updateProgress(progressX: Float, progressY: Float) {    this.progressX = progressX    this.progressY = progressY    invalidate()}

最后不要忘记在视图创建和销毁的时候将其在传感器类中进行添加和删除,以免浪费资源

传感器控制类

刚才的预备知识里面已经完成我们大部分的工作了,下面我们就来将这个偏移范围调整一下即可

val dt = (p0.timestamp - mLastTimestamp) * NS2S * 2.0fviews.forEach {    it.mRotateRadianY += p0.values[1] * dt    it.mRotateRadianX += p0.values[0] * dt    if (it.mRotateRadianY > mMaxRotateRadian) {        it.mRotateRadianY = mMaxRotateRadian.toFloat()    }    else if (it.mRotateRadianY < -mMaxRotateRadian) {        it.mRotateRadianY = -mMaxRotateRadian.toFloat()    }    if (it.mRotateRadianX > mMaxRotateRadian) {        it.mRotateRadianX = mMaxRotateRadian.toFloat()    }    else if (it.mRotateRadianX < -mMaxRotateRadian) {        it.mRotateRadianX = -mMaxRotateRadian.toFloat()    }    // 注意此处,X与Y方向是反过来的    it.updateProgress((it.mRotateRadianY / mMaxRotateRadian).toFloat(), (it.mRotateRadianX / mMaxRotateRadian).toFloat())}

最大弧度区间为(0, π/2],自己转转手机就知道了,移动距离可以自行修改
最后将偏移的比例传到ImageView中,进行刷新视图即可

额外的知识

  1. 网络加载图片框架
    框架很多,对于一般情况下的使用都问题不大,但在这个场景下坑还是蛮多的。首先是Fresco,这玩意的ScaleType.Center与ImageView的不同,图片被剪裁了,玩不了。Glide4也不行,但是Glide3是可以的。Picasso没问题

  2. RecyclerView上的使用
    在RecyclerView上使用是没有问题的,如果你想横向当Banner展示而不用ViewPager的话,建议你使用DiscreteScrollView,它将RecyclerView包装成ViewPager,并且提供诸如scrollToPosition、smoothScrollToPosition、getCurrentItem这种ViewPager中的方法
    这是无限循环banner的效果

images.add("http://wx2.sinaimg.cn/large/6e9ad2bdly1fnih8uqgkuj2140140b2b.jpg")images.add("http://vrlab-public.ljcdn.com//release//vradmin//1000000020129136//images//FF41C450.png")images.add("http://wx2.sinaimg.cn/large/6e9ad2bdly1fnih8s6on4j21401401kz.jpg")images.add("http://wx2.sinaimg.cn/large/6e9ad2bdly1fnih8uqgkuj2140140b2b.jpg")images.add("http://vrlab-public.ljcdn.com//release//vradmin//1000000020129136//images//FF41C450.png")

rv_image.setHasFixedSize(true)rv_image.scrollToPosition(1)rv_image.addOnItemChangedListener { _, adapterPosition ->    if (adapterPosition == 0) {        rv_image.scrollToPosition(3)    }    if (adapterPosition == 4) {        rv_image.scrollToPosition(1)    }}val adapter = MainAdapter(requireContext(), images)rv_image.adapter = adapter

来看看我们demo的效果

demo

高仿贝壳找房之使用陀螺仪移动图片Drawable相关推荐

  1. 使用threejs实现VR看房效果,仿贝壳找房VR看房

    一.效果演示 最近看到贝壳找房里的VR看房,感觉挺有意思:就试着仿照实现了一下.在这里做一个学习记录. 二.代码 <script>// 记录当前是在哪个房间let currentHome ...

  2. 百亿美元估值的贝壳找房,值吗?

    百亿美元估值的贝壳找房,值吗? 贝壳找房又被传上市了. 6月16日,界面新闻报道,贝壳找房即将上市,地点可能是香港或者美国. 这不是首次被传,自成立以来,有关贝壳找房要上市的消息就不断传出,虽然每一次 ...

  3. 贝壳找房挖到AI大牛叶杰平,房产中介转型需要技术,高估值更需要

    杨净 发自 凹非寺  量子位 报道 | 公众号 QbitAI 前滴滴AI实验室负责人叶杰平,去贝壳找房了. 没错,从"行"到"住",从"车" ...

  4. 现代汽车、欧莱雅、贝壳找房、信达生物、诺维信等公司高管变动

    一周企业高管变动要闻. 全球 韩国现代汽车集团(Hyundai Motor Group)首席副会长郑义宣14日升任集团新一任会长,作为现代集团的第三代继承人正式接班.这是现代汽车集团时隔20年正式换帅 ...

  5. 贝壳找房技术总监肖鹏:高速成长下的技术团队怎么带?

    本文根据肖鹏老师在[deeplus直播第248期]线上分享演讲内容整理而成.(文末有获取本期PPT&回放的方式,不要错过) 肖鹏 贝壳找房技术总监 贝壳系统平台中心技术总监,主要负责数据库.大 ...

  6. 贝壳找房《2018城市居住报告》:新一线租房量持续攀升

    2019年1月24日,贝壳找房发布<2018城市居住报告>,聚焦租房和二手房交易人群,盘点分析了北京.上海.深圳.南京.武汉.长沙.重庆.成都.合肥等9大房产交易城市现状.报告发现,过去一 ...

  7. AI 搜房,贝壳找房的Milvus实践

    | 项目背景 贝壳作为一家房产服务互联网平台,如何在海量房源中选出能够快速成交的房源是对平台和经纪人来说都是一件非常重要同时具有挑战的事情,但是针对房型图,户型信息等一系列非结构化数据在平台中应该怎么 ...

  8. AB实验平台在贝壳找房的设计与实践

    作者雏鹰(企业代号名),目前负责贝壳找房增长方向AB实验平台研发工作. 引言 ​ 随着贝壳找房业务的不断增长,精细化运营显得尤为重要.为了保证每一次迭代,每一个方案能够真正得到用户的认可,为贝壳带来有 ...

  9. 贝壳找房值百亿美元吗?

    来源:燃财经(ID:rancaijing) | 作者:唐亚华 数据猿官网 | www.datayuan.cn 今日头条丨一点资讯丨腾讯丨搜狐丨网易丨凤凰丨阿里UC大鱼丨新浪微博丨新浪看点丨百度百家丨博 ...

最新文章

  1. LeetCode简单题之按奇偶排序数组 II
  2. linux服务器上nginx日志访问量统计命令
  3. java中定义类的关键字为_在Java中,定义类的关键字是__________________.
  4. Gradle 引入本地定制 jar 包,而不使用坐标下载 jar 包的方法
  5. mysql update column_MySQL8.0 新特性:Partial Update of LOB Column
  6. 互联网日报 | 阿里国内消费者已接近10亿;联想布局半导体赛道;我国5G用户超过6千万户...
  7. Jeecg 切换默认首页方法
  8. Mybatis的直接执行SQL
  9. 力扣(LeetCode)56
  10. mysql 写出高性能sql 防止索引失效总结
  11. sublime text3 炫酷主题
  12. 淘宝API如何获取商品详情信息|sku|价格|店铺|优惠券|运费信息,淘宝商品详情API接口
  13. Masimo宣布Rad-G™ with Temperature通过CE认证
  14. 深度学习研究生常用网站||图像处理||医学影像||使用评价||汇总
  15. 在fpga中用Cordic算法来产生正弦函数
  16. ode45的常用和扩展用法
  17. ZLIB 压缩的数据格式规范
  18. 如何解决 MacBook 电池耗电问题
  19. 基于MDK开发的TencentOS-Tiny软件包,快速移植物联网操作系统到Keil中!
  20. Mac OS -- ORSSerialPort打开serialPort时提示错误Operation not permitted

热门文章

  1. 随机森林原始论文_【20108】喜讯|刘倩同学论文被制冷学报期刊录用
  2. Python攻城的成长————网络编程
  3. Unity批量预设体替换材质球
  4. Codeforces Round #782 (Div. 2) A~D
  5. 使用必须、应该、可以来确定每天事情的优先级
  6. Python黑科技-表白神器
  7. 关于Qos中常用的CIR、PIR、CBS、PBS、EBS的解释以及用法关系
  8. 【文献笔记】【部分精读】【CIR】Angle of Arrival Estimation based on Channel Impulse Response Measurements
  9. 一键搜索多个搜索引擎
  10. 新概念英语第一册(26)