- 얼마 전에 리플 효과를 적용시킨 FAB 버튼을 구현했었다.- 리플 내부에는 클릭 상태를 감지하는 interactionSource를 포함하고 있다. 이를 통해 리플뿐만 아니라 버튼 자체의 스케일이 변하는 애니메이션까지 추가해 보겠다.interactionSource = interactionSource,indication = rememberRipple( color = colorResource(R.color.color_system_label_default_light).copy(alpha = 0.16f), bounded = true),onClick = onClick - 먼저 아래 값들을 선언해 준다. 모두 pressed 상태를 감지하기 위한 요소이다.val interactionSource = rem..
- 기존에 아래와 같이 메모 데이터를 저장하는 테이블이 있다.@Entity(tableName = "save_model")data class Memo( @PrimaryKey(autoGenerate = true) val id: Int = 0, var title: String, var detail: String, val dtCreated: Long, var dtUpdated: Long, var color: String)- 링크 데이터를 추가하기 위해 String 타입 필드를 추가하려고 한다. @Entity(tableName = "save_model")data class Memo( @PrimaryKey(autoGenerate = true) val id: Int = 0, ..
- 기존의 XML 화면에서 Compose 기반 컴포넌트 활용을 위해 ComposeView를 사용하고 있었다. - 그리고 인앱 지정 컬러를 변경할 때마다 ComposeView에 걸린 컴포저블의 tint도 변경을 해줘야 했는데, 프로퍼티로 들어가는 속성 값은 변경되지만 실제 컴포저블 set 로직까지는 진입이 안 되는 것을 확인했다.mainAddBtn.setContent { TbFloatingActionButton( androidx.compose.ui.graphics.Color(AppColor()), ... - 이유는 setContent {} 자체가 최초 한 번만 컴포지션을 설정하도록 구성되어 있는 것이다. 따라서 이미 컴포지션이 설정된 ComposeView에 대해서는 발동하지 ..
- 보통 버튼이나 FAB 클릭하면 명암이 짙어지는 Ripple 효과가 발생하는 것을 볼 수 있다. - 이번엔 Text를 가진 상위 컴포저블로부터 onClick 이벤트를 받아 하위 Text의 굵기를 변경하는 작업을 진행했다. - 일단 아래 두 개 값이 pressed 상태를 경신하고 체크하는 기본 요소가 된다.val interactionSource = remember { MutableInteractionSource() }val isPressed by interactionSource.collectIsPressedAsState() - MutableInteractionSource : InteractionSource는 컴포넌트에서 발생하는 사용자 상호작용을 감지하는 채널로서 Pressed, Focused, Drag..
- 자사 구글 인앱 결제 모듈을 작년 초인가에 리팩토링 했던 기억이 난다. - 특별한 문제없이 정상 서비스되고 있었는데, 저번주 앱 심사에서 갑자기 거절당했다. 이유를 살펴보니 가격이 명시되어야 하는 UI에 Free라고 기입되어 실제 구글 결제 UI에서 나오는 가격과 다르다는 이야기였다; - 상단 이미지가 인앱 Text인데, 똑같은 수량의 달러 표시가 아니다. - Free 부분을 원래 인앱 결제 모듈에서 제공하는 ProductDetails를 통해 자사에서 지정한 가격을 실제 각 국가별 통화, 환율에 맞게 내려주는 것인데 재현은 안되지만 정황을 봤을 때 그 값이 Free라는 String으로 내려오는 것으로 보인다. (또는 0 이거나) - 따라서 가격을 찾는 코드를 아래와 같이 수정했다.val value =..
- 상위 컴포저블로 Surface를 활용한 컴포넌트에 알파 값이 적용된 컬러를 적용했더니 아래처럼 색상이 깨지는 문제가 발생했다.Surface( modifier = Modifier .height(size.value) .clip(RoundedCornerShape(dimensionResource(R.dimen.corner_radius_06))) .tbRipple { onClick.invoke() }, color = if (isDisabled) colorResource(R.color.color_system_fill_disabled_light) else colorResource(appearance.color), contentColor = colorResour..
- 기존에 아래와 같은 Vertical Grid Composable을 사용하고 있었다.LazyVerticalGrid( columns = GridCells.Fixed(2), contentPadding = PaddingValues(8.dp), modifier = Modifier.fillMaxSize()) { items(filteredList) { item -> MemoItemView(item, onClick = { selectedMemo = item coroutineScope.launch { sheetState.show() } }, onEdit = { onEdit.invoke(it) }, o..
- Hilt를 기반으로 Network Module을 만들고, Retrofit provide 함수를 생성했을 때, 다음과 같이 baseUrl을 고정해 놓았었다.@Singleton@Providesfun provideRetrofit( okHttpClient: OkHttpClient): Retrofit { return Retrofit.Builder() .client(okHttpClient) .baseUrl(ServerStatus.API_URL_PRFIX) .addConverterFactory(GsonConverterFactory.create()) .build()} - 하지만 가끔 다른 Base URL을 요구하는 api를 사용해야 하는 경우도 있다. -..
- 이전에 Image 컴포저블을 활용하여 중첩된 원형 프로필 이미지를 구현했었다. - 하지만 프로필 이미지가 기본 drawable이 아니고, File이나 네트워크로부터 수신받아야 하는 경우가 있다. - 이 경우에 Coil의 AsyncImage를 활용하여 보여주도록 처리했다.//A composable that executes an ImageRequest asynchronously and renders the result.@Composablefun AsyncImage( model: Any?, contentDescription: String?, modifier: Modifier = Modifier, transform: (State) -> State = DefaultTransform, ..
- 이미지를 중첩시키고 싶다면 Box 컴포저블을 사용하면 된다. Z 축으로 쌓이는 컨테이너 구조이다. - Image 말고 다른 컴포저블들도 중첩된다.@Composablefun Avatar(~~) { Box( modifier = Modifier.wrapContentSize() ) { Image(~~~) Image( painter = painterResource(id = R.drawable.abc), contentDescription = "Profile", modifier = Modifier .size(size.dp) .padding(3.5.dp) ..