https://developer.android.com/courses/pathways/compose
Jetpack Compose Tutorial입니다.
공식문서를 해석하며 Jetpack Compose를 학습합니다.
@Composable 을 이용하여 앱의 구성을 정의한다.
@Composable 위에 @PREVIEW 를 달아주면 미리보기가 가능하다.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MessagingCard(name = "KOTLIN")
}
}
}
@Composable
fun MessagingCard(name:String){
Text(text = "Hello ${name}!!!")
}
@Preview
@Composable
fun PreviewMessageCard() {
MessagingCard("Android")
}
여러개의 텍스트를 추가할때는 dataClass 를 이용하여 매개변수로 넣어준다.
data class Message(val author : String,val body : String)
@Composable
fun MessagingCard(msg:Message){
Text(text = msg.author)
Text(text = msg.body)
}
@Preview
@Composable
fun PreviewMessageCard() {
MessagingCard(
msg = Message("Colleague","Hey, take a look at Jetpack Compose, it's great!"))
}
이렇게 만들면 텍스트에 대한 정렬 방식이 선언되지 않았기 때문에 두개의 텍스트가 겹치게된다.
Column기능을 사용하면 요소를 세로로 정렬할 수 있다. Row를 사용하여 항목을 가로로 정렬할 수 있으며 Box를 사용하여 요소를 쌓을 수 있다.
@Composable
fun MessagingCard(msg:Message){
Column {
Text(text = msg.author)
Text(text = msg.body)
}
}
@Preview
@Composable
fun PreviewMessageCard() {
MessagingCard(
msg = Message("Colleague","Hey, take a look at Jetpack Compose, it's great!"))
}
Image를 가져올때는 리소스관리자를 이용하여 가져올 수 있다.
@Composable
fun MessagingCard(msg:Message){
Row {
Image(
painter = painterResource(id = R.drawable.profile),
contentDescription = "Contact profile picture"
)
Column {
Text(text = msg.author)
Text(text = msg.body)
}
}
}
요소의 간격을 조절할 수 있다. Compose는 modifiers를 사용한다. 이는 컴포저블(=요소)의 크기,레이아웃,모양을 변경하거나 요소를 클릭가능하게 만드는 것과 같은 상위 수준 상호 작용을 추가할 수 있습니다.
@Composable
fun MessagingCard(msg: Message) {
//modifier를 추가해준다.
//Modifier.padding을 이용하여 padding을 설정한다.매개변수로
//start = all,top = all,end = all,bottom = all 을 받을 수 있다.
Row(modifier = Modifier.padding(all = 8.dp)) {
Image(
painter = painterResource(id = R.drawable.profile),
contentDescription = "Contact profile picture",
//Image의 매개변수에 modifier를 선언해 준다.
//.size를 통하여 이미지의 크기를 조절한다.
//clip을 통하여 모양을 정할 수 있다.
modifier = Modifier
.size(40.dp)
.clip(CircleShape)
)
//Size 메서드 내부는 다음과 같다.
//fun Modifier.size(size: Dp) = this.then(
// SizeModifier(
// minWidth = size,
// maxWidth = size,
// minHeight = size,
// maxHeight = size,
// enforceIncoming = true,
// inspectorInfo = debugInspectorInfo {
// name = "size"
// value = size
// }
// )
//)
//clip의 내부는 다음과 같으며 Shape의 종류는 circle,rectangle과 같은 모양부터 button까지 너무 많으므로 추후에 정리하자.
//fun Modifier.clip(shape: Shape) = graphicsLayer(shape = shape, clip = true)
//Spacer를 통하여 빈 공간을 넣어준다. margin이라고 생각할 수도 있고, 이전의 안드로이드에서 View를 이용한 빈 공간이라고도 생각된다.
//2개를 동시에 적용하려면
//Spacer(modifier = Modifier.width(100.dp).height(100.dp))
Spacer(modifier = Modifier.width(8.dp))
Column {
Text(text = msg.author)
Spacer(modifier = Modifier.height(4.dp))
Text(text = msg.body)
}
}
}
Compose는 머티리얼 디자인 원칙을 지원한다.
Jetpack Compose는 기본적으로 Material Design 및 해당 UI 요소의 구현을 제공합니다.
머티리얼 디자인 스타일을 사용하여 MessageCard 컴포저블의 모양을 개선할 것입니다.
시작하려면 이 경우 MessageCard
프로젝트에서 생성된 Material 테마로 함수를 래핑합니다
함수 에서 ComposeTutorialTheme
모두 수행하십시오.@PreviewsetContent
머티리얼 디자인은 색상, 타이포그래피, 모양의 세 가지 기둥을 중심으로 구성됩니다. 하나씩 추가해보자
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
PracticejetpackTheme {
MessagingCard(Message("Android", "Jetpack Compose"))
}
}
}
}
@Preview
@Composable
fun PreviewMessageCard() {
PracticejetpackTheme {
MessagingCard(
msg = Message("Colleague", "Hey, take a look at Jetpack Compose, it's great!")
)
}
}
래핑된 테마의 색상으로 스타일을 지정하는 것은 쉽고 색상이 필요한 모든 곳에서 테마의 값을 사용할 수 있습니다.
제목의 스타일을 지정하고 이미지에 테두리를 추가해 보겠습니다.
@Composable
fun MessagingCard(msg: Message) {
Row(modifier = Modifier.padding(all = 8.dp)) {
Image(
painter = painterResource(id = R.drawable.profile),
contentDescription = "Contact profile picture",
modifier = Modifier
.size(40.dp)
.clip(CircleShape)
//border를 이용하면 해당 이미지에 테두리를 넣어 줄 수 있다. 두께와 색, 그리고모양을 넣어준다.
**.border(4.dp,MaterialTheme.colors.secondary, RectangleShape)**
)
Spacer(modifier = Modifier
.width(100.dp)
.height(100.dp))
Column {
Text(text = msg.author,
//setTextColors와 같은 역할을 한다. Text의 매개변수에 넣어주면 된다.
**color = MaterialTheme.colors.secondaryVariant
)**
Spacer(modifier = Modifier.height(4.dp))
Text(text = msg.body)
}
}
}
Material Typography 스타일은 MaterialTheme에서 사용할 수 있으며, Text 컴포저블에 추가하기만 하면 됩니다.
@Composable
fun MessagingCard(msg: Message) {
Row(modifier = Modifier.padding(all = 8.dp)) {
Image(
painter = painterResource(id = R.drawable.profile),
contentDescription = "Contact profile picture",
modifier = Modifier
.size(40.dp)
.clip(CircleShape)
.border(4.dp, MaterialTheme.colors.secondary, RectangleShape)
)
Spacer(
modifier = Modifier
.width(100.dp)
.height(100.dp)
)
Column {
Text(
text = msg.author,
color = MaterialTheme.colors.secondaryVariant,
//style이라는 매개변수에 지정해준다.
**style = MaterialTheme.typography.subtitle2**
)
Spacer(modifier = Modifier.height(4.dp))
Text(text = msg.body,
**style = MaterialTheme.typography.subtitle1**
)
}
}
}
//typography의 요소들은 다음과 같이 있으며 기본값이다. 추가적으로 유저가 설정할 수 있다.
//class Typography internal constructor(
// val h1: TextStyle,
// val h2: TextStyle,
// val h3: TextStyle,
// val h4: TextStyle,
// val h5: TextStyle,
// val h6: TextStyle,
// val subtitle1: TextStyle,
// val subtitle2: TextStyle,
// val body1: TextStyle,
// val body2: TextStyle,
// val button: TextStyle,
// val caption: TextStyle,
// val overline: TextStyle
//)
@Composable
fun MessagingCard(msg: Message) {
Row(modifier = Modifier.padding(all = 8.dp)) {
Image(
painter = painterResource(id = R.drawable.profile),
contentDescription = "Contact profile picture",
modifier = Modifier
.size(40.dp)
.clip(CircleShape)
.border(4.dp, MaterialTheme.colors.secondary, RectangleShape)
)
Spacer(
modifier = Modifier
.width(100.dp)
.height(100.dp)
)
Column {
Text(
text = msg.author,
color = MaterialTheme.colors.secondaryVariant,
style = MaterialTheme.typography.subtitle2
)
Spacer(modifier = Modifier.height(4.dp))
//shape를 통하여 텍스트 영역을 라운드로 감쌀수 있으며 기본 바탕색이 지원뙨다.
**Surface(shape = MaterialTheme.shapes.medium, elevation = 1.dp) {**
Text(
text = msg.body,
modifier = Modifier.padding(all = 4.dp),
style = MaterialTheme.typography.body2
)
}
}
}
}
//Shaps 클래스를 보면 다음과 같이 되어있다.
//class Shapes(
/**
* Shape used by small components like [Button] or [Snackbar]. Components like
* [FloatingActionButton], [ExtendedFloatingActionButton] use this shape, but override
* the corner size to be 50%. [TextField] uses this shape with overriding the bottom corners
* to zero.
*/
// val small: CornerBasedShape = RoundedCornerShape(4.dp),
/**
* Shape used by medium components like [Card] or [AlertDialog].
*/
// val medium: CornerBasedShape = RoundedCornerShape(4.dp),
/**
* Shape used by large components like [ModalDrawer] or [ModalBottomSheetLayout].
*/
// val large: CornerBasedShape = RoundedCornerShape(0.dp)
//)
기본적으로 Compose는 다크테마를 지원하며 다크테마 설정시 자동적으로 색상을 바꿔줍니다.
@Preview(name = "Light Mode")
@Preview(
uiMode = Configuration.UI_MODE_NIGHT_YES,
showBackground = true,
name = "Dark Mode"
)
@Composable
fun PreviewMessageCard() {
PracticejetpackTheme {
MessagingCard(
msg = Message("Colleague", "Hey, take a look at Jetpack Compose, it's great!")
)
}
}
하나의 메시지로 대화하는 것은 조금 외로운 느낌이 드니, 대화를 하나 이상의 메시지로 바꿔보자. Conversation
여러 메시지를 표시 하는 함수를 만들어야 합니다. 이 사용 사례의 경우 Compose LazyColumn
및 LazyRow
이러한 구성 가능 요소는 화면에 표시되는 요소만 렌더링하므로 긴 목록에 대해 매우 효율적으로 설계되었습니다. 동시에 RecyclerView
XML 레이아웃의 복잡성을 피할 수 있습니다.
LazyColumn
이 코드 조각에서 항목 하위 항목이 있는 것을 볼 수 있습니다 . List
매개변수로 사용하고 해당 람다는 의 인스턴스인 이름을 지정한 매개변수 ( message
원하는 대로 이름을 지정할 수 있음)를 Message
받습니다. 간단히 말해서 이 람다는 제공된 의 각 항목에 대해 호출됩니다 List
. 이 샘플 데이터세트 를 프로젝트로 가져와서 대화를 빠르게 부트스트랩할 수 있습니다.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
PracticejetpackTheme {
Column() {
MessagingCard(Message("Android", "Jetpack Compose"))
Conversation(SampleData.conversationSample)
}
}
}
}
}
//매개변수로 메세지를 받습니다. MessagingCard()의 매개변수.
//LazyColumn을 선언하므로써 하위 항목이 있다는 것을 알 수 있고, List형식의 매개변수를 받습니다.
//이 람다는 List의 각 항목에 대해 호출이 됩니다.forEach와 비슷함.
@Composable
fun Conversation(messages: List<Message>) {
LazyColumn {
items(messages) { message ->
MessagingCard(message)
}
}
}
@Preview
@Composable
fun PreviewConversation() {
PracticejetpackTheme {
Conversation(SampleData.conversationSample)
}
}
@Composable
fun MessagingCard(msg: Message) {
Row(modifier = Modifier.padding(all = 8.dp)) {
Image(
painter = painterResource(R.drawable.profile),
contentDescription = null,
modifier = Modifier
.size(40.dp)
.clip(CircleShape)
.border(1.5.dp, MaterialTheme.colors.secondaryVariant, CircleShape)
)
Spacer(modifier = Modifier.width(8.dp))
//메서드 안에 isExpanded 선언.
//remember는 람다식 내부의 값을 기억하고 있습니다.
//상태 변경을 추적합니다. 메모리에 로컬 상태를 저장하고 추적합니다.
//값이 변경될 때마다 자동적으로 다시 그려집니다.
**var isExpanded by remember { mutableStateOf(false) }**
//Column안의 요소에게 clickable를 통하여 클릭이 가능하게 변경합니다.
**Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) {**
Text(
text = msg.author,
color = MaterialTheme.colors.secondaryVariant,
style = MaterialTheme.typography.subtitle2
)
Spacer(modifier = Modifier.height(4.dp))
Surface(
shape = MaterialTheme.shapes.medium,
elevation = 1.dp,
) {
//text의 요소입니다.
//isExpanded에 따라 maxLines를 변경해줍니다.
**Text(
text = msg.body,
modifier = Modifier.padding(all = 4.dp),
maxLines = if (isExpanded) Int.MAX_VALUE else 1,
style = MaterialTheme.typography.body2
)**
}
}
}
}