最近听同事说自如banner的裸眼3D效果很有创意,下载APP体验了一番觉得效果确实非常不错,所以立马就仿了一下。代码已上传至github仓库中,AndroidUiDemo

地址:https://github.com/SHPDZY/AndroidUiDemo

下面是demo和自如app的效果对比。

忽略搜索栏哈哈,我是直接截屏自如主页用ps扣来的图。


自定义ZiRuLayout

通过自定义view实现自如banner的裸眼3D效果,需要效果的view外层套上ZiRuLayout即可,demo中主要做测试,具体实现需要根据业务定制。本次是直接在自定义view中注册传感器,如需更好的体验的话,建议自定义个SersenManager类来注册监听,数据通过livedata对外暴露。下面来实现裸眼3D效果的具体代码。

注册传感器监听

代码中分别使用了陀螺仪和加速度传感器来实现裸眼3D效果的效果。主要是通过传感器相应的值,计算view的移动位置,并通过Scroller进行滑动

private fun initView(context: Context) {mScroller = Scroller(context)//SensorManager实例mSensorManager = context.getSystemService(Context.SENSOR_SERVICE) as? SensorManagermSensorGyroscope = mSensorManager?.getDefaultSensor(Sensor.TYPE_GYROSCOPE)mSensorAccelerometer = mSensorManager?.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)//设置传感器监听,灵敏度设置为game就足够if (useGyroscope) {mSensorManager?.registerListener(this, mSensorGyroscope, SENSOR_DELAY_GAME)} else {mSensorManager?.registerListener(this, mSensorAccelerometer, SENSOR_DELAY_GAME)}if (context is FragmentActivity) {addZiRuLifecycleObserver(context)}
}

重写监听的onSensorChanged方法

数据计算和view的滑动都在此方法中执行

    override fun onSensorChanged(sensorEvent: SensorEvent?) {when (sensorEvent?.sensor?.type) {Sensor.TYPE_GYROSCOPE -> {if (timestamp != 0f) {val dT = (sensorEvent.timestamp - timestamp) * NS2Sangle[0] += sensorEvent.values[0] * dTangle[1] += sensorEvent.values[1] * dTval angleY = Math.toDegrees(angle[0].toDouble()).toFloat()val angleX = Math.toDegrees(angle[1].toDouble()).toFloat()if (totalY == 0f) {totalY = angleY; return}if (totalX == 0f) {totalX = angleX; return}var scrollX = 0fvar scrollY = 0fval dx = totalX - angleXval dy = totalY - angleYif (abs(dx) >= 0.1) scrollX = handleX(dx) * mDirection * 1.5fif (abs(dy) >= 0.1) scrollY = handleY(dy) * mDirection * 1fif (scrollX != 0f) totalX = angleXif (scrollY != 0f) totalY = angleYif (scrollX != 0f || scrollY != 0f)smoothScrollBy(scrollX.toInt(), scrollY.toInt())}timestamp = sensorEvent.timestamp.toFloat()}Sensor.TYPE_ACCELEROMETER -> {val angleY = sensorEvent.values[1]val angleX = sensorEvent.values[0]if (totalY == 0f) {totalY = angleY; return}if (totalX == 0f) {totalX = angleX; return}var scrollX = 0fvar scrollY = 0fval dx: Float = totalX - angleXval dy: Float = totalY - angleYif (abs(dx) > 0.2 && abs(dx) < 2) scrollX = handleX(dx) * mDirection * 5if (abs(dy) > 0.2 && abs(dy) < 2) scrollY = handleY(dy) * mDirection * 2fif (scrollX != 0f) totalX = angleXif (scrollY != 0f) totalY = angleYif (scrollX != 0f || scrollY != 0f)smoothScrollBy(-scrollX.toInt(), -scrollY.toInt())}}}

