/ 모임과 행사

5~6월은 안드로이드 '기초'에 대해서 알아보았습니다.

안드로이드 프래그먼트(Fragment)

  • 5~6월은 안드로이드의 프래그먼트를 사용해서 가볍게 토이 프로그램을 만들어보는 것을 스터디하고, 몇가지 기초적인 사항을 기록으로 남겨봅니다.

  • 스터디를 하면서 배웠거나 알아봤던 내용을 기록으로 남기지 않아서 많은 내용은 없지만 필요한 몇가지 내용을 정리하였습니다.

  • 이런게 있구나... 하고 가볍게 읽어보시길 권해드립니다.

액티비티

  • 액티비티는 사용자와의 인터페이스를 구성하지만 그 자체는 출력 기능이 없어 직접적으로 보이지 않으며, 사용자 눈에 실제로 보이는 것인 뷰이며 그래서 액티비티는 반드시 내부에 뷰나 뷰그룹을 가져야 함 [1]
  • 화면의 기본 단위가 되고 가장 많이 쓰이는 컴포넌트이고 독립적인 화면이라면 액티비티를 고려하고, 종속적인 화면이라면 다른 컴포넌트를 사용 [2]
    • setContentView()를 반드시 호출해야 하는 것이 아님 해당 사항에 대한 내용은 레퍼런스 [2:1]을 참고할 것

액티비티 생명주기

  • 다른 액티비티가 일부만 가리면 onPuse(), 전체를 가리면 onSteop()을 호출하며, 종료시 전자의 경우 onResume을 후자의 경우 onRestartonStart()를 호출
    • 해당 내용의 세부적인 사항은 레퍼런스 [2:2]을 참고
  • 우선순위가 더 높은 앱이 메모리가 필요하다면 앱은 언제든 종료될 수 있기 때문에 onStop(), onDestroy() 메서드를 반드시 실행된다는 보장은 없음
  • 시스템에 의해서도 액티비티가 제거될 수 있기 떄문에 태스크의 목록이 유지된다고 가정하면 안 됨

액티비티 시작

  • 액티비티끼리 서로 호출하려면 통신을 위한 장치가 필요한데 이 장치가 바로 인텐트(Intent)[1:1]
    • Intent에 관한 세부적인 내용은 레퍼런스 [1:2]을 참고
  • startActivity()Context 메서드이기 때문에 어디서든 호출 가능하지만, startActivityForResult()Activity의 메서드임[2:3]

주의사항

  • onCreate()에서 생성했다면 onDestroy()에서 제거, onResume()에서 생성했다면 onPause()에서 제거
  • onCreate(), onStart(), onResume()에서는 super.onXXX가 먼저 실행되고, 나머지는 나중에 실행됨

구성변경

  • 구성(Configuration)은 컴포넌트에서 어떤 리소스를 사용할지 결정하는 조건, 이 조건 항목은 프레임워크에 결정되어 있음
  • 구성이 변경되어 액티비티를 재시작하면 하나의 인스턴스를 가지고 새로 초기화해서 재사용하는 것이 아니고, onDestroy()까지 실행하고 새인스턴스가 onCreate()부터 실행
  • 구성이 변경되어 재시작되어도 사용자 경험상 기존에 보던 화면을 유지하기 위해서 onRestoreInstanceState()onSaveinstanceState()를 사용하면 됨
    • onSaveInstanceState() 메서더는 targetSdkVersion에 따라 호출되는 시점이 다름

프래그먼트

            << 폰 >>                          << 태블릿 >>
--------------      --------------     ---------------------------
|            |      |            |     |            |            |
|            |      |            |     |            |            |
|            |      |            |     |            |            |
|   리스트   | -->  |   디테일   |  ,  |   리스트   |   디테일   |
|            |      |            |     |            |            |
|            |      |            |     |            |            |
|            |      |            |     |            |            |
--------------      --------------     ---------------------------

