티스토리 뷰
728x90
일반 이벤트
package net.pilseong.todocompose.ui.screen.task
import android.annotation.SuppressLint
import android.util.Log
import android.widget.Toast
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import net.pilseong.todocompose.R
import net.pilseong.todocompose.ui.viewmodel.SharedViewModel
import net.pilseong.todocompose.util.Action
@OptIn(ExperimentalMaterial3Api::class)
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@Composable
fun TaskScreen(
taskId: Int,
sharedViewModel: SharedViewModel,
toListScreen: (Action, Int) -> Unit,
) {
val context = LocalContext.current
val taskScreenState = sharedViewModel.taskScreenState
// list screen 에서 toTaskScreen 이 실행된 경우 taskId 를 받아서 DB 에서 검색한 후 보여 준다.
// 아래의 getSearchedTask 는 coroutine 에서 실행 된다.
LaunchedEffect(key1 = taskId) {
Log.i("PHILIP", "[TaskScreen] getSearchedTask with $taskId")
sharedViewModel.getSearchedTask(taskId)
}
val selectedTask = sharedViewModel.selectedTask
Log.i("PHILIP", "[TaskScreen] selectedTask $selectedTask")
// LaunchedEffect 가 없으면 마지막 저장 후 disposal 시 에 taskId 가 -1 인 경우가 실행 되어 모두 비어짐
// 읽어 온 task 를 메모 내에 반영 하기 위한 구문들
LaunchedEffect(key1 = selectedTask) {
if (selectedTask != null || taskId == -1) {
Log.i("PHILIP", "[TaskScreen] selected task updated ${selectedTask.toString()}")
sharedViewModel.updateTaskContent(selectedTask)
}
}
// 뒤로 가기 버튼에 대한 가로 채기 및 처리
// BackHandler(onBackPressed = { toListScreen(Action.NO_ACTION) })
BackHandler {
toListScreen(Action.NO_ACTION, taskId)
}
val emptyTitleString = stringResource(id = R.string.empty_title_popup)
val emptyDescriptionString = stringResource(id = R.string.empty_description_popup)
Scaffold(
topBar = {
TaskAppBar(
taskScreenState = taskScreenState,
todoTask = selectedTask,
toListScreen = { action ->
// 수정 할 내용을 반영 해야 할 경우 title, description 이 비어 있는지 확인
if (action != Action.NO_ACTION) {
if (sharedViewModel.title.isEmpty()) {
Toast.makeText(
context,
emptyTitleString,
Toast.LENGTH_SHORT
).show()
} else if (sharedViewModel.description.isEmpty()) {
Toast.makeText(
context,
emptyDescriptionString,
Toast.LENGTH_SHORT
).show()
} else {
toListScreen(action, taskId)
}
} else {
toListScreen(action, taskId)
}
}
)
},
content = { paddingValues ->
Column(
modifier = Modifier
.padding(top = paddingValues.calculateTopPadding())
.fillMaxSize()
) {
TaskContent(
taskScreenState = taskScreenState,
title = sharedViewModel.title,
description = sharedViewModel.description,
priority = sharedViewModel.priority,
createdAt = sharedViewModel.createdAt,
updatedAt = sharedViewModel.updatedAt,
onTitleChange = { title ->
sharedViewModel.updateTitle(title)
},
onDescriptionChange = { description ->
sharedViewModel.description = description
},
onPriorityChange = { priority ->
sharedViewModel.priority = priority
}
)
}
}
)
}
//@Composable
//fun BackHandler(
// backDispatcher: OnBackPressedDispatcher? =
// LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher,
// onBackPressed: () -> Unit
//) {
// val currentOnBackPressed by rememberUpdatedState(newValue = onBackPressed)
//
// val backCallback = remember {
// object : OnBackPressedCallback(true) {
// override fun handleOnBackPressed() {
// currentOnBackPressed()
// }
// }
// }
// DisposableEffect(key1 = backDispatcher) {
// backDispatcher?.addCallback(backCallback)
//
// onDispose {
// backCallback.remove()
// }
// }
//
//}
swipeToDismiss로 구현
package net.pilseong.todocompose.ui.screen.task
import android.annotation.SuppressLint
import android.util.Log
import android.widget.Toast
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.KeyboardArrowLeft
import androidx.compose.material.icons.filled.KeyboardArrowRight
import androidx.compose.material3.DismissDirection
import androidx.compose.material3.DismissValue
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SwipeToDismiss
import androidx.compose.material3.rememberDismissState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import net.pilseong.todocompose.R
import net.pilseong.todocompose.data.model.TodoTask
import net.pilseong.todocompose.ui.screen.list.ColorBackGround
import net.pilseong.todocompose.ui.viewmodel.SharedViewModel
import net.pilseong.todocompose.util.Action
import net.pilseong.todocompose.util.RequestState
import net.pilseong.todocompose.util.TaskScreenState
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@Composable
fun TaskScreen(
// taskId: Int,
sharedViewModel: SharedViewModel,
toListScreen: (Action, Int) -> Unit,
) {
val taskScreenState = sharedViewModel.taskScreenState
val index = sharedViewModel.index
Log.i("PHILIP", "[TaskScreen] index is $index")
// Flow 에 대한 collection 을 처리 하는 파이프 연결 변수들. 이 변수들 은 외부 데이터 베이스 나 외부 API 에 의존 한다.
// 모든 task 의 상태를 감시 한다. 리스트 는 nav graph 안에서 변동 될 수 있다.
val allTasks by sharedViewModel.allTasks.collectAsState()
val searchedTasks by sharedViewModel.searchedTasks.collectAsState()
val prioritySortState by sharedViewModel.prioritySortState.collectAsState()
val lowPriorityTasks by sharedViewModel.lowPriorityTasks.collectAsState()
val highPriorityTasks by sharedViewModel.highPriorityTasks.collectAsState()
when (allTasks) {
is RequestState.Success -> {
MainScreen(
taskScreenState = taskScreenState,
tasks = (allTasks as RequestState.Success<List<TodoTask>>).data,
// taskId = taskId,
taskIndex = index,
sharedViewModel = sharedViewModel,
toListScreen = toListScreen
)
}
else -> {
}
}
}
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@Composable
fun MainScreen(
taskScreenState: TaskScreenState,
tasks: List<TodoTask>,
// taskId: Int,
taskIndex: Int,
sharedViewModel: SharedViewModel,
toListScreen: (Action, Int) -> Unit,
) {
Log.i("PHILIP", "[MainScreen] size of tasks ${tasks.size}")
val context = LocalContext.current
val selectedTask = tasks[taskIndex]
Log.i("PHILIP", "[TaskScreen] selectedTask $selectedTask")
// LaunchedEffect 가 없으면 마지막 저장 후 disposal 시 에 taskId 가 -1 인 경우가 실행 되어 모두 비어짐
// 읽어 온 task 를 메모 내에 반영 하기 위한 구문들
// LaunchedEffect(key1 = selectedTask) {
if (selectedTask != null) { //|| taskId == -1) {
Log.i("PHILIP", "[TaskScreen] selected task updated ${selectedTask.toString()}")
sharedViewModel.updateTaskContent(selectedTask)
}
// }
// 뒤로 가기 버튼에 대한 가로 채기 및 처리
// BackHandler(onBackPressed = { toListScreen(Action.NO_ACTION) })
BackHandler {
toListScreen(Action.NO_ACTION, taskIndex)
}
val emptyTitleString = stringResource(id = R.string.empty_title_popup)
val emptyDescriptionString = stringResource(id = R.string.empty_description_popup)
Scaffold(
topBar = {
TaskAppBar(
taskScreenState = taskScreenState,
todoTask = selectedTask,
toListScreen = { action ->
// 수정 할 내용을 반영 해야 할 경우 title, description 이 비어 있는지 확인
if (action != Action.NO_ACTION) {
if (sharedViewModel.title.isEmpty()) {
Toast.makeText(
context,
emptyTitleString,
Toast.LENGTH_SHORT
).show()
} else if (sharedViewModel.description.isEmpty()) {
Toast.makeText(
context,
emptyDescriptionString,
Toast.LENGTH_SHORT
).show()
} else {
toListScreen(action, taskIndex)
}
} else {
toListScreen(action, taskIndex)
}
}
)
},
content = { paddingValues ->
// 화면 전환의 기준 점 계산 화면의 3분의 1이상 swipe 할 경우 전환
val threshold = LocalConfiguration.current.screenWidthDp / 3
val dismissState = rememberDismissState(
confirmValueChange = {
when (it) {
DismissValue.Default -> false
DismissValue.DismissedToEnd -> {
sharedViewModel.decrementIndex()
true
}
DismissValue.DismissedToStart -> {
sharedViewModel.incrementIndex()
true
}
}
},
positionalThreshold = { threshold.dp.toPx() }
)
// index 의 이동이 일어난 경우 실행 된다. 동일한 인덱스 로 이동 하는 경우는 없기 때문에
// 중복 이벤트 발생에 대한 대처를 할 필요가 없다.
// index 가 변경 된 상태 변경이 확인 되는 경우에 실행 된다.
LaunchedEffect(key1 = sharedViewModel.index) {
if (dismissState.dismissDirection == DismissDirection.StartToEnd) {
dismissState.snapTo(DismissValue.DismissedToStart)
} else {
dismissState.snapTo(DismissValue.DismissedToEnd)
}
dismissState.reset()
}
Column(
modifier = Modifier
.padding(top = paddingValues.calculateTopPadding())
.fillMaxSize()
) {
val currentItem by rememberUpdatedState(newValue = selectedTask)
SwipeToDismiss(
state = dismissState,
background = {
ColorBackGround(
dismissState = dismissState,
leftToRightColor = MaterialTheme.colorScheme.surface,
rightToLeftColor = MaterialTheme.colorScheme.surface,
leftIcon = Icons.Default.KeyboardArrowLeft,
rightIcon = Icons.Default.KeyboardArrowRight
)
},
dismissContent = {
TaskContent(
taskScreenState = taskScreenState,
title = currentItem.title,
description = currentItem.description,
priority = currentItem.priority,
createdAt = currentItem.createdAt,
updatedAt = currentItem.updatedAt,
onTitleChange = { title ->
sharedViewModel.updateTitle(title)
},
onDescriptionChange = { description ->
sharedViewModel.description = description
},
onPriorityChange = { priority ->
sharedViewModel.priority = priority
}
)
},
// directions = setOf(DismissDirection.StartToEnd)
)
}
}
)
}
//@Composable
//fun BackHandler(
// backDispatcher: OnBackPressedDispatcher? =
// LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher,
// onBackPressed: () -> Unit
//) {
// val currentOnBackPressed by rememberUpdatedState(newValue = onBackPressed)
//
// val backCallback = remember {
// object : OnBackPressedCallback(true) {
// override fun handleOnBackPressed() {
// currentOnBackPressed()
// }
// }
// }
// DisposableEffect(key1 = backDispatcher) {
// backDispatcher?.addCallback(backCallback)
//
// onDispose {
// backCallback.remove()
// }
// }
//
//}
@HiltViewModel
class SharedViewModel @Inject constructor(
private val todoRepository: TodoRepository,
private val dataStoreRepository: DataStoreRepository
) : ViewModel() {
var index by mutableStateOf(0)
private set
fun updateIndex(index: Int) {
this.index = index
}
// 화면 인덱스 이동 - delay 를 준 것은 swipeToDismiss 에서 swipe animation 구동 시에
// 전환 된 화면이 화면에 표출 되는 것을 막기 위함
fun incrementIndex() {
viewModelScope.launch {
delay(100)
index++
Log.i("PHILIP", "[SharedViewModel] incrementIndex $index")
}
}
fun decrementIndex() {
viewModelScope.launch {
delay(300)
index--
Log.i("PHILIP", "[SharedViewModel] decrementIndex $index")
}
}
728x90
'기록' 카테고리의 다른 글
Kotlin: resilience4j circuit breaker fallback (0) | 2024.03.15 |
---|---|
Android: 사오미 폰 다른 앱 switching 시에 process termination (0) | 2023.06.05 |
코딩 면접에 대해서 (0) | 2023.04.02 |
AWS : Beanstalk ALB access log 생성 문제 (0) | 2023.03.28 |
1 warning(s): 1285 mysql is started in --skip-name-resolve mode; (0) | 2023.03.12 |
댓글
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
- 도커 개발환경 참고
- AWS ARN 구조
- Immuability에 관한 설명
- 자바스크립트 멀티 비동기 함수 호출 참고
- WSDL 참고
- SOAP 컨슈머 참고
- MySql dump 사용법
- AWS Lambda with Addon
- NFC 드라이버 linux 설치
- electron IPC
- mifare classic 강의
- go module 관련 상세한 정보
- C 메모리 찍어보기
- C++ Addon 마이그레이션
- JAX WS Header 관련 stackoverflow
- SOAP Custom Header 설정 참고
- SOAP Custom Header
- SOAP BindingProvider
- dispatcher 사용하여 설정
- vagrant kvm으로 사용하기
- git fork, pull request to the …
- vagrant libvirt bridge network
- python, js의 async, await의 차이
- go JSON struct 생성
- Netflix Kinesis 활용 분석
- docker credential problem
- private subnet에서 outbound IP 확…
- 안드로이드 coroutine
- kotlin with, apply, also 등
- 안드로이드 초기로딩이 안되는 경우
- navigation 데이터 보내기
- 레이스 컨디션 navController
- raylib
TAG
- 하이버네이트
- XML
- Spring
- 자바
- hibernate
- WebMvc
- 설정하기
- 매핑
- Many-To-Many
- spring boot
- login
- jsp
- 스프링
- one-to-one
- RestTemplate
- Rest
- Spring Security
- 외부파일
- 로그인
- mapping
- MYSQL
- crud
- one-to-many
- Security
- form
- Validation
- Angular
- 상속
- 스프링부트
- 설정
250x250