传感器发生变化后通过Scroller滑动view

    fun smoothScrollTo(fx: Int, fy: Int) {val dx = fx - mScroller.finalXval dy = fy - mScroller.finalYsmoothScrollBy(dx, dy)}fun smoothScrollBy(dx: Int, dy: Int) {// 参数一:startX 参数二:startY为开始滚动的位置,dx,dy为滚动的偏移量mScroller.startScroll(mScroller.finalX, mScroller.finalY, dx, dy, 200)invalidate()}override fun computeScroll() {// 判断滚动是否完成 true就是未完成if (mScroller.computeScrollOffset()) {scrollTo(mScroller.currX, mScroller.currY)postInvalidate()}super.computeScroll()}

生命周期感知

通过addZiRuLifecycleObserver()方法设置activity生命周期观察者,使view拥有生命周期感知的能力,自主注册和反注册。

    fun addZiRuLifecycleObserver(owner: LifecycleOwner?) {owner?.lifecycle?.addObserver(ZiRuLifecycleObserverAdapter(owner, this))}override fun onResume(owner: LifecycleOwner?) {if (useGyroscope) {mSensorManager?.registerListener(this, mSensorGyroscope, SENSOR_DELAY_GAME)} else {mSensorManager?.registerListener(this, mSensorAccelerometer, SENSOR_DELAY_GAME)}}override fun onPause(owner: LifecycleOwner?) {mSensorManager?.unregisterListener(this)}override fun onDestroy(owner: LifecycleOwner?) {}...interface ZiRuLifecycleObserver : LifecycleObserver {fun onResume(owner: LifecycleOwner?)fun onPause(owner: LifecycleOwner?)fun onDestroy(owner: LifecycleOwner?)
}class ZiRuLifecycleObserverAdapter(private val mLifecycleOwner: LifecycleOwner,private val mObserver: ZiRuLifecycleObserver
) :LifecycleObserver {@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)fun onResume() {LogUtils.d("ZiRuLifecycleObserverAdapter onResume")mObserver.onResume(mLifecycleOwner)}@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)fun onPause() {LogUtils.d("ZiRuLifecycleObserverAdapter onPause")mObserver.onPause(mLifecycleOwner)}@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)fun onDestroy() {LogUtils.d("ZiRuLifecycleObserverAdapter onDestroy")mObserver.onDestroy(mLifecycleOwner)}
}

Banner翻页效果

自定义viewpager的PageTransformer,重写transformPage()方法实现banner背景淡入淡出,上层浮动的图案正常左右滑动。

class ZiRuBannerTransformer : BasePageTransformer() {private var mMinAlpha: Float = DEFAULT_MIN_ALPHAoverride fun transformPage(view: View, position: Float) {val pageWidth = view.width //得到view宽when {position < -1 -> { // [-Infinity,-1)// This page is way off-screen to the left. 出了左边屏幕view.alpha = mMinAlpha}position <= 1 -> { // [-1,1]var factor = 0fif (position < 0) {//消失的页面view.translationX = -pageWidth * position //阻止消失页面的滑动(view as FrameLayout).run {view.findViewById<FrameLayout>(R.id.frame_layout).translationX =pageWidth * position}factor = mMinAlpha + (1 - mMinAlpha) * (1 + position)} else {//出现的页面view.translationX = pageWidth.toFloat() //直接设置出现的页面到底(view as FrameLayout).run {view.findViewById<FrameLayout>(R.id.frame_layout).translationX =pageWidth * position}view.translationX = -pageWidth * position //阻止出现页面的滑动factor = mMinAlpha + (1 - mMinAlpha) * (1 - position)}//透明度改变Logview.alpha = factor}else -> { // (1,+Infinity]// This page is way off-screen to the right.    出了右边屏幕view.alpha = mMinAlpha}}}companion object {private const val DEFAULT_MIN_ALPHA = 0.0f}
}

Banner的Item布局示例

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"><FrameLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><com.example.zyuidemo.widget.ZiRuLayoutandroid:id="@+id/zr_bac"android:layout_width="match_parent"android:layout_height="match_parent"><ImageViewandroid:id="@+id/sdv_single"android:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="fitXY"android:src="@drawable/img_bac_1" /></com.example.zyuidemo.widget.ZiRuLayout><FrameLayoutandroid:id="@+id/frame_layout"android:layout_width="match_parent"android:layout_height="wrap_content"><ImageViewandroid:id="@+id/iv_ad_text"android:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="fitXY"android:src="@drawable/img_ad_text_1" /><com.example.zyuidemo.widget.ZiRuLayoutandroid:id="@+id/zr_text"android:layout_width="match_parent"android:layout_height="match_parent"><ImageViewandroid:id="@+id/iv_ad_icon"android:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="fitXY"android:src="@drawable/img_ad_bird_1" /></com.example.zyuidemo.widget.ZiRuLayout></FrameLayout></FrameLayout></layout>

Android 仿自如APP裸眼3D效果相关推荐

  1. Android OpenGL 仿自如 APP 裸眼 3D 效果

    概述 之前看到 自如团队 发布的 自如客APP裸眼3D效果的实现 ,非常有趣,不久后,社区内 Android 的开发者们陆续提供了 Flutter. Android 原生 .Android Jetpa ...

  2. 卷起来了!Android OpenGL仿自如APP裸眼3D效果

    /   今日科技快讯   / 近日,"乘联会"微信公众号发布消息,2021年12月新能源乘用车市场多元化发力,厂商批发销量突破万辆的企业有14家,较前期大幅增多,其中:比亚迪933 ...

  3. 设计师:裸眼 3D 效果,你们客户端实现很难吗?

    自如-黄进 | 作者 承香墨影 | 编辑 https://juejin.cn/post/6989227733410644005 | 原文 Hi,大家好,这里是承香墨影! 说到裸眼 3D 效果,最先想到 ...

