본문 바로가기

Dailycoding/Android

Fragment 화면 전환 시 상태 유지하기

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