프래그먼트의 필요성

  • 태블릿은 핸드셋에 비해서 화면이 훨씬 넓어 화면에 더(핸드폰에 비해서) 많은 정보를 한눈에 볼 수 있기 있음
  • 액티비티는 화면 하나를 정적으로 정의하면 더 이상 분할하기 어려기 때문에 태블릿 전용으로 만들지 않으면 대화면의 이점을 누리지 못함
  • 액티비티보다 더 작은 화면 단위를 정의한 것이 프래그먼트, 프래그먼트를 조합하여 완성된 화면을 표현 액티비티에 비해 실행 중에도 추가, 제거, 교체 가능하여 동적이고 유연한 화면을 만들 수 있지만, 혼자서는 독립적으로 실행할 수 없음

프래그먼트의 주요 특징

  • 액티비티를 분할하여 화면의 한 부분을 정의
  • 자신만의 레이아웃, 독작, 생명주기를 가지는 독립적인 모듈
  • 여러 액티비티에서 재사용 가능
  • 실행 중에 추가, 제거, 대체 가능하며 잠시 숨길 수도 있음

프래그먼트 생성

  • 프래그먼트는 XML 레이아웃 파일의 저장형태로 추가될 수 있으며, 클래스 내부에서 직접 코드로 정의해서 사용할 수 있음
  • 프래그먼트를 사용하는 레이아웃의 경우 android.support.v7.app.AppCompatActivityandroid.support.v4.app.Fragment를 사용해야 하며 onCreateView()를 오버라이딩 해야함
  • XML 레이아웃 파일에 포함시킬 때는 <fragment>요소를 사용해서 액티비티 레이아웃에 포함

프래그먼트 생명주기

  • 많은 콜백 중에서 onCreate(), onCrateView(), onPause() 콜백은 거의 필수적으로 구현해야 하며 나머지는 필요할 때만 구현하면 됨
-----------------
|프래그먼트 추가|
-----------------
        |
--------v--------
|    onAttach   |
-----------------
        |
--------v--------     -----------------      -----------------
|    onCreate   | <-- | onDestroyView |  --> |   onDestroy   |
-----------------     --------^--------      -----------------
        |                     |                      |
--------v--------     -----------------      --------v--------
| onCreateView  |     |    onStop     |      |   onDetach    |
-----------------     --------^--------      -----------------
        |                     |                      |
--------v--------     -----------------      --------v--------
|   onStart     |     |   onPause     |      |프래그먼트 파괴|
-----------------     --------^--------      -----------------
        |                     |
--------v--------     -----------------
|   onResume    |     |프래그먼트 실행|
-----------------     -----------------

프래그먼트 초기화

  • void onAttach(Activity activity)

    • 프래그먼트가 처음 부착될 때 호출
    • 인수로 전달되는 activity는 프래그먼트가 배치되는 호스트 액티비티
      • 프래그먼트는 홀로 사용될 수 없고 항상 액티비티에 종속적으로 사용해야 함
  • void onCreate(Bundle savedlnstanceState)

    • 프래그먼트가 생성될 때 호출
    • 이 단계에서는 호스트 액티비티도 아직 초기화중인 상태이며 따라서 액티비티의 컨트롤을 안전하게 참조할 수 없기 때문에 완전히 초기화된 시점이 필요하면 onActivityCreated 콜백을 사용해야 함
    • savedlnstanceState는 프래그먼트가 재생성될 때의 이전 상태이며 이 값을 참조하여 이전 상태로 복구
  • View onCreateView(Layoutlnflater inflater, ViewGroup container, Bundle savedlnstanceState)

    • 프래그먼트 UI를 처음 그릴 때 호출되며 이 단계에서 프래그먼트는 자신의 레이아웃을 생성한 후 최상단(root) 뷰를 반환
    • inflater가 인수로 전달되기 때문에 inflater를 따로 생성하지 않아도 됨
      • inflate 메서드의 마지막 인수는 false여야 하고, 이 값을 true로 지정하면 전개된 뷰를 container에 부착해도 별다른 이상은 없으며 에러도 발생하지 않지만 이중으로 부착되어 자원만 낭비되므로 시스템에 맡겨두면 됨
    • container는 프래그먼트가 배치될 액티비티의 최상단 뷰이며 이 위치에 프래그먼트가 생성 및 배치됨
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_layout, container, false);
    }
    
  • void onActivityCreated(Bundle savedlnstanceState)

    • 액티비티가 완전히 초기화되고 프래그먼트의 레이아웃도 완성되었을 때 호출
    • 이 메서드에서 뷰의 상태를 복구하는 등의 동작을 안전하게 수행할 수 있으며 액티비티의 다른 위젯을 참조하거나 조작할 수 있음

