[Android / Kotlin] Fragment

Subeen·2024년 1월 4일
0

Android

목록 보기
30/71

Fragment

  • 프래그먼트(Fragment)는 액티비티 위에서 동작하는 모듈화된 사용자 인터페이스
    • 액티비티와 분리되어 독립적으로 동작할 수 없다.
  • 여러 개의 프래그먼트를 하나의 액티비티에 조합하여 창이 여러개인 UI를 구축할 수 있으며, 하나의 프래그먼트를 여러 액티비티에서 재 사용 할 수 있다.

Activity와 Fragment 비교

Activity

  • 시스템의 액티비티 매니저에서 인텐트를 해석해 액티비티간 데이터를 전달한다.

Fragment

  • 액티비티의 프래그먼트 매니저에서 메소드로 프래그먼트간 데이터를 전달한다.

Fragment를 사용하는 이유

  • Activity로 화면을 계속 넘기는 것보다는 Fragment로 일부만 바꾸는 것이 자원 이용량이 적어 속도가 빠르기 때문
  • Fragment를 사용하면 Activity를 적게 만들 수 있다.
  • Activity의 복잡도를 줄일 수 있다.
  • Fragment를 사용하면 재사용할 수 있는 레이아웃을 분리해서 관리가 가능하다.
  • Fragment를 사용하면 최소 1개의 Activity 안에서 Fragment 공간에 View만 집어 넣으면 여러 Activity를 만들지 않아도 여러 화면을 보여줄 수 있다.

Fragment의 데이터 전달

Activity ➡️ Fragment

Activity에서 Fragment로 데이터를 전달할 때는 프래그먼트의 인스턴스를 생성하고 newInstance 메소드를 통해 데이터를 전달한다. Bundle 객체를 사용하여 데이터를 프래그먼트의 인자(arguments)로 설정하고, 인자를 프래그먼트가 받아 사용한다.

MainActivity

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        val view = binding.root
        setContentView(view)

        binding.run {
            fragment1Btn.setOnClickListener {
                // Activity -> FirstFragment
                val dataToSend = "Hello First Fragment: \n From Activity"
                val fragment = FirstFragment.newInstance(dataToSend) // Instance를 생성해서 String 데이터를 전달
                setFragment(fragment)
            }
            fragment2Btn.setOnClickListener {
                // Activity -> SecondFragment
                val dataToSend = "Hello Second Fragment: \n From Activity"
                val fragment = SecondFragment.newInstance(dataToSend)
                setFragment(fragment)
            }
        }
        setFragment(FirstFragment())

    }

    private fun setFragment(frag: Fragment) {
        supportFragmentManager.commit {
            replace(R.id.frameLayout, frag)
            setReorderingAllowed(true)
            addToBackStack("")
        }
    }
}

FirstFragment

private const val ARG_PARAM1 = "param1"

class FirstFragment : Fragment() {
    private val binding by lazy { FragmentFirstBinding.inflate(layoutInflater) }
    private var param1: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            param1 = it.getString(ARG_PARAM1) // Bundle 안에 있는 값을 꺼내서 param1에 넣음
        }
    }

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

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // Activity -> FirstFragment
        binding.tvFrag1Text.text = param1
    }

    companion object {
        @JvmStatic
        fun newInstance(param1: String) = // Instance가 생성
            // Activity -> FirstFragment
            FirstFragment().apply {
                arguments = Bundle().apply { // Fragment의 arguments에 Bundle() 객체로 넣음
                    putString(ARG_PARAM1, param1)
                }
            }

    }
}

SecondFragment

private const val ARG_PARAM1 = "param1"

class SecondFragment : Fragment() {
    private var _binding: FragmentSecondBinding? = null
    private val binding get() = _binding!!

    private var param1: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            param1 = it.getString(ARG_PARAM1)
        }
    }

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

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.tvFrag2Text.text = param1

    }

    companion object {
        @JvmStatic
        fun newInstance(param1: String) =
            SecondFragment().apply {
                arguments = Bundle().apply {
                    putString(ARG_PARAM1, param1)
                }
            }
    }
}

