上次有文章介绍了利用传感器实现3D效果,根据加速度和重力传感器,计算xy偏移值,然后在移动view。
1. 利用MotionLayout实现
最开始想到的是用motionlayout也可以同样实现,但是最后发现我错了,motionlayout设置的view路径是固定的,无法在xy轴上自由移动。
一个简单的效果:
2. 封装View
利用自定义ViewGroup实现,直接在布局中引用就行了
主要代码
传感器初始化:
//获取传感器XYZ值
private var mAcceleValues : FloatArray ?= null
private var mMageneticValues : FloatArray ?= null
private val listener = object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent?) {
if (event?.sensor?.type == Sensor.TYPE_ACCELEROMETER) {
mAcceleValues = event.values
//log("x:${event.values[0]},y:${event.values[1]},z:${event.values[2]}")
}
if (event?.sensor?.type == Sensor.TYPE_MAGNETIC_FIELD) {
mMageneticValues = event.values
}
if (mAcceleValues==null || mMageneticValues==null) return
val values = FloatArray(3)
val R = FloatArray(9)
SensorManager.getRotationMatrix(R, null, mAcceleValues, mMageneticValues);
SensorManager.getOrientation(R, values);
//val z = values[0].toDouble()
// x轴的偏转角度
//val x = Math.toDegrees(values[1].toDouble()).toFloat()
// y轴的偏转角度
//val y = Math.toDegrees(values[2].toDouble()).toFloat()
val degreeZ = Math.toDegrees(values[0].toDouble()).toInt()
val degreeX = Math.toDegrees(values[1].toDouble()).toInt()
val degreeY = Math.toDegrees(values[2].toDouble()).toInt()
log("x:${degreeX},y:${degreeY},z:${degreeZ}")
calculateScroll(degreeX, degreeY, degreeZ)
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
}
}
//传感器初始化
private var hasInit = false
private fun initSensor(){
if (hasInit) return
log("initSensor")
val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
// 重力传感器
val acceleSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
sensorManager.registerListener(listener, acceleSensor, SensorManager.SENSOR_DELAY_GAME)
// 地磁场传感器
val magneticSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)
sensorManager.registerListener(listener, magneticSensor, SensorManager.SENSOR_DELAY_GAME)
hasInit = true
}
计算移动距离:
private fun calculateScroll(x: Int, y: Int, z: Int) {
var deltaY = 0
var deltaX = 0
//除去一下特殊的角度
//if (abs(x)==180 || abs(x)==90 || abs(y)==180 || abs(y)==90) return
//if (x !in -90 until 45) return
if (abs(z) > 90){
if (y in -180 until 0){
//从右向左旋转
//x值增加
deltaX = abs(((y / 180.0) * slideDistance).toInt())
}else if (y in 0 until 180){
//从左向右旋转
deltaX = -abs(((y / 180.0) * slideDistance).toInt())
}
}
if (x in -90 until 0){
deltaY = abs((((x) / 180.0) * slideDistance).toInt())
}else if (x in 0 until 90){
deltaY = -abs((((x) / 180.0) * slideDistance).toInt())
}
if (abs(deltaX) < 5) deltaX=0
if (abs(deltaY) < 5) deltaY=0
log("onSensorChanged,scrollX:${deltaX},scrollY:${deltaY},x:${x} y:${y}")
}
滑动:
scroller.startScroll(deltaX,deltaY, (this.x+deltaX).toInt(),(this.y+deltaY).toInt(),1000)
override fun computeScroll() {
super.computeScroll()
//判断Scroller是否执行完毕
if (scroller.computeScrollOffset()) {
val slideX = scroller.currX
val slideY = scroller.currY
val bottomSlideX = slideX/3
val bottomSlideY = slideY/3
val middleSlideX = slideX/2
val middleSlideY = slideY/2
val topSlideX = -slideX
val topSlideY = -slideY
bottomImageView?.layout(0+bottomSlideX,0+bottomSlideY,measuredWidth+bottomSlideX,measuredHeight+bottomSlideY)
//middleImageView?.layout(middleLeft+middleSlideX,middleTop+middleSlideY,middleRight+middleSlideX,middleBottom+middleSlideY)
topImageView?.layout(topLeft+topSlideX,topTop+topSlideY,topRight+topSlideX,topBottom+topSlideY)
/*(bottomImageView as View).scrollTo( scroller.currX, scroller.currY )*/
//通过重绘来不断调用computeScroll
invalidate()
}
}
属性配置:
<declare-styleable name="layout3d">
<!--上中下层资源文件-->
<attr name="TopLayer" format="reference" />
<attr name="MiddleLayer" format="reference" />
<attr name="BottomLayer" format="reference" />
<!--上中下层滑动距离-->
<attr name="SlideDistance" format="dimension"/>
<!--上中下层是否滑动-->
<attr name="TopSlidingEnable" format="boolean"/>
<attr name="MiddleSlidingEnable" format="boolean"/>
<attr name="BottomSlidingEnable" format="boolean"/>
<!--中层图片资源坐标设置-->
<attr name="MiddleLayerTop" format="dimension"/>
<attr name="MiddleLayerBottom" format="dimension"/>
<attr name="MiddleLayerLeft" format="dimension"/>
<attr name="MiddleLayerRight" format="dimension"/>
<!--上层图片资源坐标设置-->
<attr name="TopLayerTop" format="dimension"/>
<attr name="TopLayerBottom" format="dimension"/>
<attr name="TopLayerLeft" format="dimension"/>
<attr name="TopLayerRight" format="dimension"/>
</declare-styleable>
“`
在布局中引入:
<com.example.montionlayout3d.view.Layout3D
android:id="@+id/vg3D"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="@color/titi"
app:TopLayer="@drawable/circle"
app:MiddleLayer="@drawable/star1"
app:BottomLayer="@drawable/back"
layout3d:MiddleLayerTop="30dp"
layout3d:MiddleLayerBottom="70dp"
layout3d:MiddleLayerLeft="300dp"
layout3d:MiddleLayerRight="340dp"
layout3d:TopLayerTop="50dp"
layout3d:TopLayerBottom="200dp"
layout3d:TopLayerLeft="20dp"
layout3d:TopLayerRight="170dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
最终效果:
4. 问题
不知道为啥有时候会莫名抽搐,移动的不是很顺滑。。。。
实现效果不太好,小问题还很多,不建议使用😥😥
求大佬指点
5. 项目地址
今天的文章View实现3D效果分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/18110.html