프래그먼트 시작

프래그먼트 초기화의 메서드를 차례대로 실행 한 후 onStartonResume가 연이어 호출됨

  • void onPause()

    • 프래그먼트가 정지될 때 호출됨
  • void onSavelnstanceState(Bundle outState)

    • onPause와 함께 호출되며 이 메서드에서 임시 정보를 저장함
    • onState에 정보를 저장하면 프래그먼트가 재생성 될 때 다시 전달됨

프래그먼트 관리자

  • 프래그먼트와 관련된 편집 작업은 프래그먼트 관리자를 사용하며 이 객체는 아래 두 메서드로 구현

    • FragmentManager Activity.getFragmentManager()
    • FragmentManager Fragment.getFragmentManager()
  • 액티비티와 프래그먼트에 같은 이름의 메서드가 제공되므로 언제든지 관리자 객체를 얻을 수 있음

    • Fragment findFragmentByld(int id)
    • Fragment findFragmentByTag(String tag)
  • 프래그먼트는 다음 3가지 방식으로 지칭

    • id : 프래그먼트를 배치할 때 지정한 android:id 속성의 정수값을 사용
    • 부모의 id : 실행 중에 생성한 프래그먼트는 고유한 id가 없기 때문에 부모의 id를 통해 프래그먼트를 찾음
    • 문자열 형태의 태그 : 실행 중의 프래그먼트를 생성할 때 고유한 이름의 태그를 붙일 수 있으며, UI가 없는 프래그먼트의 겨우 XML 문서에서 android:tag 속성으로 태그명을 부여함

실행 중 프래그먼트 추가, 제거 교체

  • FragmentTransaction beginTransaction()

    • 동시에 여러 동작을 처리해야 하며 프래그먼트의 부피가 크면 상당한 시간이 걸릴 수 있음
    • 뿐만 아니라 각 동작마다 화면을 갱신하면 화면 깜박거림이 발생하기 떄문에 전체 편집 동작을 한 묶음으로 처리하기 위해 트랜잭션으로 처리함
    • 프래그먼트 관련 처리를 모아 두었다가 한꺼번에 처리하기 위해 트랜잭션으로 묶는 것 뿐임
  • FragmentTransaction add(int containerViewld , Fragment fragment [, String tag]), FragmentTransaction add(Fragment fragment , String tag)

    • 프래그먼트 추가를 위한 메서드
    • containerViewld는 프래그먼트가 추가될 부모 View의 id
    • fragment는 추가할 프래그먼트 객체이며, tag 인수로 이름을 지정
  • FragmentTransaction remove(Fragment fragment)

    • 프래그먼트가 화면에서 사라짐
  • FragmentTransaction replace(int containerViewld, Fragment fragment [, String tag])

    • 프래그먼트를 다른 프래그먼트로 대체
  • FragmentTransaction show(Fragment fragment), * FragmentTransaction hide(Fragment fragment)

    • 프래그먼트를 숨기거나 화면에 보일 수 있음
  • int commit()

    • 스레드가 한가할 때 처리하도록 스케줄링을 함
    • 이 메서드를 호출하지 안흐면 트랜잭션 편집은 모두 취소


  1. 김상형, "안드로이드 프로그래밍 정복 Vol.2 - 6.X 마시멜로와 안드로이드 스튜디오를 기반으로 다시 집필한, 전면개정판," 한빛미디어, 2016 (많은 분들이 참고하시는 교재이긴 하지만, 몇가지 변경사항이 반영되지 못한 부분이 존재하기 때문에 권하고 싶지 않음) ↩︎ ↩︎ ↩︎

  2. 노재춘, "안드로이드 프로그래밍 Next Step," 인사이트, 2017 (글을 작성하면서 참고한 책 중에서 가장 알찬 지식을 전해주는 책, 강추!) ↩︎ ↩︎ ↩︎ ↩︎