[Android] 5(3). 프래그먼트 (프래그먼트로 값 전달하기)

5 minute read


프래그먼트에 관한 포스팅은 3개로 나누어 진행합니다.

첫번째 포스팅: 프래그먼트 생성과 전환

두번째 포스팅: 프래그먼트로 값 전달하기

세번째 포스팅: 프래그먼트의 생명주기 관리

본 포스팅은 프래그먼트 포스팅 중 두 번째 포스팅으로, 첫 번째 포스팅인 ‘프래그먼트(프래그먼트 생성과 전환)’ 에 이어서 작성됩니다.


프래그먼트 (프래그먼트로 값 전달하기)

프래그먼트로 값을 전달하는 방법에는 크게 두 가지가 있습니다.

하나는 프래그먼트 생성 시에 값을 전달하는 것이고, 또 하나는 이미 생성되어 있는 프래그먼트에 값을 전달하는 것입니다.


프래그먼트 생성 시 값 전달하기


프래그먼트를 생성하며 값을 전달할 때는 프래그먼트의 arguments 프로퍼티를 이용합니다.

전달할 값을 번들에 담고 번들을 arguments에 전달하면 생성된 프래그먼트에서 arguments로 꺼낼 수 있습니다.


프래그먼트에 값 전달

1. 번들 생성하고 값 담기

// 액티비티에 프래그먼트를 삽입하는 메서드
fun setFragment(){

    val listFragment: ListFragment = ListFragment()

    // 번들을 하나 생성하고 전달할 값을 담는다. (인텐트와 동일)
    var bundle = Bundle()
    bundle.putString("key1", "List Fragment")
    bundle.putInt("key2", 20210101)

    val transaction = supportFragmentManager.beginTransaction()
    transaction.add(R.id.frameLayout, listFragment)
    transaction.commit()
}


2. arguments에 번들 전달

// 액티비티에 프래그먼트를 삽입하는 메서드
fun setFragment(){

    val listFragment: ListFragment = ListFragment()

    var bundle = Bundle()
    bundle.putString("key1", "List Fragment")
    bundle.putInt("key2", 20210101)
    // 값이 담긴 번들을 프래그먼트의 arguments에 담는다.
    listFragment.arguments = bundle

    val transaction = supportFragmentManager.beginTransaction()
    transaction.add(R.id.frameLayout, listFragment)
    transaction.commit() 
}


3. 트랜잭션 시작


프래그먼트에서 값 꺼내기

4. arguments에서 값 꺼내기

프래그먼트의 onCreateView 안에서 arguments에 들어있는 값을 꺼내서 사용합니다.

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
        val binding = FragmentListBinding.inflate(inflater, container, false) 

        // arguments에서 값을 꺼내고 텍스트뷰에 입력하기
        binding.textTitle.text = arguments?.getString("key1")
        binding.textValue.text = "${arguments?.getInt("key2")}"

        return binding.root
    }



생성되어 화면에 보이는 프래그먼트에 값 전달하기


이미 생성되어 있는 프래그먼트에 값을 전달할 때에는 프래그먼트에 메서드를 정의하고 액티비티에서 fragment.setValue( )의 형태로 메서드를 직접 호출하면 됩니다.

전달된 값을 액티비티에서 전송하고 프래그먼트에서 보기 위해 액티비티에는 id가 ‘btnSend’인 버튼을 하나 만들고 프래그먼트에는 id가 ‘textFromActivity’인 텍스트뷰를 하나 만들고 진행합니다.


1. 프래그먼트 바인딩을 전역 프로퍼티로 만들기

지금까지는 onCreateView 메서드 안에서만 바인딩을 참조했기 때문에 메서드 내에 선언했지만, 다른 메서드들에서도 모두 사용할 수 있도록 전역 프로퍼티로 선언하는 것이 좋습니다.

클래스 스코프 맨 위에 binding을 선언하고 onCreateView 의 코드를 수정합니다.

class ListFragment : Fragment() {
  
    var mainActivity: MainActivity? = null