  4. 怎么设置ppt页面的长度和宽度_在PPT中将照片变裸眼3D效果怎样操作?分享技巧,帮你快速实现...

    PPT的使用相信大家都不陌生,使用最多的就是制作PPT对工作进行汇报,对新项目进行展开讨论.其实在PPT中还可以设计海报,制作高逼格封面以及将照片变为3D效果等偏设计类的操作.今天将以如何把照片变为3 ...

  5. 投影仪的裸眼3D效果

    裸眼3D是指在看投影仪投出来的画面时,不需要戴3D眼镜就能看到立体效果的技术.裸眼3D投影仪通常使用两个投影仪同时投出左右眼图像,利用人眼视差原理来产生立体效果.这种方式看起来比较自然,但是画面质量通 ...

  6. 基于android的裸眼3d,午诺裸眼3D原理其实并不复杂

    午诺裸眼3D原理其实并不复杂 其实同样是裸眼3D手机,可能其中含有的技术却大不相同.午诺P8采用的是国内领先3D光学厂商康得新提供的柱状光栅技术,屏幕通过特殊处理给左右眼的图像是不一样的,消费者面对手 ...

  7. 裸眼 3D 是什么效果?

    作者:沙因,腾讯 IEG 前端开发工程师 介绍一种裸眼 3D 的实现方式,代码以 web 端为例. 平常我们都是戴着 3D 眼镜才能感受 3D 效果,那裸眼能直接看 3D 么?可以看看下面这个视频: ...

  8. 如何让视频产生裸眼3D的效果

    要使视频产生裸眼3D效果,可以使用视差技术或立体投影技术.这需要通过制作两个不同的视频或图像来模拟左眼和右眼的视线,然后使用特殊的投影器或显示器进行投影或显示.

  9. 户外LED显示屏如何实现裸眼3D显示效果:创造逼真立体体验的新视界

    随着科技的不断进步,户外LED显示屏已成为现代广告和娱乐领域中不可或缺的元素.而在这个数字化时代,人们对于视觉体验的要求也越来越高.裸眼3D效果作为一种引人注目的显示技术,为户外LED显示屏注入了全新 ...

  10. 成都太古里,溢出屏幕的裸眼3d

    在成都太古里和春熙路交界处有一块裸眼3D屏,早在十月份的时候就凭借一段酷炫真实的外星飞碟裸眼3d视频登上热搜,刷爆朋友圈,让本就是打卡圣地的太古里再一次上升成为一个网红打卡必经之地. 数字平原有幸参与 ...

最新文章

  1. Python中的星号本质及其使用方法详解
  2. 在ActionBar显示ShareActionProvider分享文本,点击可以打开进行分享(19)
  3. MPLS标签分发、通告及管理方式
  4. oracle 锁表如何查看_【赵强老师】第一个Oracle的手工备份和恢复
  5. hive中如何进行按周计算
  6. ffmpeg 常用基本命令和ffmpeg处理RTMP流媒体的常用命令
  7. 计算机动画人机交互,北大计算机系多媒体与人机交互.ppt
  8. 程序员必修课:为什么非要用Python做数据分析?Excel不好吗?
  9. Java I/O系统之Print 流
  10. 关键词选择与维护教程
  11. islower()方法
  12. html画表盘 随时间转动,canvas绘制表盘时钟
  13. Linux文件裸写,Linux下使用裸設備作為數據文件
  14. 怎么修改我的世界服务器封面,《我的世界:初识服务器》
  15. Elasticsearch CCR源码分析
  16. 如何修改Android的ro属性
  17. 计算机c盘属性不显示安全选项,Win10系统下磁盘属性没有安全选项卡怎么解决?...
  18. ncr管理系统_建设银行北京分行采用NCR的系统管理软件
  19. 大数据软件应用举例商圈分析城市管理
  20. SIGIR 2021 | FSCD-PreRank:面向效率和效果更加均衡的交互式粗排模型

热门文章

  1. 如何用python制作动画电影_用Python制作3D动画
  2. C:\WINDOWS\system32\config\systemprofile\Desktop引用了一个不可用的位置
  3. ipad上的游戏服务器无响应,教你 game center无法连接服务器解决办法及iOS9.3.2公测版已修复GameCenter无响应bug...
  4. 5类网线,超5类网线,6类网线,超6类网线的区别
  5. win7系统怎么用笔记本做wifi热点
  6. DDD之一年级小学生作业辅导
  7. pdm系统是归档服务器吗,PDM系统的主要功能
  8. 2015年数模B题学习报告
  9. 域服务器无法修改域账户密码,域用户使用Ctrl+Alt+del不能修改密码
  10. 处理器哪个好_对比骁龙730G、765G、猎户座980、天玑1000处理器,性能哪个好?