티스토리 뷰

728x90

 


Fragment

• 앱 UI의 재사용 가능한 부분

• 여러 개의 프래그먼트를 하나의 액티비티에 결합하여 창이 여러 개인 UI를 빌드할 수 있다.

• 하나의 fragment를 여러 액티비티에서 재사용할 수 있다.

-> 즉, 액티비티의 모듈화된 구역이라고 생각하면 편하다.

• Fragment는 자체적인 생명 주기를 갖는다.

     - 생명 주기는 호스트 액티비티의 생명 주기에 직접적으로 영향을 받는다.

     * 호스트 액티비티 : 해당 프래그먼트를 포함하는 액티비티

• Fragment는 자체 입력 이벤트를 수신한다.

 

 

Fragment 생명 주기

1. Activity의 onCreate(savedInstanceState)

• onAttach()

     - 프래그먼트가 액티비티와 연결되어 있는 경우 호출된다.

     - Activity가 전달된다.

• onCreate()

     - 프래그먼트가 생성되는 시간이다.

     - 초기화 작업을 한다.

• onCreateView()

     - 프래그먼트가 자신의 인터페이스를 그리는 시간이다.

     - View를 리턴해야 한다.

     - UI가 없는 프래그먼트의 경우 null을 반환해야 한다.

• onActivityCreated()

 

2. Activity의 onStart()

• onStart()

 

3. Activity의 onResume()

• onResume()

 

4. Activity의 onPause()

• onPause()

     - 사용자가 프래그먼트를 떠난다는 것을 나타내는 첫 번째 신호.

     - 변경사항 저장 등의 작업.

 

5. Activity의 onStop()

• onStop()

 

6. Activity의 onDestroy()

• onDestroyView()

• onDestroy()

• onDetach()

     - 프래그먼트가 액티비티와 연결이 끊어지는 시간이다.

 

Fragment 생성 방법

• 코틀린 클래스를 생성하여 Fragment() 상속

• layout을 구성할 xml 파일 생성

• 코틀린 클래스에서 onCreateView(inflater, container, savedInstanceState) 오버라이드하여 xml 파일과 연결

     - inflater : xml 파일을 뷰로 만들어주는 역할

     - container : 해당 프래그먼트의 부모 뷰

     - savedInstanceState : 사용자의 저장된 사용 기록, 상태

     - inflater.inflate(R.layout.XML파일명, container, false)을 리턴하여야 한다.

          - attchToRoot : 루트 뷰에 지금 포함시킬 지(true), 나중에 포함시킬 지(false)

 

• FragmentOne_11.kt

package com.example.fastcampus

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment

class FragmentOne_11: Fragment() { // Fragment() 상속
    override fun onCreateView( // onCreateView 오버라이드
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.activity_fragment_one11, container, false) // activity_fragment_one11.xml에 해당하는 뷰를 생성
    }
}

 

• activity_fragment_one11.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/fragmentTextView"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="#AAAAFF"
        android:gravity="center"
        android:text="Fragment One"
        android:textSize="40dp" />

</androidx.appcompat.widget.LinearLayoutCompat>

생성한 프래그먼트

 

생성한 Fragment 액티비티에 포함시키는 방법

1. xml로 포함시키기

• 프래그먼트를 포함할 호스트 액티비티 생성.

• 호스트 액티비티에서 <androidx.fragment.app.FragmentContainerView> 태그 사용하여 프래그먼트를 불러올 수 있다.

• android:id 무조건 필요

     - 다른 뷰에서는 id가 필요할 때만 쓰면 됐는데 FragmentContainerView에서는 id가 필수이다.

     - 이유는 아직 모르겠다.

• android:name="패키지명.프래그먼트명"

     - 포함시킬 프래그먼트명을 적는다.

• 프래그먼트 뷰는 불러오는 것이기 때문에 xml 미리보기에 뜨지 않는다.

     - tools:layout="@layout/XML파일명"을 통해 미리보기가 가능하다.

 

