IDE & Framework/Android

[Kotlin] Fragment (2) - 기초 예제

Greensky0026 2021. 12. 19. 13:04
반응형

* 본 예제는 간단히 ViewBinding을 사용합니다

https://greensky0026.tistory.com/190

 

View binding 사용해보기

findbyviewid를 쓰지 않아도 되게 해줘서 코드가 매우 간결해집니다. app level gradle //build.gradle android{ //... viewBinding { enabled = true } } //... } replace Acitivity - onCreate : setContentView..

greensky0026.tistory.com


개요

본 예저는 간단히 fragment를 버튼을 클릭해 swap해 보여주는 Application 입니다.

MainActivity는 main, another 버튼이 존재하며 클릭시 해당 fragment를 swap해 보여줍니다.

 

구성

MainActivity.kt

activity_main.mxl

MainHomeFragment.kt

fragment_main_home.xml

MainAnotherFragment.kt

fragment_main_another.xml

 

1. MainActivity.kt

변수 선언

val b by lazy {
ActivityMainBinding.inflate(layoutInflater)
}

private val fragmentManager by lazy {
	supportFragmentManager
}

private val homeFragment by lazy {
	MainHomeFragment()
}

private val anotherFragment by lazy {
	MainAnotherFragment()
}

private lateinit var transaction: FragmentTransaction
private var nowFragment: Fragment? = null

by lazy를 사용해 val 변수들을 호출시에 초기화 해주두록 세팅했습니다.

fragmentManager와 각 fragment들을 var변수로 선언했습니다.

 

transaction은 프레그먼트를 추가, 제거 및 교체를 할 수 있습니다. 자세한 사항은 여기를 참조하세요.

여기서는 단순 교체작업용 만으로 사용합니다.

nowFragment는 현재 보여지고 있는 fragment로 교채를 요청했을 때, 이벤트 발생을 위해 선언했습니다.

 

 override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   setContentView(b.root)
   replaceTransaction(homeFragment)

  b.btnPresentMainFragment.setOnClickListener {
      replaceTransaction(homeFragment)
  }
  b.btnPresentAnotherFragment.setOnClickListener {
      replaceTransaction(anotherFragment)
  }
}

 

 버튼을 세팅하고, replaceTransaction 함수를 homeFragment를 매개변수로 넣어서 호출햇습니다.

이름에서 보듯이 해당 함수는 fragment를 swap해주는 함수입니다. 

 

private fun replaceTransaction(fragment: Fragment){
  if(nowFragment == fragment){
    Toast.makeText(this, "이미 해당 Fragment를 보여주고 있습니다.", Toast.LENGTH_SHORT).show()
    return
  }
  transaction = fragmentManager.beginTransaction()
  transaction.replace(b.flFlagmentContainer.id, fragment).commitAllowingStateLoss()
  nowFragment = fragment
}

교체시 nowFragment에 현재 fragment를 확인해 같은 프래그먼트라면 교체작업을 진행하지 않고 토스트로 명시 후 작업을 종료합니다.

교체시엔 beginTransaction()호출 후 작업을 명시한 후에 commitAllowingStateLoss를 호출하면 됩니다.

트랜젝션을 시작 후 커밋해야 하다니, 약간은 db 명령어 같죠? ㅎㅎ

교체또한 replace에 fragment를 담을 container view와 framgent객채만 입력해주면 됩니다.

 

2. activity_main.mxl

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    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=".MainActivity"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:orientation="horizontal">
        <View
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="0dp"/>
        <Button
            android:id="@+id/btn_present_main_fragment"
            android:layout_width="120dp"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:text="main"/>
        <Button
            android:id="@+id/btn_present_another_fragment"
            android:layout_width="120dp"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:text="another"/>
        <View
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="0dp"/>
    </LinearLayout>
    <FrameLayout
        android:id="@+id/fl_flagment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

간단히 버튼 두개와 프래그먼트를 출력할 FramLayout으로 구성되어 잇습니다.

 

3. MainHomeFragment.kt

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

우선은 변수선언부터 해줍니다. view binding을 할 것이므로 초기화 해 주고,

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

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

onCreateView로 view를 그리기 전, 그릴 bingding 객체를 inflate하여 _binding에 저장하고 반환합니다.

override fun onDestroy() {
  super.onDestroy()
  _binding = null
}

위와같이 작업한다면, 메모리 누수를 방지하기 위해 onDestroy에 꼭!!! _binding을 초기화 해주어야 합니다.

이런 일련의 작업이 불편하다면, androidx.lifecycle을 사용해 편하게 작성할 수 잇습니다. 여기를 참고하세요.

4. fragment_main_home.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainHomeFragment"
    android:background="#FFEFEF">

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Home Flagment"
        android:textSize="40dp"
        android:textColor="@color/black"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Fragment를 구분하기 위해 배경색과 text를 입력해뒀습니다.

MainAnotherFragment.kt와 fragment_main_another.xml도 같은 형식이니 생략하겠습니다.

궁금하시다면 아래 전체 코드보기로 봐주세요 ㅎㅎ

 

결과

 

 

 


전체 코드 보기

https://github.com/gr2nsky/FlagmentPactice

 

GitHub - gr2nsky/FlagmentPactice

Contribute to gr2nsky/FlagmentPactice development by creating an account on GitHub.

github.com

MainActivity

더보기
class MainActivity : AppCompatActivity() {

    val b by lazy {
        ActivityMainBinding.inflate(layoutInflater)
    }

    private val fragmentManager by lazy {
        supportFragmentManager
    }

    private val homeFragment by lazy {
        MainHomeFragment()
    }

    private val anotherFragment by lazy {
        MainAnotherFragment()
    }

    private lateinit var transaction: FragmentTransaction
    private var nowFragment: Fragment? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(b.root)
        replaceTransaction(homeFragment)

        b.btnPresentMainFragment.setOnClickListener {
            replaceTransaction(homeFragment)
        }
        b.btnPresentAnotherFragment.setOnClickListener {
            replaceTransaction(anotherFragment)
        }
    }

    private fun replaceTransaction(fragment: Fragment){
        if(nowFragment == fragment){
            Toast.makeText(this, "이미 해당 Fragment를 보여주고 있습니다.", Toast.LENGTH_SHORT).show()
            return
        }
        transaction = fragmentManager.beginTransaction()
        transaction.replace(b.flFlagmentContainer.id, fragment).commitAllowingStateLoss()
        nowFragment = fragment

    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    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=".MainActivity"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:orientation="horizontal">
        <View
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="0dp"/>
        <Button
            android:id="@+id/btn_present_main_fragment"
            android:layout_width="120dp"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:text="main"/>
        <Button
            android:id="@+id/btn_present_another_fragment"
            android:layout_width="120dp"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:text="another"/>
        <View
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="0dp"/>
    </LinearLayout>
    <FrameLayout
        android:id="@+id/fl_flagment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

MainHomeFragment

더보기
class MainHomeFragment : Fragment() {

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

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

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

    override fun onDestroy() {
        super.onDestroy()
        _binding = null
    }
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainHomeFragment"
    android:background="#FFEFEF">

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Home Flagment"
        android:textSize="40dp"
        android:textColor="@color/black"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

MainAnotherFragment

더보기
class MainAnotherFragment : Fragment() {

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

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

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

    override fun onDestroy() {
        super.onDestroy()
        _binding = null
    }

}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainAnotherFragment"
    android:background="#EFF2FF">

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Another Flagment"
        android:textSize="40dp"
        android:textColor="@color/black"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

 

 

 

 

 

 

반응형