프래그먼트 - 1 add/replace/addToBackStack, FragmentContainerView

상상코딩·2022년 3월 16일
0

안드로이드

목록 보기
13/21

1. 프래그먼트 붙이기

Fragment는 Activity나 다른 Fragment에 호스팅되어야함. 이때, Activity는 FragmentActivity 를 상속하는 Activity여야하는데, AppCompatActivityFragmentActivity를 상속하고 있으므로 ok

  • 방법 1: Activity 레이아웃 안에 프래그먼트 존재 정의 (액티비티 inflate시 같이 inflate됨)
  • 방법 2: Activity 레이아웃 안에 프래그먼트 컨테이너 정의, programmatically add하기

*(이 때, FragmentContainerView를 정의해서 프래그먼트 위치를 정의)
FragmentContainerView: FrameLayout에서 제공하지 않는 조작 관련 사항이 추가된 새로운 뷰(사실 FrameLayout을 상속하는 뷰임.)

  • xml 코드
    이렇게 name속성으로 주면 처음 inflate될 프래그먼트 설정해주고, 코드로 변경도 가능!
  • activity코드
    이제 더이상 beginTransaction~~ 이런거 호출하지 않고 바로 아래와같이 쓰면 됨.

2. Fragment add vs replace


→ replace

replace를 하면 onAttach - onCreate - onCreateView - onStart - onResume 의 생명주기를 탄다.

그 말은 즉..

replace(binding.container.id, myfragment, tag)와 같이 써도replace(binding.container.id, MyFragment.newInstance(), tag) 처럼 동작한다는 것이다.!!!!!

myFragment를 변수로 둘 이유가 없음! 매번 생성하는것과 동일하니까.. 그래서 프래그먼트의 데이터도 유지가 안되고, 내가 스크롤 내려서 보던 위치도 기억할 수 없음.

왜냐: replace는 해당 프래그먼트를 remove하고 다시 add하는 것과 동일하므로.


→ add 는?

add도 마찬가지로 위 생명주기( onAttach - onCreate - onCreateView - onStart - onResume ) 모두 탄다.

그러나 주의할 점은

add(binding.container.id, myfragment, tag)와 같이 사용한다면 해당 함수를 또 불렀을 때, 이미 add된 프래그먼트를 또 불렀다면서 에러를 뱉는다!!!! 동일 프래그먼트에 한 해서는 한번만 add될 수 있다.

그런데 add(binding.container.id, MyFragment.newInstance(), tag) 와 같이 쓴다면 가능!

show, hide를 사용한다면 말그대로 visibility만 바꾸는 것이므로 다른 생명주기를 타지 않는다.

이 말은 즉, 뷰의 스크롤 위치/ 데이터 모두 유지가 된다는 말!


+ addToBackStack

얘를 처리하지 않으면 안드로이드의 back 버튼을 눌렀을 때 아무일도 일어나지 않고 액티비티가 꺼진다!!!1

안드 back 버튼은 activity 기준으로 finish해주는데, fragment는 따로 처리를 해줘야 한다.

구현은 아래와 같이 addToBackStack(null) 처럼 해주면 되는데, 이걸 해주는 순간 커밋을 하기 전까지 모든 변경 내용이 하나의 트랜잭션으로 backStack에 들어간다. 트랜잭션으로는 add/replace/hide/show 모두 가능!

따로 onBackPressed를 구현하지 않아도 되고 그냥 해당 커밋을 돌려놓는 것으로 이 역할을 한다.

🤓 Back버튼 누르면 생명주기는 어떻게 되나???

back했을 때 프래그먼트가 어떻게 되는지 궁금해서 replace한 것을 back 해봤는데 생명주기가 아래와 같이 탔다. (add한것 back 했을 때도 동일.)

없어지는 프래그먼트 : onPause - onStop - onDestroyView (- onDestroy - onDetach)

생기는 프래그먼트 : (onAttach - onCreate -) onCreateView - onStart - onResume

(괄호친 부분 주기는 안탐) 왜냐면 다시 살려야하니까 아예없애면 안 되거든. BackStack에 안 넣으면 괄호친 부분까지 다 타는것!!


  • 추가로 replace, add 시 tag가 옵션인데 이 tag로 프래그먼트를 찾아올 수 있음.

replace(binding.container.id, myfragment, tag)또는add(binding.container.id, myfragment, tag)

4. 나의 개발일기

Replace는 사용안한다 - 왜냐면 탭 구조상 돌아갔을때 내가 보던 데이터/위치 살아있어야 맞을 것 같아서

탭 구조상 전체메뉴 탭도 back버튼 눌렀을 때 앱 종료가 맞지만 사용성을 위해 이전 프래그먼트로 돌려주고 싶었다. → 이 부분에서 고민한 방법 두가지

  1. BackStack에 넣기
    이걸 활용해서 한큐에 끝내고 싶었다...
    그러나 이 방법을 포기한 이유는,, 바로 바텀 네비게이션 때문이었다.

Back했을 때, 화면의 프래그먼트는 “이전 프래그먼트”를 잘 보여주는데*, 바텀 내비게이션의 selectedItem이 그 “이전 프래그먼트”의 Item을 가리키고 있지 않았기 때문이다.

따라서 이전 프래그먼트의 내비 아이디를 가지고 있어야함.
= onBackPressed를 오버라이드하여 내비게이션 selectedItem에 해당 네비아이디를 set해줘야함.
→ 그럼 이제 굳이 두벌 처리해야하는가?
: 하나의 기능을 한곳에서 처리하는것이 더 깔끔하기 때문에 BackStack을 포기하고 onBackPressed에 모두 구현하도록 방법을 변경했다.

*) 이 때의 삽질기
: 전체메뉴에서 Back했을 때만 이전 프래그먼트를 보여주고 싶었다! 그래서 메뉴 탭을 눌렀을 때만 BackStack에 넣어주었다.

  • 문제점
    : 요리조리 겁나게 왔다갔다하고 나면 (ex. 뱅킹 -> 메뉴 -> 자산 -> 메뉴 -> 라이프 -> 메뉴) 총 3개의 backstack 트랜잭션이 생김. 따라서 3번이나 백을 누를 수 있게 되었다.
  • 해결
    : 백스택 초기화
    supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
  1. onBackPressed 오버라이드 하기
    이렇게 고공

*) 또다른 삽질기

하다가 메뉴를 막 바꿔대면 가끔 막 다른게 뜨고 난리가 남 - 보니까 isAdded가 됐는데 안됐다고 나와서 다른 로직을 탐

supportFragmentManager*.executePendingTransactions*() 쓰삼.

왜냐!!!! FragmentTransactions are committed asynchronously.

+추가 : 이것도 쓰지말자!!! 왜냐면 2편에 나올 commit/commitNow/allowingStateLoss 에 다룰 예정.

  • 1번 방법 때 코드
profile
히히낙낙

0개의 댓글