• activity_fragment_container11.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".FragmentContainer_11"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/hostActivityTextView"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="#000000"
        android:gravity="center"
        android:text="Host Activity"
        android:textColor="#FFFFFF"
        android:textSize="40dp" />

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/fragment_container_view1"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:name="com.example.fastcampus.FragmentOne_11"
        tools:layout="@layout/activity_fragment_one11" />

</androidx.appcompat.widget.LinearLayoutCompat>

프래그먼트를 포함한 호스트 액티비티의 모습

 

 

2. 코틀린 프로그래밍으로 포함시키기

• supportFragmentManager 객체의 beginTransaction() 메소드를 사용하여 FragmentTransaction 생성.

• FragmentTransaction

     - 프래그먼트 연산을 위한 트랜잭션.

     - 프래그먼트 추가, 삭제, 교체 등의 작업을 한다.

     - FragmentTransaction.replace(부모 뷰, 프래그먼트 객체) : 존재하는 프래그먼트를 부모 뷰에 포함시킨다.

     - FragmentTransaction.remove(프래그먼트 객체) :  프래그먼트를 부모 뷰에 포함시킨다.

     - FragmentTransaction.commit()

          - 트랜지션을 커밋한다.

          - commit, commitAllowingStateLoss, commitNow, commitNowAllowingStateLoss가 있다.

               - AllowingStateLoss : 저장된 상태에 대한 손실 허락 여부.

               - Now : 트랜잭션을 메인 쓰레드에 예약할 것인지, 즉시 실행할 것인지 여부.

     * xml로 추가한 프래그먼트는 조작하지 못한다.

 

* Transaction (트랜잭션)

     - 쪼갤 수 없는 작업의 최소 단위

     - 은행 ATM이나 데이터베이스 등의 시스템에서 사용한다.

     - 여러 절차의 작업들을 하나의 트랜잭션으로 설정하면 이 작업들은 별개로 분리될 수 없고 하나의 작업으로 처리된다.

     -> 따라서 트랜잭션을 이루는 여러 절차 중 몇 개는 성공하고 몇 개는 실패하고 이럴 수 없다.

     -> 모든 절차가 완료되면 commit(커밋), 하나라도 오류가 발생하면 원래 상태로 rollback(롤백)한다.

     ex) A가 돈을 지불하고, B가 돈을 받는 트랜잭션

          -> A가 돈을 지불했지만 B가 돈을 받지 않았으면 해당 트랜잭션은 작업 실패.

          -> A의 돈 지불, B의 돈 받기가 모두 성공하여야 하나의 트랜잭션이 완료된 것.

 

• activity_fragment_container11.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".FragmentContainer_11"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/hostActivityTextView"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="#000000"
        android:gravity="center"
        android:text="Host Activity"
        android:textColor="#FFFFFF"
        android:textSize="40dp" />

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/fragment_container_view1"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:name="com.example.fastcampus.FragmentOne_11"
        tools:layout="@layout/activity_fragment_one11" />

    <TextView
        android:id="@+id/textAdd"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="#FFAAAA"
        android:gravity="center"
        android:text="add"
        android:textSize="40dp" />

    <TextView
        android:id="@+id/textRemove"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="#FFAAAA"
        android:gravity="center"
        android:text="remove"
        android:textSize="40dp" />

    <LinearLayout
        android:id="@+id/root"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:orientation="horizontal"/>

</androidx.appcompat.widget.LinearLayoutCompat>

 

• FragmentContainer_11.kt

package com.example.fastcampus

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView

class FragmentContainer_11 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_fragment_container11)

        val fragmentManager = supportFragmentManager // supportFragmentManager 객체 생성
        val fragmentOne = FragmentOne_11()

        findViewById<TextView>(R.id.textAdd).setOnClickListener {
            val transaction = fragmentManager.beginTransaction() // beginTransaction() 메소드로 FragmentTransaction 객체 생성
            transaction.replace(R.id.root, fragmentOne) // root 뷰에 fragmentOne 포함
            transaction.commit() // 트랜지션 커밋
        }

        findViewById<TextView>(R.id.textRemove).setOnClickListener {
            val transaction = fragmentManager.beginTransaction() // beginTransaction() 메소드로 FragmentTransaction 객체 생성
            transaction.remove(fragmentOne) // fragmentOne 삭제
            transaction.commit() // 트랜지션 커밋
        }
    }
}

