1️⃣ Fragment 화면 전환 시 상태 유지하기
기존의 화면에서는 처음에는 OneFragement 를 나타내고 있다가 BottomNavigationView 의 아이템인 two 를 클릭하면
TwoFragement 으로 이동했다가 다시 one 을 클릭하면 OneFragement 으로 이동하는데, 이동을 하면 다시 맨처음 즉 맨 위로 이동해져있다(즉, 상태를 저장하고 있지 않고 OneFragement 화면이 새로 생성되고 있다)
기존 코드 MainActivity
fun replaceFragment(name: String) {
val fragmentTransaction = supportFragmentManager.beginTransaction()
val newFragment: Fragment = when(name) {
ONE_FRAGMENT -> OneFragment()
TWO_FRAGMENT -> TwoFragment()
else -> Fragment()
}
fragmentTransaction.replace(R.id.fragment_container_main, newFragment)
fragmentTransaction.commit()
}
fun bottomNavigation() {
binding.bottomNavigationMain.run {
setOnItemSelectedListener {
when(it.itemId) {
R.id.item_one_menu -> {
replaceFragment(ONE_FRAGMENT)
return@setOnItemSelectedListener true
}
R.id.item_two_menu -> {
replaceFragment(TWO_FRAGMENT)
return@setOnItemSelectedListener true
}
else -> return@setOnItemSelectedListener false
}
}
}
}
즉, MainActivity 파일에서 바텀네비게이션을 클릭하면 fragment 를 replace 해서 새로 생성하고 있어서 fragment 의 상태를 유지하지 못 하고 있다.
fragment 상태 유지하는 화면
상태 유지 코드 MainActivity
fun addFragment(name: String) {
val fragmentTransaction = supportFragmentManager.beginTransaction()
// 현재 추가된 모든 프래그먼트를 숨깁니다.
val currentFragments = supportFragmentManager.fragments
for (fragment in currentFragments) {
fragmentTransaction.hide(fragment)
}
// 이름에 따라 새 프래그먼트를 찾거나 생성합니다.
var newFragment = supportFragmentManager.findFragmentByTag(name)
if (newFragment == null) {
newFragment = when(name) {
ONE_FRAGMENT -> OneFragment()
TWO_FRAGMENT -> TwoFragment()
else -> Fragment()
}
// 새 프래그먼트를 추가합니다. 태그를 사용하여 찾을 수 있도록 합니다.
fragmentTransaction.add(R.id.fragment_container_main, newFragment, name)
} else {
// 프래그먼트가 이미 존재한다면 보여줍니다.
fragmentTransaction.show(newFragment)
}
fragmentTransaction.commit()
}
fun bottomNavigation() {
binding.bottomNavigationMain.run {
setOnItemSelectedListener {
when(it.itemId) {
R.id.item_one_menu -> {
addFragment(ONE_FRAGMENT)
return@setOnItemSelectedListener true
}
R.id.item_two_menu -> {
addFragment(TWO_FRAGMENT)
return@setOnItemSelectedListener true
}
else -> return@setOnItemSelectedListener false
}
}
}
}
나는 기존의 fragment 를 replace 하는 대신에 add 를 사용했다.
2️⃣ 메뉴 두 번 클릭 시 화면 스크롤
MainActivity.kt
private lateinit var viewModel: MainViewModel
private var oneState = true
viewModel = ViewModelProvider(this)[MainViewModel::class.java]
fun bottomNavigation() {
binding.bottomNavigationMain.run {
setOnItemSelectedListener {
when(it.itemId) {
R.id.item_one_menu -> {
if (oneState) {
viewModel.upOneFragment()
} else {
addFragment(ONE_FRAGMENT)
}
oneState = true
return@setOnItemSelectedListener true
}
R.id.item_two_menu -> {
addFragment(TWO_FRAGMENT)
oneState = false
return@setOnItemSelectedListener true
}
else -> return@setOnItemSelectedListener false
}
}
}
}
oneState 는 OneFragmet 화면이 현재 선택되어 있는지 확인하기 위한 boolean 변수이다.
MainViewModel.kt
class MainViewModel : ViewModel() {
private val _oneState = MutableLiveData<Boolean>()
val oneState: LiveData<Boolean> = _oneState
fun upOneFragment() {
_oneState.value = true
}
}
OneFragment.kt
private lateinit var mainViewModel: MainViewModel
// MainViewModel을 Activity 범위에서 가져오기
mainViewModel = ViewModelProvider(requireActivity())[MainViewModel::class.java]
mainViewModel.oneState.observe(viewLifecycleOwner) { state ->
if (state) {
binding.recyclerViewOne.smoothScrollToPosition(0)
}
}
MainViewModel 에서 oneFragment 가 클릭되었는지를 나타내는 oneState 변수를 LiveDate 로 관찰해서 upOneFragment 함수를 사용하게 되면 이 값이 true 가 되게 하고 있다. 그래서, LiveDate 가 true 가 되면 OneFragment 에서 이 값을 관찰하고 있어서 true 가 되면 recyclerViewOne의 위치를 smoothScrollToPosition(0) 을 사용해서 맨 위로 이동하게 하고 있다.
3️⃣ 위로 당겨서 새로고침
buidl.gradle.kts
// swiperefreshlayout
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
공식문서
https://developer.android.com/jetpack/androidx/releases/swiperefreshlayout?hl=ko
OneFragment.kt
private fun swipeRefresh() {
binding.swipeRefreshLayoutOne.setOnRefreshListener {
viewModel.getAllData()
// 새로고침 아이콘 없애기
binding.swipeRefreshLayoutOne.isRefreshing = false
}
}
현재 viewModel.getAllData() 를 통해서 데이터를 받아오고 있어서 나는 새로고침을 했을 때, 데이터를 다시 받아오게 하고 있다
fragment_one.xml
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout_one"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView_one"
android:layout_width="0dp"
android:layout_height="0dp" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
간단하게 RecyclerView 를 SipeRefreshLayout 으로 감싸 주기만 하면 된다.
전체 코드
https://github.com/wjdwntjd55/Blog/tree/develop/Project/MaintainFragmentState
Blog/Project/MaintainFragmentState at develop · wjdwntjd55/Blog
Contribute to wjdwntjd55/Blog development by creating an account on GitHub.
github.com
참고한 자료
[Android] 프래그먼트 화면 전환 시 상태 유지하기 (FragmentManager)
카카오톡을 보면 바텀네비게이션 메뉴를 클릭해 다른 화면으로 이동 후 다시 돌아왔을 때 기존의 화면이 유지되어 있는 것을 확인할 수 있다. 또한 그 화면에서 같은 메뉴를 다시 클릭하면 화면
hanyeop.tistory.com
SwipeRefreshLayout :: 당겨서 새로고침 하기
1. SwipeRefreshLayout란? 2. xml 소스 코드 3. activity 소스 코드 1. SwipeRefreshLayout란? 앱에서 게시물 목록을 당겨서 새로고침 할 때 사용하는 것이 바로 SwipeRefreshLayout이다. 화면을 당기면 빙글빙글 돌아가
todaycode.tistory.com
'Dailycoding > Android' 카테고리의 다른 글
Data Binding (0) | 2024.08.27 |
---|---|
CoordinatorLayout (0) | 2024.08.21 |
wifi 없을 때 Room 사용해서 화면에 표현 (0) | 2024.08.19 |
shimmer library를 사용해서 스켈레톤 로딩 화면 (0) | 2024.06.11 |
Viewpager2 활용해서 banner 구현 (0) | 2024.06.01 |