前因后果
这段时间比较忙,一直没抽出时间来写文章,登录账号看了下,上一篇文章还是两个月前写的,之前一个月都会写一篇文章的,有点懒惰了。。。
今天在博客中看见自如客APP研发团队发的一篇文章:自如客APP裸眼3D效果的实现,看了看效果感觉很炫,然后又看到已经有人使用 Flutter
实现了这个效果,Flutter
能实现,Compose
肯定也没问题,来吧,说干就干。
实现思路
先来看看实现的效果吧,没有人家的那么好看,大家将就着看吧😂。
实现思路其实很简单,就是放了两层布局(当然自如客APP中实现是三层布局,原理是一样的),姑且叫背景和前景吧,背景不动,前景跟随设备旋转的角度来进行位置偏移就可以了。
码代码
实现思路有了,下面就来码代码吧,先来注册下传感器吧:
mSensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
// 陀螺仪传感器
mMagneticSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)
mSensorManager.registerListener(this, mMagneticSensor, SensorManager.SENSOR_DELAY_GAME)
再来在回调中获取下需要偏移的坐标:
override fun onSensorChanged(event: SensorEvent?) {
if (event == null) return
when (event.sensor.type) {
Sensor.TYPE_ACCELEROMETER -> {
// x,y,z分别存储坐标轴x,y,z上的加速度
val x = event.values[0]
val y = event.values[1]
val z = event.values[2]
refreshState(x, y)
Log.d(TAG, "TYPE_ACCELEROMETER x:$x y:$y z:$z")
}
Sensor.TYPE_MAGNETIC_FIELD -> {
// 三个坐标轴方向上的电磁强度,单位是微特拉斯(micro-Tesla),用uT表示,也可以是高斯(Gauss),1Tesla=10000Gauss
val x = event.values[0]
val y = event.values[1]
val z = event.values[2]
refreshState(x, y)
Log.d(TAG, "TYPE_MAGNETIC_FIELD x:$x y:$y z:$z")
}
}
}
获取完坐标后就需要思考一个问题,在 Compose
中我们一直说数据驱动 UI 改变,这里也同样是,先来创建一个 ViewModel
:
class MainViewModel : ViewModel() {
private val _xState = MutableLiveData(0f)
val xState: LiveData<Float> = _xState
fun onxStateChanged(position: Float) {
_xState.value = position
}
private val _yState = MutableLiveData(0f)
val yState: LiveData<Float> = _yState
fun onyStateChanged(position: Float) {
_yState.value = position
}
}
ViewModel
中提供了两个方法,分别来修改 x 和 y 的坐标,上面的 refreshState 方法中就调用的是这两个方法:
private fun refreshState(x: Float, y: Float) {
viewModel.onxStateChanged(x)
viewModel.onyStateChanged(y)
}
好了,万事具备,只欠东风,下面就来画布局吧:
@Composable
fun ThreeDImage(x: Float, y: Float) {
Box(modifier = Modifier.fillMaxSize()) {
Image(
modifier = Modifier
.fillMaxSize(),
contentScale = ContentScale.Crop,
painter = painterResource(id = R.drawable.icon_three_bg),
contentDescription = "背景",
)
Image(
modifier = Modifier
.fillMaxSize()
.offset(x = x.dp, y = y.dp),
painter = painterResource(id = R.drawable.icon_three_small),
contentDescription = "前景",
)
}
}
ThreeDImage 可组合项中接受两个参数,分别是前景需要偏移的 x 和 y 值,整个布局使用 Box
进行包裹,里面依次放了背景和前景,然后根据传进来需要偏移的 x 和 y 值来对前景做偏移。
是不是非常简单,这还是多亏了 Compose
中万能的 Modifier
,调用 Modifier
的扩展方法 offset
即可调整布局的偏移量。
最后把 ViewModel
中的 LiveData
数据转为 Compose
中可观察的 State
类型即可:
setContent {
BannerTheme {
val xState by viewModel.xState.observeAsState(0f)
val yState by viewModel.yState.observeAsState(0f)
Surface(color = MaterialTheme.colors.background) {
ThreeDImage(xState, yState)
}
}
}
OK,到这里代码就写完了,是不是很简单,核心代码只有一行: Modifier .offset(x = x.dp, y = y.dp)。
写在最后
Compose
在七月底也发布了正式版本,Google
对 Compose
也抱有非常大的希望,在各大社区开发者对 Compose
的反馈也都非常好,Compose
绝对是未来 Android UI
的未来。
本文只是给大家提供一个实现的思路,大家跟着文中的代码再优化下效果会更好的。
本文中所有代码都在 Github
中进行托管:github.com/zhujiang521… ,有需要的可以进行查看。
如果对你有帮助别忘了点赞,如果文中描述有问题请在评论区告诉我,不胜感激。