add를 누르면 fragment 포함, remove를 누르면 fragment 삭제

 

Fragment와 Activity 상호 접근

1. 호스트 Activity에서 Fragment로 데이터 전달하기

• 프래그먼트는 호스트 액티비티에 포함되어 있기 때문에, 호스트 액티비티에서 프래그먼트로 쉽게 접근이 가능하다.

• 호스트 액티비티에서는 프래그먼트 객체의 arguments 프로퍼티에 접근하여 bundle 타입의 데이터를 보내고, 프래그먼트에서는 arguments 프로퍼티에 전달된 데이터를 getString 등의 메소드로 받아 데이터를 사용할 수 있다.

 

• FragmentContainer_11.kt

package com.example.fastcampus

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView

class FragmentContainer_11 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_fragment_container11)

        val fragmentManager = supportFragmentManager
        val fragmentOne = FragmentOne_11()

        findViewById<TextView>(R.id.textAdd).setOnClickListener {
            val transaction = fragmentManager.beginTransaction()

            val bundle = Bundle() // Bundle 객체 생성
            bundle.putString("key", "hello") // string 데이터 넣음
            fragmentOne.arguments = bundle // 프래그먼트의 arguments 프로퍼티에 접근하여 전달

            transaction.replace(R.id.root, fragmentOne)
            transaction.commit()
        }

        findViewById<TextView>(R.id.textRemove).setOnClickListener {
            val transaction = fragmentManager.beginTransaction()
            transaction.remove(fragmentOne)
            transaction.commit()
        }
    }
}

 

• FragmentOne_11.kt

package com.example.fastcampus

import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment

class FragmentOne_11: Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.activity_fragment_one11, container, false)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val data: String? = arguments?.getString("key") // arguments 프로피터로 전달받은 데이터 data 변수에 저장
        Log.d("testt", "data : " + data) // Logcat에 출력
    }
}

 

• 결과

     - 첫 번째로 나온 null은 xml로 포함한 프래그먼트의 로그이다.

          -> xml로 포함한 프래그먼트에는 데이터 전달이 어렵다고 한다.

     - add를 눌러 포함된 두 번째 프래그먼트에서는 전달된 문자열 데이터 hello가 정상적으로 출력되었다.

     - 프래그먼트를 포함하고 삭제하고를 반복할 때 마다 잘 출력이 된다.

 

2. Fragment에서 호스트 Activity로 접근하기

• 호스트 액티비티에 있는 함수를 Fragment에서 접근해보자.

 

• 호스트 액티비티인 FragmentContainer_11.kt에 printTestLog 함수 생성

package com.example.fastcampus

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.TextView

class FragmentContainer_11 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_fragment_container11)

        val fragmentManager = supportFragmentManager
        val fragmentOne = FragmentOne_11()

        findViewById<TextView>(R.id.textAdd).setOnClickListener {
            val transaction = fragmentManager.beginTransaction()

            val bundle = Bundle()
            bundle.putString("key", "hello")
            fragmentOne.arguments = bundle

            transaction.replace(R.id.root, fragmentOne)
            transaction.commit()
        }

        findViewById<TextView>(R.id.textRemove).setOnClickListener {
            val transaction = fragmentManager.beginTransaction()
            transaction.remove(fragmentOne)
            transaction.commit()
        }
    }

    fun printTestLog() { // printTestLog 함수 생성
        Log.d("testt", "print test log")
    }
}

 

