GDSC HUFS 3기/Android with Kotlin Team 5

[5팀] 코틀린 안드로이드 기초강의_31~32 | 프래그먼트

루이란 2021. 11. 3. 23:45

이 글은 이것이 안드로이드다 with 코틀린(개정판)를 참고하여 작성하였습니다.

작성자 : 임예람

개발환경은 Windows, Android Studio입니다. 

 

1. (14-5) 화면 구성하기: 프래그먼트

📌프래그먼트: 액티비티 내에서 화면 UI의 일부이다. 여러 개의 프래그먼트를 조합해 액티비티가 출력하는 한 화면의 UI를 표현할 수 있으며 하나의 프래그먼트를 다른 액티비티에 재사용할 수 있다.

 

<프래그먼트 사용하기>

 프래그먼트 만드는 순서

👉🏻 프래그먼트를 만들어 준다.

<resources>
    <string name="app_name">Fragment</string>

    <string name="title_list">List</string>
    <string name="title_detail">Detail</string>
</resources>

✍🏻 strings.xml에 다음과 같이 코드를 적으면 각 name 값으로 id 값이 부여된다.

👉🏻 다음과 같이 프래그먼트를 만들어 준다.

👉🏻 팔레트에서 Fragments를 클릭하면 본인이 만든 프래그먼트 사용 가능하다.

✍🏻 위의 방법 대신 프레임 레이아웃을 사용하면 동작이 더욱 원활하다.

 

<프래그먼트 화면 전환하기>

package com.example.fragment

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.fragment.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
    val listFragment by lazy { ListFragment() }
    val detailFragment by lazy { DetailFragment() }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
        setFragment()
    }

    //디테일 페이지로 이동
    fun goDetail(){
        val transaction = supportFragmentManager.beginTransaction()
        transaction.add(R.id.frameLayout, detialFragment)
        transaction.addToBackStack("detail")
        transaction.commit()
    }

    //뒤로 이동
    fun goBack() {
        //뒤로 가기 명령어를 담고 있다.
        onBackPressed()
    }

    fun setFragment() {
        //1. 사용할 프래그먼트 생성
//        val listFragment = ListFragment()
        //2. 트랜잭션 생성
        val transaction = supportFragmentManager.beginTransaction()
        //3. 트랜잭션을 통해 프래그먼트 삽입
        transaction.add(R.id.frameLayout, listFragment)
        transaction.commit()
    }
}

mainActivity.kt

package com.example.fragment

import android.content.Context
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.example.fragment.databinding.FragmentListBinding

//프래그먼트가 메인 액티비티에 삽입되는 순간 호출된다.
class ListFragment : Fragment() {

    lateinit var binding: FragmentListBinding

    //변수에 메인 액티비티 담아둠
    lateinit var mainActivity: MainActivity

    //나를 삽입한 액티비티 담김
    override fun onAttach(context: Context) {
        super.onAttach(context)
        if(context is MainActivity) mainActivity = context
    }

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

        return binding.root
    }

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

        //버튼 누르면 디테일 페이지로 이동하도록 한다.
        binding.btnNext.setOnClickListener{
            mainActivity.goDetail()
        }
    }
}

ListFragment.kt

package com.example.fragment

import android.content.Context
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.example.fragment.databinding.FragmentDetailBinding
import com.example.fragment.databinding.FragmentListBinding

class DetailFragment : Fragment() {

    lateinit var binding: FragmentDetailBinding
    lateinit var mainActivity: MainActivity

    //나를 삽입한 액티비티 담김
    override fun onAttach(context: Context) {
        super.onAttach(context)
        if(context is MainActivity) mainActivity = context
    }

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

        return binding.root
    }

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

        //버튼 누르면 이전전 페이로 이동하도록 한다.
        binding.btnNext.setOnClickListener{
            mainActivity.goBack()
        }
    }

}

DetailFragment.kt

✍🏻 clickable:True: 뒤에 있는 프래그먼트가 클릭되지 않도록 한다.

 

<프래그먼트에 값 전달하기>

    fun setFragment() {
        
        val bundle = Bundle()
        bundle.putString("key1", "List Fragment")
        bundle.putInt("key2", 20210331)
        
        listFragment.arguments = bundle
        
        val transaction = supportFragmentManager.beginTransaction()
        //3. 트랜잭션을 통해 프래그먼트 삽입
        transaction.add(R.id.frameLayout, listFragment)
        transaction.commit()
    }

mainActivity.kt

👉🏻 setFragment에서 어떠한 값을 보낼지 설정한다.

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

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


            //버튼 누르면 디테일 페이지로 이동하도록 한다.
            btnNext.setOnClickListener{
                mainActivity.goDetail()
            }
        }
    }

ListFragment.kt

✍🏻 apply: 이 메소드를 사용하면 arguments가 this를 의미한다.

    fun setValue(value: String){
        binding.textFromActivity.text = value
    }

👉🏻 값을 받아오는 함수 새로 생성한다.

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
        setFragment()
        
        //버튼을 누르면 값을 전달해서 표시한다.
        binding.btnSend.setOnClickListener{
            listFragment.setValue("값 전달하기")
        }
    }

👉🏻 send 버튼을 누르면 값이 이동하도록 이벤트 설정을 한다.

 

2. 14-5) 화면 구성하기: 프래그먼트끼리 값 주고 받기

📌 목표: Sender 프래그먼트에서 Receiver 프래그먼트로 값 주고 받기!

sender

👉🏻 버튼을 2개 만들어 연결한다.

class ReceiverFragment : Fragment() {
    
    lateinit var binding: FragmentReceiverBinding

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

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        //키와 번들을 보내며 수신 준비 완료
        setFragmentResultListener("request") { key, bundle ->
            bundle.getString("senderKey")?.let{
                binding.textiew.text = value
            }
        }
    }
}

ReceiverFragment.kt

👉🏻 값을 받을 준비 끝

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
    }

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

        with(binding) {
            btnYes.setOnClickListener{
                //"senderkey" 값에서 "Yes"값을 담겠다는 의미
                //여기서 senderkey는 아까의 request
                val bundle = bundleOf("senderkey" to "Yes")
                setFragmentResult("request", bundle)
            }
            btnNo.setOnClickListener{
                //"senderkey" 값에서 "Yes"값을 담겠다는 의미
                val bundle = bundleOf("senderkey" to "No")
                setFragmentResult("request", bundle)
            }
        }
    }
}

SenderFragment.kt

👉🏻 값을 보내는 코드