Fragment ➡️ Fragment

한 프래그먼트에서 다른 프래그먼트로 데이터를 전달 할 때는, 첫 번째 프래그먼트에서 두 번째 프래그먼트의 newInstance 메소드를 사용하여 인스턴스를 생성하고, 데이터를 전달 한다.


private const val ARG_PARAM1 = "param1"

class FirstFragment : Fragment() {
    private val binding by lazy { FragmentFirstBinding.inflate(layoutInflater) }
    private var param1: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            param1 = it.getString(ARG_PARAM1) // Bundle 안에 있는 값을 꺼내서 param1에 넣음
        }
    }

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

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // Activity -> FirstFragment
        binding.tvFrag1Text.text = param1

        // Fragment -> Fragment
        binding.btnGoFrag2.setOnClickListener {
            val dataToSend = "Hello Fragment! \n From Fragment1"
            val fragment2 = SecondFragment.newInstance(dataToSend)

            requireActivity().supportFragmentManager.beginTransaction()
                .replace(R.id.frameLayout, fragment2)
                .addToBackStack(null)
                .commit()
        }
    }

    companion object {
        @JvmStatic
        fun newInstance(param1: String) = // Instance가 생성
            // Activity -> FirstFragment
            FirstFragment().apply {
                arguments = Bundle().apply { // Fragment의 arguments에 Bundle() 객체로 넣음
                    putString(ARG_PARAM1, param1)
                }
            }
    }
}

Fragment ➡️ Activity

프래그먼트에서 액티비티로 데이터를 전달 할 때는 콜백 인터페이스를 정의하고, 해당 인터페이스를 액티비티가 구현하도록 한다. 프래그먼트는 이 인터페이스를 사용하여 액티비티에 데이터를 전달한다.

Interface

interface FragmentDataListener {
    fun onDataReceived(data: String)
}

MainActivity


class MainActivity : AppCompatActivity(), FragmentDataListener {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        val view = binding.root
        setContentView(view)

        binding.run {
            fragment1Btn.setOnClickListener {
                // Activity -> FirstFragment
                val dataToSend = "Hello First Fragment: \n From Activity"
                val fragment =
                    FirstFragment.newInstance(dataToSend) // Instance를 생성해서 String 데이터를 전달
                setFragment(fragment)
            }
            fragment2Btn.setOnClickListener {
                // Activity -> SecondFragment
                val dataToSend = "Hello Second Fragment: \n From Activity"
                val fragment = SecondFragment.newInstance(dataToSend)
                setFragment(fragment)
            }
        }
        setFragment(FirstFragment())

    }

    private fun setFragment(frag: Fragment) {
        supportFragmentManager.commit {
            replace(R.id.frameLayout, frag)
            setReorderingAllowed(true)
            addToBackStack("")
        }
    }

    override fun onDataReceived(data: String) {
        Toast.makeText(this, data, Toast.LENGTH_SHORT).show()
    }
}

SecondFragment

private const val ARG_PARAM1 = "param1"

class SecondFragment : Fragment() {
    // SecondFragment -> Activity
    private var listener: FragmentDataListener? = null

    private var _binding: FragmentSecondBinding? = null
    private val binding get() = _binding!!

    private var param1: String? = null

    override fun onAttach(context: Context) {
        super.onAttach(context)
        // SecondFragment -> Activity
        if (context is FragmentDataListener) {
            listener = context
        } else {
            throw RuntimeException("$context must implement FragmentDataListener")
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            param1 = it.getString(ARG_PARAM1)
        }
    }

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

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // Fragment -> Fragment
        binding.tvFrag2Text.text = param1

        // SecondFragment -> Activity
        binding.btnsendActivity.setOnClickListener {
            val dataToSend = "Hello From SecondFragment!"
            listener?.onDataReceived(dataToSend)
        }

    }

    companion object {
        @JvmStatic
        fun newInstance(param1: String) =
            SecondFragment().apply {
                arguments = Bundle().apply {
                    putString(ARG_PARAM1, param1)
                }
            }
    }
}
profile
개발 공부 기록 🌱

0개의 댓글