    // 프래그먼트 바인딩을 전역변수로 만들기
    lateinit var binding:FragmentListBinding

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // val binding = FragmentListBinding.inflate(inflater, container, false) -> 전역변수로 만들기
        binding = FragmentListBinding.inflate(inflater, container, false)
        binding.btnNext.setOnClickListener { mainActivity?.goDetail() }

        binding.textTitle.text = arguments?.getString("key1")
        binding.textValue.text = "${arguments?.getInt("key2")}"

        return binding.root 
    }
    ...


2. 프래그먼트에서 setValue 메서드 정의

이제 텍스트뷰에 액티비티에서 보낸 값을 출력하는 setValue 메서드를 정의합니다.

// 액티비티로부터 전달받을 문자열을 출력
fun setValue(value: String){
    binding.textFromActivity.text = value
}

3. 액티비티에서 setValue 메서드 호출하기

액티비티에 버튼 리스너를 만들고, 버튼을 클릭하면 프래그먼트의 setValue 메서드를 호출하여 값을 보낼 수 있도록 합니다.

액티비티에서도 기존에는 setFragment 메서드 내에서 프래그먼트가 생성되었지만, 이제 전역 프로퍼티로 수정합니다.

class MainActivity : AppCompatActivity() {

    // 메인 액티비티 바인딩 연결
    val binding by lazy {ActivityMainBinding.inflate(layoutInflater)}

    // 프래그먼트 전역변수 선언
    lateinit var listFragment: ListFragment

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 바인딩된 뷰 전달
        setContentView(binding.root)

        setFragment()

        // 버튼 클릭 시 listFragment의 setValue 호출
        binding.btnSend.setOnClickListener {
            listFragment.setValue("From Activity")
        }
    }

    fun setFragment(){
        // val listFragment: ListFragment = ListFragment() -> 전역변수로 만들기
        listFragment = ListFragment()

        var bundle = Bundle()
        bundle.putString("key1", "List Fragment")
        bundle.putInt("key2", 20210101)
        listFragment.arguments = bundle

        val transaction = supportFragmentManager.beginTransaction()
        transaction.add(R.id.frameLayout, listFragment)
        transaction.commit()
    }


여기까지의 과정을 따라오고 에뮬레이터를 실행하여 SEND 버튼을 누르면 아래와 같은 화면이 출력됩니다.

image-20210805174101451



프래그먼트 간에 값 전달하기


프래그먼트 간에 값을 송수신 할 때는 송신 측에서는 setFragmentResult 메서드를, 수신 측에서는 setFragmentResultListener 메서드를 사용합니다.

그런데 그보다 먼저 gradle 파일의 설정이 필요합니다.


사전 작업

1. gradle 파일 설정

프래그먼트 간 값을 송수신하려면 build.gradle 파일의 dependencies 영역에 프래그먼트 버전 1.3.0-beta02코틀린용 fragment 1.3.0 버전을 추가해야 합니다.

dependencies {
		...
  
    // 프래그먼트 버전 1.3.0-beta02
    def fragment_version = "1.3.0-beta02"
    // 자바용 fragment 1.3.0
    // implementation "androidx.fragment:fragment:$fragment_version"
    // 코틀린용 fragment 1.3.0
    implementation "androidx.fragment:fragment-ktx:$fragment_version"
}

또한 android 스코프에 뷰바인딩도 잊지 말고 설정합니다.


송신용 프래그먼트

2. 송신용 프래그먼트 생성

송신용 프래그먼트를 만들고 레이아웃에 각각 id가 btnYes와 btnNo인 두 개의 버튼을 배치합니다.


3. 송신용 프래그먼트에서 setFragmentResult 메서드 호출

먼저 프래그먼트 바인딩을 전역 프로퍼티로 선언하고 onCreateView 메서드의 코드를 수정합니다.

class SenderFragment : Fragment() {

    lateinit var binding: FragmentSenderBinding

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = FragmentSenderBinding.inflate(inflater, container, false)
        return binding.root
    }
    ...
}


다음으로 OncreateView 메서드 아래에서 [Ctrl+O]를 눌러 onViewCreated 메서드를 오버라이드합니다.

