Ins and outs

I have been busy these days, so I haven’t found time to write articles. After checking my account, I found that my last article was written two months ago, and I always write an article in the previous month, so I am a little lazy.

Today in my blog, I saw an article from the DEVELOPMENT team of Freeze.com APP: The realization of the naked eye 3D effect of Freeze.com APP is very cool. Then I saw that someone has already realized this effect with Flutter. If Flutter can be realized, Compose must have no problem.

Implementation approach

First to see the effect of the realization, no other people’s so good-looking, we will see 😂.

The idea is actually very simple, is to put a two-layer layout (of course, the APP is three-layer layout, the principle is the same), let’s call the background and the foreground, the background does not move, the foreground follows the Angle of the device rotation to carry out the position offset can be.

Code code

Implementation ideas have, the following code code, first to register the sensor:

mSensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
// Gyro sensor
mMagneticSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)

mSensorManager.registerListener(this, mMagneticSensor, SensorManager.SENSOR_DELAY_GAME)
Copy the code

Get the coordinates to be offset in the callback:

override fun onSensorChanged(event: SensorEvent?). {
    if (event == null) return
    when (event.sensor.type) {
        Sensor.TYPE_ACCELEROMETER -> {
            // X,y, and z store the accelerations on axes x,y, and z, respectively
            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 -> {
            // the unit of the electromagnetic intensity in the three axes is micro-tesla (uT), or 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")}}}Copy the code

In Compose we’ve been talking about data-driven UI changes, and the same is true here. Let’s create a 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
    }

}
Copy the code

There are two methods provided in the ViewModel to change the coordinates of x and y, which are called in the refreshState method above:

private fun refreshState(x: Float, y: Float) {
    viewModel.onxStateChanged(x)
    viewModel.onyStateChanged(y)
}
Copy the code

Well, everything has, only owe the east wind, the following to draw the layout:

@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 = "Background",
        )
        Image(
            modifier = Modifier
                .fillMaxSize()
                .offset(x = x.dp, y = y.dp),
            painter = painterResource(id = R.drawable.icon_three_small),
            contentDescription = "Outlook")}},Copy the code

The ThreeDImage composable accepts two parameters, which are the x and Y values that the foreground needs to be offset. The entire layout is wrapped in Box, which places the background and foreground in turn, and then offsets the foreground according to the x and Y values that need to be offset.

This is easy, thanks to the Modifier for Compose, which calls the Modifier extension called offset to adjust the offset of the layout.

Change the ViewModel LiveData to the observable State type for Compose:

setContent {
    BannerTheme {
        val xState by viewModel.xState.observeAsState(0f)
        val yState by viewModel.yState.observeAsState(0f)
        Surface(color = MaterialTheme.colors.background) {
            ThreeDImage(xState, yState)
        }
    }
}
Copy the code

OK, to here the code is finished, is not very simple, the core code only one line: Modifier. Offset (x = X.dp, y = y.dp).

Write in the last

Compose was released at the end of July, and Google has high hopes for the app, as well as positive feedback from developers in the community. Compose is definitely the future of Android UI.

This article is just to provide you with a realization of the idea, we follow the code in the article to optimize the effect will be better.

All the code in this article is hosted on Github: github.com/zhujiang521… , you can check it if necessary.

Don’t forget to like it if it’s helpful and let me know if you have any questions in the comments section. Thank you very much.