안드로이드 Compose의 LazyColumn은 성능을 최적화하기 위해 사용되는 효율적인 리스트 뷰 컴포넌트입니다. LazyColumn은 필요한 데이터만 렌더링하여 메모리 사용을 줄이고 스크롤 성능을 향상시킵니다. 이를 통해 대량의 아이템을 가진 목록을 효율적으로 처리할 수 있습니다.
LazyColumn은 스크롤 가능한 단일 컬럼 목록을 만들기 위해 사용됩니다. 아이템이 스크롤 영역 내에 표시될 때만 실제로 그려지며, 스크롤이 일어날 때만 해당 아이템을 렌더링합니다. 이는 많은 아이템을 포함하는 목록에서 메모리와 렌더링 성능을 크게 개선합니다.
LazyColumn은 아이템의 높이를 예측하거나 측정할 필요 없이 컨테이너의 크기를 자동으로 조절합니다. 따라서 아이템의 크기가 다르거나 동적으로 변할 때도 효과적으로 처리할 수 있습니다.
구현 방식: RecyclerView는 기존의 View 시스템과 함께 동작하는 뷰 그룹으로, 아이템의 재사용과 스크롤 시에만 필요한 아이템을 렌더링합니다. 반면에 Compose의 LazyColumn은 Jetpack Compose의 일부로써, 선언적인 방식으로 UI를 작성하고 상태에 따라 UI를 다시 그립니다.
코드 간결성: Compose는 Kotlin 기반으로 작성되어 있으며, 선언적인 UI 작성을 통해 코드의 가독성과 유지 보수성을 향상시킵니다. LazyColumn을 사용하면 반복적인 작업을 간소화할 수 있으며, 코드의 양을 줄이고 간결하게 표현할 수 있습니다.
메모리 관리: LazyColumn은 스크롤 시에만 실제로 아이템을 렌더링하여 메모리 사용을 최적화합니다. 이는 대량의 데이터를 다룰 때 효과적입니다. RecyclerView는 뷰 재사용을 통해 메모리 관리를 지원하지만, 여전히 모든 아이템을 메모리에 로드하고 유지해야 합니다.
레이아웃 관리: RecyclerView는 레이아웃 관리자(Layout Manager)를 사용하여 아이템을 배치하고 스크롤을 처리합니다. 반면에 Compose의 LazyColumn은 자체적으로 아이템의 배치와 스크롤을 관리합니다. Compose는 새로운 레이아웃 시스템을 도입하여 더 유연하고 직관적인 방식으로 레이아웃을 다룰 수 있습니다.
상호 운용성: RecyclerView와 Compose의 LazyColumn은 서로 다른 시스템이므로 직접적인 상호 운용은 어렵습니다. 그러나 Jetpack Compose는 Android View와 호환되는 ComposeView를 제공하므로, 둘을 혼합하여 사용할 수 있습니다. 이를 통해 점진적으로 기존의 RecyclerView를 Compose로 대체하는 과정을 진행할 수 있습니다.
요약하면, Compose의 LazyColumn은 Kotlin과 Jetpack Compose를 기반으로 한 새로운 UI 프레임워크로, 선언적인 방식으로 UI를 작성하고 메모리 사용을 최적화하여 대량의 데이터를 처리합니다. 이를 통해 코드의 간결성과 유지 보수성이 향상됩니다.
import android.annotation.SuppressLint
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.provider.ContactsContract.Profile
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
import com.dong.study.ui.theme.MyBlue
import com.dong.study.ui.theme.MyComposeStudyTheme
import com.dong.study.utils.DummyDataProvider
import com.dong.study.utils.RandomUser
class LazyColumnActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyComposeStudyTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
ContentView()
}
}
}
}
}
@Composable
fun ContentView(){
Surface(color =MaterialTheme.colors.background) {
Scaffold(
backgroundColor = Color.White
,topBar = {
MyAppBar()
}) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(it)
) {
RandomUserListView(randomUsers = DummyDataProvider.userList)
}
}
}
}
@Composable
fun RandomUserListView(randomUsers: List<RandomUser>){
// LazyColumn은 RecyclerView와 유사하다.
LazyColumn(){
items(randomUsers){
RandomUserView(it)
}
}
}
// LazyColumn에 들어갈 아이템 생성
@Composable
fun RandomUserView(randomUser: RandomUser){
val typograpy = MaterialTheme.typography // ui.theme의 Type
Card( // Card View
modifier = Modifier
.padding(10.dp)
.fillMaxWidth() // fillMaxWidth로 가로를 꽉 차게
,elevation = 10.dp // 해당 뷰를 살짝 띄우기
, shape = RoundedCornerShape(12.dp) // 라운드 쉐입
) {
Row(modifier = Modifier.padding(10.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(10.dp) // Row안에 각 뷰들을 20dp 간격으로 띄움
) {
// Box(
// modifier = Modifier
// .size(width = 60.dp, height = 60.dp)
// .clip(CircleShape)
// .background(Color.Red)
// ) {}
ProfileImg(imgUrl = randomUser.profileImage)
Column() {
Text(text = randomUser.name,
style = typograpy.subtitle1
)
Text(text = randomUser.description,
style = typograpy.body1
)
}
}
}
}
// 이미지 불러오는 방법
// Gilde 사용 -> gradle에 의존성 추가
// menifest에 인터넷 사용권한 추가
@SuppressLint("UnrememberedMutableState")
@Composable
fun ProfileImg(imgUrl: String, modifier: Modifier = Modifier){
// 이미지 비트맵
val bitmap : MutableState<Bitmap?> = mutableStateOf(null)
val imageModifier = modifier
.size(width = 50.dp, height = 50.dp)
// .clip(RoundedCornerShape(10.dp)) // 사각형에 라운드 주는거
.clip(CircleShape) // 원형으로 만드는거
Glide.with(LocalContext.current)
.asBitmap() // 뭘로 변활 할 것?
.load(imgUrl) // 어디서 가지고 올 것?
.into(object : CustomTarget<Bitmap>(){ // 어디에 넣을 것?
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
// resource가 다운 받은 이미지
bitmap.value = resource
}
override fun onLoadCleared(placeholder: Drawable?) {
}
})
// bitmap에 데이터가 있다면? -> 이미지를 다운 받았다면
bitmap.value?.asImageBitmap()?.let {
Image(bitmap = it
, contentScale = ContentScale.Fit
, contentDescription = null
, modifier = imageModifier
)
} ?: Image(painter = painterResource(id = R.drawable.ic_empty_person_24) // 다운 받은 이미지가 없는 경우
, contentScale = ContentScale.Fit
, contentDescription = null
, modifier = imageModifier
)
}
@Composable
fun MyAppBar(){
TopAppBar(
elevation = 10.dp
, backgroundColor = MyBlue
, modifier = Modifier.height(58.dp) // TopAppBar의 높이 지정
) { // elevation: 뷰를 살짝 띄움
Text(
text = "LAZYColumn"
, modifier = Modifier
.padding(8.dp)
.align(Alignment.CenterVertically)
, fontSize = 18.sp
, fontWeight = FontWeight.Black
)
}
}
@Preview(showBackground = true)
@Composable
fun LazyColumnPreview(){
MyComposeStudyTheme(){
ContentView()
}
}