이 onViewCreate 메서드 내에서 버튼 리스너를 설정합니다. 리스너가 호출되면 번들을 생성하고, setFragmentResult 메서드를 호출하여 수신용 프래그먼트에 번들을 송신합니다.

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    // YES버튼 클릭 리스너 생성
    binding.btnYes.setOnClickListener {
        val bundle = bundleOf("valueKey" to "Yes") // 값을 갖는 번들 생성
        setFragmentResult("request", bundle)    // request를 요청키로 번들 전송
    }
    // NO버튼 클릭 리스너 생성
    binding.btnNo.setOnClickListener {
        val bundle = bundleOf("valueKey" to "No") // 값을 갖는 번들 생성
        setFragmentResult("request", bundle)   // request를 요청키로 번들 전송
    }
}

bundleOf([key] to [value]) 꼴로 사용하면 값을 가진 번들을 생성할 수 있습니다.

setFragmentResult의 파라미터

  • requestKey: 프래그먼트 간 값을 주고받을 때의 요청 키
  • bundle: 전송할 데이터를 담은 번들 인스턴스


수신용 프래그먼트

4. 수신용 프래그먼트 생성

수신한 값을 확인하기 위해 레이아웃에 id가 textView인 텍스트뷰 하나를 배치합니다.


5. 수신용 프래그먼트에서 setFragmentResultListener 메서드 호출

이제 송신용 프래그먼트에서 보낸 데이터를 받기 위해 수신용 프래그먼트에서 setFragmentResultListener 메서드를 호출합니다.

마찬가지로 먼저 binding을 전역변수로 선언하고 onCreateView 메서드의 코드를 수정합니다.

class ReceiverFragment : Fragment() {

    // binding 선언
    lateinit var binding:FragmentReceiverBinding

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = FragmentReceiverBinding.inflate(inflater, container, false)
        return binding.root
    }
    ...
}


그리고 onCreateView 메서드 아래에서 onViewCreated 메서드를 오버라이딩합니다.

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    // 값을 보내는 측 프래그먼트에서 "request"라는 키로 값을 보내면 리스너 안의 코드가 실행됨.
    // 실제 값은 bundle 안에 Map의 형태로 들어있음.
    setFragmentResultListener("request"){key, bundle ->
        // 스코프 함수 let을 이용해 꺼낸 값이 있을 때만 화면의 textView에 값을 세팅
        // "request"는 요청 전체에 대한 키이고, bundle.getString에 입력되는 "valueKey"는 요청에
        // 담겨있는 여러 개 값 중 하나를 가리키는 키이다.
        bundle.getString("valueKey")?.let{
            binding.textView.text = it
        }
    }
}

setFragmentListener의 파라미터로는 요청 키를 전달합니다. 실제 정보는 bundle에 있습니다.


액티비티 레이아웃 설정

이제 마지막으로 결과를 간단히 확인하기 위해 메인 액티비티 레이아웃에서 Container 카테고리의 _FragmentContainerView_를 두 개 배치하여 각각 송신과 수신용 프래그먼트를 삽입합니다. 앞선 포스팅에서 화면 전환이 일어나지 않는 프래그먼트는 FragmentContainerView를 사용한다고 언급하였습니다.

결과화면은 다음과 같습니다.

[YES 버튼 클릭 시]

image-20210805185934473



정리


  • 프래그먼트로 값을 전달하는 경우에는 프래그먼트 생성 시 전달, 프래그먼트 생성 후 전달, 프래그먼트 간 전달의 3가지 경우가 있습니다.

  • 프래그먼트 생성 시 전달하는 경우에는 값을 전달하고자 하는 프래그먼트의 arguments 프로퍼티에 번들을 전달합니다.

  • 프래그먼트 생성 후 전달하는 경우에는 프래그먼트 소스 코드 파일에 setValue와 같은 메서드를 정의하고, 액티비티에서 setValue를 호출합니다.

  • 프래그먼트 간 전달의 경우에는 gradle 파일을 설정하고 송신용 프래그먼트에서는 setFragmentResult 메서드를, 수신용 프래그먼트에서는 setFragmentResultListener 메서드를 호출합니다.

Categories:

Updated:

Leave a comment