• 프래그먼트인 FragmentOne_11.kt에서 호스트 액티비티의 printTestLog 함수 접근

     - activity 키워드로 호스트 액티비티를 가져올 수 있다.

     - 프래그먼트는 여러 액티비티에서 재사용되어 사용될 수 있기 때문에, 호스트 액티비티가 여러 개 있을 수 있다.

     -> 원하는 호스트 액티비티로 캐스팅 해주어야 한다.

package com.example.fastcampus

import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment

class FragmentOne_11: Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val fragmentView = inflater.inflate(R.layout.activity_fragment_one11, container, false) // inflater로 만든 뷰를 바로 리턴하지 않고 변수에 저장

        fragmentView.findViewById<TextView>(R.id.fragmentTextView).setOnClickListener { // 뷰에 클릭 리스너 추가
            (activity as FragmentContainer_11).printTestLog() // activity 키워드로 호스트 액티비티 가져오기. 호스트 액티비티 중 FragmentContainer_11로 캐스팅
        }

        return fragmentView // 뷰 리턴
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val data: String? = arguments?.getString("key")
        Log.d("testt", "data : " + data)
    }
}

 

3. 호스트 Activity에서 Fragment로 접근하기

• 프래그먼트에 있는 함수를 호스트 액티비티에서 접근해보자.

 

• 프래그먼트인 FragmentOne_11.kt에 printTestLogFragment() 생성.

package com.example.fastcampus

import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment

class FragmentOne_11: Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val fragmentView = inflater.inflate(R.layout.activity_fragment_one11, container, false)

        fragmentView.findViewById<TextView>(R.id.fragmentTextView).setOnClickListener {
            (activity as FragmentContainer_11).printTestLog()
        }

        return fragmentView
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val data: String? = arguments?.getString("key")
        Log.d("testt", "data : " + data)
    }

    fun printTestLogFragment() {
        Log.d("testt", "print test log by fragment")
    }
}

 

• 호스트 액티비티인 FragmentContainer_11.kt에서 프래그먼트의 printTestLogFragment 함수 접근

package com.example.fastcampus

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.TextView

class FragmentContainer_11 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_fragment_container11)

        val fragmentManager = supportFragmentManager
        val fragmentOne = FragmentOne_11()

        findViewById<TextView>(R.id.textAdd).setOnClickListener {
            val transaction = fragmentManager.beginTransaction()

            val bundle = Bundle()
            bundle.putString("key", "hello")
            fragmentOne.arguments = bundle

            transaction.replace(R.id.root, fragmentOne, "fragmentOneTag") // 접근을 위한 Tag 입력
            transaction.commit()
        }

        findViewById<TextView>(R.id.textRemove).setOnClickListener {
            val transaction = fragmentManager.beginTransaction()
            transaction.remove(fragmentOne)
            transaction.commit()
        }

        findViewById<TextView>(R.id.hostActivityTextView).setOnClickListener {
            // xml로 포함시킨 fragment를 id로 접근
            val fragmentOne = supportFragmentManager.findFragmentById(R.id.fragment_container_view1) as FragmentOne_11
            fragmentOne.printTestLogFragment()
            
            // 코틀린 프로그래밍으로 포함시킨 fragment를 tag로 접근
            val fragmentOne2 = supportFragmentManager.findFragmentByTag("fragmentOneTag") as FragmentOne_11
            fragmentOne2.printTestLogFragment()
        }
    }

    fun printTestLog() {
        Log.d("testt", "print test log")
    }
}

 

• 결과

     - xml로 포함시킨 프래그먼트에서 나온 로그와 코틀린 프로그래밍으로 포함시킨 프래그먼트에서 나온 로그 모두 잘 출력된 모습.

     * 코틀린 프로그래밍으로 포함시킨 프래그먼트의 로그를 띄우기 위해선 add를 눌러 프래그먼트를 추가해줘야 한다.

 

 

 

 

 

 

 

 

 

이 글은

패스트 캠퍼스 Android 앱 개발의 정석 with Kotlin 올인원 패키지 Online

강의를 듣고 공부한 내용을 바탕으로 작성되었습니다.

 


728x90
댓글
공지사항