티스토리 뷰


이번 활용 예제에서 만들어볼 화면은 재생 플레이어입니다.
왼쪽 화면에서 해당 영역을 위로 스와이프 시 오른쪽 화면으로 애니메이션 동작을 진행합니다.
우선 해당 화면에 들어갈 아이템들을 MotionLayout 안에 설정해 둡니다.
val context = LocalContext.current
val scene = remember {
context.resources
.openRawResource(R.raw.player)
.readBytes()
.decodeToString()
}
var isDetail by remember { mutableStateOf(false) }
val progress by animateFloatAsState(
targetValue = if (isDetail) 1f else 0f,
animationSpec = tween(1000)
)
MotionLayout(
motionScene = MotionScene(content = scene),
progress = progress
) {
/** 배경 **/
Box(
modifier = Modifier
.background(Color(0xFF282828))
.layoutId("background")
)
/** 이미지 **/
Image(
painter = painterResource(id = R.drawable.ic_launcher_background),
contentDescription = "image",
contentScale = ContentScale.Crop,
modifier = Modifier.layoutId("image")
)
/** 타이틀 **/
Text(
text = "제목이 들어갑니다.",
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color = Color.White,
modifier = Modifier.layoutId("title")
)
/** 서브 타이틀 **/
Text(
text = "[서브 내용]",
fontSize = 12.sp,
color = Color(0xFFB9B9B9),
modifier = Modifier.layoutId("subTitle")
)
/** 재생버튼 라운드 **/
Box(modifier = Modifier
.clip(CircleShape)
.background(Color(0xFF656565))
.layoutId("playBg")
)
/** 재생버튼 **/
Image(
painter = painterResource(id = R.drawable.ic_play),
contentDescription = "play",
modifier = Modifier
.layoutId("play")
)
/** 다음 버튼 **/
Image(
painter = painterResource(id = R.drawable.ic_next),
contentDescription = "next",
modifier = Modifier
.layoutId("next")
)
/** 이전 버튼 **/
Image(
painter = painterResource(id = R.drawable.ic_prev),
contentDescription = "prev",
modifier = Modifier
.layoutId("prev")
)
/** 축소 버튼 **/
Image(
painter = painterResource(id = R.drawable.ic_arrow_down),
contentDescription = "down",
modifier = Modifier
.layoutId("down")
)
}
위치나 사이즈 등 변하는 요소를 제외하고 고정인 내용을 설정해 줍니다.
그다음 모션 동작 수행을 위한 Json5 파일을 생성합니다.
지난 포스팅에서 간단한 사용은 진행하였으니 그 내용을 제외하고 설명하겠습니다.
전체 코드는 하단에 있습니다.
ConstraintSets: {
start: {
...
image: {
top: ['background', 'top', 0],
bottom: ['background', 'bottom', 0],
start: ['background', 'start', 0],
width: 90,
height: 'spread'
},
....
}
end: {
...
image: {
top: ['parent', 'top', 42],
start: ['parent', 'start', 0],
end: ['parent', 'end', 0],
width: 'parent',
height: 300
},
...
}
}
width와 height의 값을 주는 방법은 4가지가 있습니다. (문서에 있는 내용은 아니라서 더 있을 순 있습니다.)
- 직접 입력 : dp 값을 직접 입력하여 사이즈를 조절할 수 있습니다.
- wrap : 자신의 사이즈를 사이즈를 설정합니다.
- parent : 부모의 사이즈를 사이즈를 설정합니다.
- spread : 제약조건에 해당하는 만큼의 사이즈를 설정합니다.
당연한 내용이지만 top, bottom, start, end 설정 시 parent와 설정한 id를 통해 제약조건을 설정할 수 있습니다.
ConstraintSet에 설정할 수 있는 종류는 다음과 같습니다.
- width / height
- start / end / top / bottom
- left / right / center / centerHorizontally /centerVertically
- alpha
- scaleX / scaleY
- translationX / translationY / translationZ
- pivotX / pivotY
- rotationX / rotationY / rotationZ
- visibility (= visible, invisible, gone)
- custom - custom properties
ConstraintSets: {
start: {
...
title: {
top: ['background', 'top', 12],
bottom: ['subTitle', 'top', 0],
start: ['image', 'end', 10],
end: ['play', 'start', 10],
width: 'spread',
},
....
}
end: {
...
title: {
top: ['image', 'bottom', 25],
start: ['parent', 'start', 0],
end: ['parent', 'end', 0],
scaleX: 1.6,
scaleY: 1.6
},
...
}
}
custom을 통해 fontSize를 가져갈 수도 있지만
그렇게 하면 높이 값이 자동으로 바뀌지 않아서 높이 값을 지정해 주지 않으면 텍스트가 잘리는 현상이 발생하였습니다.
또한 width의 값을 설정하지 않을 시 사이즈의 변화가 발생하지 않았습니다.
이번에는 예제라서 사이즈를 직접 설정하긴 애매했습니다.
그래서 다른 방법으로 사용한 것이 스케일을 조정을 통해 사이즈를 변화를 주는 것이었습니다.
다만 이 방식의 경우 타이틀이 길어지거나, 디바이스의 비율에 따라 글씨가 화면 밖으로 나갈 수 있습니다.
ConstraintSets: {
...
},
Transitions: {
default: {
from: 'start',
to: 'end',
onSwipe: {
anchor: 'background',
direction: 'up',
side: 'top',
},
}
}
마지막으로 onSwipe 관련입니다.
onSwipe의 경우 ConstraintLayout의 버전이 1.1.0-alpha01 이상일 때에만 사용이 가능합니다.
anchor는 스와이프를 감지하는 Composable의 id입니다.
dirdection은 스와이프 모션으로 “up", "down", "left", "right", "start", "end” 중 상황에 맞게 이용하면 됩니다.
side 역시 "top", 'left', 'right', 'bottom', 'middle', 'start", 'end’ 중 상황에 맞게 선택하시면 됩니다.
주의할 점은 위에서 설정한 progress랑 별도로 동작을 진행이 됩니다.
이걸 동기화 시키는 방법에 관해서는 아직 찾지 못했습니다.
그래서 background에 클릭 이벤트를 설정하게 되면 클릭과 스와이프가 겹치게 되면서 이상한 애니메이션이 될 수 있으니 주의해서 사용하시길 바랍니다.
이 외의 설정은 다음과 같습니다.
- scale
- maxVelocity
- maxAccel
- mode
- touchUp
- springMass
- springStiffness
- springDamping
- stopThreshold
- springBoundary
해당 설명 관해서는 링크 남기겠습니다
GitHub - androidx/constraintlayout: ConstraintLayout is an Android layout component which allows you to position and size widget
ConstraintLayout is an Android layout component which allows you to position and size widgets in a flexible way - GitHub - androidx/constraintlayout: ConstraintLayout is an Android layout component...
github.com
전체 코드입니다.
{
ConstraintSets: {
start: {
background: {
bottom: ['parent', 'bottom', 0],
start: ['parent', 'start', 0],
end: ['parent', 'end', 0],
width: 'parent',
height: 64,
},
image: {
top: ['background', 'top', 0],
bottom: ['background', 'bottom', 0],
start: ['background', 'start', 0],
width: 90,
height: 'spread'
},
title: {
top: ['background', 'top', 12],
bottom: ['subTitle', 'top', 0],
start: ['image', 'end', 10],
end: ['play', 'start', 10],
width: 'spread',
height: 'wrap',
},
subTitle: {
top: ['title', 'bottom', 5],
bottom: ['background', 'bottom', 12],
start: ['title', 'start', 0],
end: ['title', 'end', 0],
width: 'spread',
height: 'wrap',
},
play: {
top: ['background', 'top', 0],
bottom: ['background', 'bottom', 0],
end: ['next', 'start', 10],
width: 24,
height: 24
},
playBg: {
top: ['play', 'top', 0],
bottom: ['play', 'bottom', 0],
start: ['play', 'start', 0],
end: ['play', 'end', 0],
width: 24,
height: 24,
visibility: 'gone'
},
next: {
top: ['background', 'top', 0],
bottom: ['background', 'bottom', 0],
end: ['background', 'end', 20],
width: 24,
height: 24
},
prev: {
top: ['play', 'top', 0],
bottom: ['play', 'bottom', 0],
end: ['play', 'start', 0],
width: 0,
height: 0
},
down: {
top: ['parent', 'top', 0],
start: ['parent', 'start', 0],
width: 0,
height: 0
}
},
end: {
background: {
top: ['parent', 'top', 0],
bottom: ['parent', 'bottom', 0],
start: ['parent', 'start', 0],
end: ['parent', 'end', 0],
width: 'parent',
height: 'parent'
},
image: {
top: ['parent', 'top', 42],
start: ['parent', 'start', 0],
end: ['parent', 'end', 0],
width: 'parent',
height: 300
},
title: {
top: ['image', 'bottom', 25],
start: ['parent', 'start', 0],
end: ['parent', 'end', 0],
scaleX: 1.6,
scaleY: 1.6
},
subTitle: {
top: ['title', 'bottom', 10],
start: ['parent', 'start', 0],
end: ['parent', 'end', 0],
scaleX: 1.3,
scaleY: 1.3
},
play: {
top: ['subTitle', 'bottom', 50],
start: ['parent', 'start', 0],
end: ['parent', 'end', 0],
width: 50,
height: 50
},
playBg: {
top: ['play', 'top', 0],
bottom: ['play', 'bottom', 0],
start: ['play', 'start', 0],
end: ['play', 'end', 0],
width: 70,
height: 70
},
next: {
top: ['play', 'top', 0],
bottom: ['play', 'bottom', 0],
start: ['play', 'end', 50],
width: 50,
height: 50
},
prev: {
top: ['play', 'top', 0],
bottom: ['play', 'bottom', 0],
end: ['play', 'start', 50],
width: 50,
height: 50
},
down: {
top: ['parent', 'top', 0],
bottom: ['image', 'top', 0],
start: ['parent', 'start', 15],
width: 24,
height: 24
}
}
},
Transitions: {
default: {
from: 'start',
to: 'end',
onSwipe: {
anchor: 'background',
direction: 'up',
side: 'top',
},
}
}
}
결과 화면입니다.
'안드로이드 > 코드' 카테고리의 다른 글
안드로이드 Compose BottomSheet (0) | 2022.11.06 |
---|---|
Compose QR코드 스캐너 (2) | 2022.11.04 |
Compose MotionLayout (0) | 2022.11.02 |
Compose ConstraintLayout (0) | 2022.11.02 |
Compose Naver Map (1) | 2022.10.29 |
- Total
- Today
- Yesterday
- Compose ConstraintLayout
- Compose BottomSheet
- Compose ModalBottomSheetLayout
- WebView
- 포켓몬 도감
- Compose 네이버 지도
- Android
- Compose MotionLayout
- 안드로이드
- 웹뷰
- Android Compose
- Retrofit
- Gradient
- Compose QRCode Scanner
- LazyColumn
- WorkManager
- compose
- Compose BottomSheetDialog
- Compose 네이버 지도 api
- Compose Naver Map
- Worker
- Pokedex
- Duplicate class fond 에러
- Compose BottomSheetScaffold
- 안드로이드 구글 지도
- column
- Fast api
- Kotlin
- Duplicate class found error
- Row
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |