Масштабирование, вращение и перемещение

Последнее обновление: 19.04.2024

Мастабирование

С помощью двух пальцев можно выполнить масштабирование контента и создать эффекта увеличения (при разведении пальцев) или уменьшения масштаба (при сведении пальцев). Этот тип жестов обрабатывается с помощью модификатора transformable(), который принимает в качестве параметра состояние типа TransformableState:

fun Modifier.transformable(
    state: TransformableState,
    lockRotationOnZoomPan: Boolean = false,
    enabled: Boolean = true
): Modifier

Для создания объекта TransformableState применяется функция rememberTransformableState(), которая принимает функцию с тремя параметрами:

(zoomChange: Float, panChange: Offset, rotationChange: Float) -> Unit
  • zoomChange: значение Float, которое обновляется при выполнении жестов масштабирования.

  • panChange: значение Offset, которое содержит текущие значения смещения x и y. Это значение обновляется, когда с помощью жестов производится перемещение целевого компонента.

  • rotationChange: значение Float, которое представляет текущее изменение угла с помощью жестов вращения.

При вызове функции rememberTransformableState() необходимо объявить все три параметра, даже если их не предполагается использовать. Типичное объявление TransformableState, которое отслеживает изменения масштаба, может выглядеть следующим образом:

var scale by remember { mutableStateOf(1f) }

val state = rememberTransformableState { 
    scaleChange, offsetChange, rotationChange -> scale *= scaleChange
}

Затем созданное состояние можно передать в вызов модификатора transformable():

Composable(modifier = Modifier.transformable(state = state) {

    }
)

Например, рассмотрим следующее приложение:

package com.example.helloapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.rememberTransformableState
import androidx.compose.foundation.gestures.transformable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size

import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp


class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            var scale by remember { mutableStateOf(1f) }

            val state = rememberTransformableState {
                scaleChange, offsetChange, rotationChange -> scale *= scaleChange
            }

            Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
                Box(Modifier
                    .graphicsLayer(scaleX = scale, scaleY = scale)
                    .transformable(state = state)
                    .background(Color.DarkGray)
                    .size(150.dp)
                )
            }
        }
    }
}

Здесь внутренний компонент Box использует модификатор transformable() для отслеживания масштабирования. По мере выполнения жеста масштабирования состояние масштаба - переменная scale будет обновляться. Чтобы отразить эти изменения, мы обращаемся к графическому слою компонента, установив параметры scaleX и scaleY в текущее состояние масштаба:

Box(Modifier
    .graphicsLayer(scaleX = scale, scaleY = scale)

Таким образом, с помощью жеста масштабирования мы сможем изменять размер вложенного компонента Box:

Обработка масштабирования и модификатор transformable в Jetpack Compose Kotlin Android

Вращение

Аналогично с помощью того же модификатора transformable() можно обрабатывать жесты вращения:

package com.example.helloapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.rememberTransformableState
import androidx.compose.foundation.gestures.transformable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size

import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp


class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            var angle by remember { mutableStateOf(0f) }
            val state = rememberTransformableState {
                scaleChange, offsetChange, rotationChange -> angle += rotationChange
            }

            Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
                Box(Modifier
                    .graphicsLayer(rotationZ = angle)
                    .transformable(state = state)
                    .background(Color.DarkGray)
                    .size(200.dp)
                )
            }
        }
    }
}

В данном случае для отслеживания угла поворота определяем переменную angle и затем изменяем ее, прибавляя к ней значение из rotationChange. А у компонента Box устанавливаем угол поворота с помощью параметра rotationZ модификатора graphicsLayer

Обработка вращения и модификатор transformable в Jetpack Compose Kotlin Android

Перемещение

И последний тип трансформаций - перемещение компонента жестами делается аналогично:

package com.example.helloapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.rememberTransformableState
import androidx.compose.foundation.gestures.transformable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size

import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp


class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            var offset by remember { mutableStateOf(Offset.Zero)}
            val state = rememberTransformableState {
                scaleChange, offsetChange, rotationChange -> offset += offsetChange
            }

            Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
                Box(Modifier
                    .graphicsLayer(
                        translationX = offset.x,
                        translationY = offset.y
                    )
                    .transformable(state = state)
                    .background(Color.DarkGray)
                    .size(200.dp)
                )
            }
        }
    }
}

Здесь для хранения данных о перемещении определяем переменную offset:

var offset by remember { mutableStateOf(Offset.Zero)}

Причем эта переменная представляет объект Offset и будет содержать смещение сразу по обоим осям - X и Y.

При обработке перемещения к этой переменной прибавляем значения перемещения из параметра offsetChange:

val state = rememberTransformableState {
    scaleChange, offsetChange, rotationChange -> offset += offsetChange
}

Для установки привязки компонента Box к значениям из переменной offset используем параметры translationX и translationY функции-модификатора graphicsLayer():

Box(Modifier
    .graphicsLayer(
        translationX = offset.x,
        translationY = offset.y
)

При необходимости можно объединить обработку нескольких типов жестов:

package com.example.helloapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.rememberTransformableState
import androidx.compose.foundation.gestures.transformable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size

import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp


class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            var scale by remember { mutableStateOf(1f) }
            var angle by remember { mutableStateOf(0f) }
            var offset by remember { mutableStateOf(Offset.Zero)}
            val state = rememberTransformableState {
                scaleChange, offsetChange, rotationChange ->
                    scale *= scaleChange
                    angle += rotationChange
                    offset += offsetChange
            }

            Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
                Box(Modifier
                    .graphicsLayer(
                        scaleX = scale,
                        scaleY = scale,
                        rotationZ = angle,
                        translationX = offset.x,
                        translationY = offset.y
                    )
                    .transformable(state = state)
                    .background(Color.DarkGray)
                    .size(200.dp)
                )
            }
        }
    }
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850