반응형

fragment 를 이동하는 방법에는 두가지가 있다.

 

navication component 를 사용하는것과 사용하지 않는것.

 

1~2 페이지만 있다면 일반

뒤로가기, 딥링크, 애니메이션 등 효과를 줘야한다면 navigation component 사용하면 된다

 

1. Navigation graph 설정

네비게이션 컴포넌트를 사용하려면 navigation graph 를 추가해야한다.

그전에 우선 build.gradle 을 수정하자

 

1) build.gradle 수정

dependencies {

...
    def nav_version = "2.4.1"

    // navigation
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
}

 

2) Main Fragment 생성

Main Activity 에 띄울 mainFragment 를 생성한다

fragment_main.xml 과 MainFragment.kt 파일을 각각  생성하자

 

build.gradle 데이터바인딩 설정 추가

android {
...
    dataBinding {
        enabled = true
    }
}

dependencies {
...

 

fragment_main.xml 추가

<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white">

        <EditText
            android:id="@+id/et_message"
            android:layout_width="144dp"
            android:layout_height="35dp"
            android:background="#ffdddd"
            app:layout_constraintBottom_toTopOf="@id/btn_move_second"
            app:layout_constraintEnd_toEndOf="@id/btn_move_second"
            app:layout_constraintStart_toStartOf="@id/btn_move_second" />

        <Button
            android:id="@+id/btn_move_second"
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:text="go second"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <Button
            android:id="@+id/btn_move_third"
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:text="go Third"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/btn_move_second" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

 

MainFragment 추가

class MainFragment : Fragment() {

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

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = DataBindingUtil.inflate(
            layoutInflater, R.layout.fragment_main, container, false
        )

        return binding.root
    }

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

        binding.apply {
            btnMoveSecond.setOnClickListener {

            }

            btnMoveThird.setOnClickListener {

            }
        }
    }

}

 

3) navigation graph 추가

res 폴더에 navigation 패키지를 생성하고 navigation_graph.xml 파일을 생성한다

<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/navigation_graph"
    app:startDestination="@id/mainFragment">

    <fragment
        android:id="@+id/mainFragment"
        android:name="com.example.movepage.MainFragment"
        android:label="MainFragment">
    </fragment>


</navigation>

 

startDestination : 제일 먼저 실행 할 fragment 를 설정한다
fragment : 기본적으로 보여줄 frament 를 넣어준다. 해당공간에 앞으로 이동될 fragment 들이 위치할것

 

2. Fragment 이동

상단의 title bar 에서 뒤로가기, fragment 에서 다른 fragment 로 이동하기를 해보자

 

1) title bar 생성

적당히 타이틀, 뒤로가기버튼을 넣어줬다

<?xml version="1.0" encoding="utf-8"?>

<layout 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">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:background="@color/title_background"
        tools:context=".SecondFragment">

        <ImageButton
            android:id="@+id/btn_back"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_marginStart="20dp"
            android:background="@drawable/btn_back"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/txt_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="title"
            android:textSize="18dp"
            android:textColor="@color/black"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

 

 

2) Second Fragment , Thid Fragment 추가

첫번째로 이동할 페이지를 만들어준다

 

title 추가 해주고, 다른 fragment 로 이동할 버튼하나 넣어준다

<layout 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">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/teal_200"
        tools:context=".SecondFragment">

        <include
            android:id="@+id/inc_titlebar"
            android:layout_height="50dp"
            android:layout_width="match_parent"
            layout="@layout/inc_title_bar" />

        <Button
            android:id="@+id/btn_move"
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:text="go third"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

 

데이터바인딩 로직추가

class SecondFragment : Fragment() {

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

    companion object {
        fun newInstance() : SecondFragment {
            val fragment = SecondFragment()
            val args = Bundle()
            fragment.arguments = args
            return fragment
        }
    }

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

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

        binding.apply {
            incTitlebar.txtTitle.text = "Second"
            incTitlebar.btnBack.setOnClickListener {
                
            }
            
            btnMove.setOnClickListener {
                
            }
        }
    }
}

 

이런식으로 Third Fragment 도 생성해준다.

 

그리고 타이틀의 뒤로가기 버튼과 화면 페이지이동 버튼에 onclick 이벤트를 줄건데

다수의 fragment 에서 공통으로 fragment 이동로직을 사용해야하므로, 해당로직은 MainActivity 에 넣기로 하자

 

4) Main Activity 에 공통 fragment 이동 로직 추가

MainActivity 위에 다양한 fragment 가 올라갈것이고,

모든 fragment 들이 공통적으로 페이지이동 로직이 필요하기때문에, MainActivity 에 fragment 이동 공통로직을 작성한다

 

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

	//추가
	fun openFragment(fragment: Fragment, tag: String) {
        val transaction: FragmentTransaction = supportFragmentManager.beginTransaction()
        transaction.replace(R.id.nav_host_fragment, fragment)
        transaction.addToBackStack(tag)
        transaction.commit()
    }

}

 

 

해당 로직을 MainFragment, SecondFragment 에서 사용하자

 

MainFragment

btnMoveSecond.setOnClickListener {
    val act = activity as MainActivity
    act.openFragment(ThirdFragment() , "2")
}
btnMoveThird.setOnClickListener {
    val act = activity as MainActivity
    act.openFragment(ThirdFragment() , "3")
}

 

내가 변경하고싶은 fragment 와 해당 fragment 임을 특정할수있는 TAG를 끼워준다

 

Second Fragment

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

    //추가
    binding.apply {
        incTitlebar.txtTitle.text = "Second"

        incTitlebar.btnBack.setOnClickListener {
        }

        btnMove.setOnClickListener {
            val act = activity as MainActivity
            act.openFragment(ThirdFragment(), "3")
        }
    }
}

 

title 의 text 를 주고 onclick 이벤트를 작성했다.

 

이렇게 MainFragment 의 로직을 통해서 fragment 이동이 가능하다

 

3. 뒤로가기

title bar 에서 공통으로 사용될 뒤로가기 로직을 MainActivity 에 작성한다

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    fun navigateUp(tag: String) {
        Log.d("TLOG", "btn back 2.................")
        supportFragmentManager.popBackStack(tag, FragmentManager.POP_BACK_STACK_INCLUSIVE)
    }
    
    ...

 

supportFargmentManager.popbackStack 을 이용하는데, 이때 파라미터로 tag 값이 필요하다

그리고 POP_BACK_STACK_INCLUSIVE 옵션은 백스택에서 해당 Fragment와 위의 Fragment 들을 모두 제거한다

 

이것을 활용하여 뒤로가기를 실행해보자

 

Second Fragment와 Third fragment 에서 titlebar의 click이벤트를 설정한다

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

    binding.apply {

        //추가
        incTitlebar.txtTitle.text = "Second"
        incTitlebar.btnBack.setOnClickListener {
            val act = activity as MainActivity
            act.navigateUp("2")
        }

        btnMove.setOnClickListener {
            val act = activity as MainActivity
            act.openFragment(ThirdFragment(), "3")
        }
    }
}

 

이렇게하면 뒤로가기 완료

 

 

4. 홈으로 이동하기

모든 back Stack 을 전부 지우고 MainFragment 로 이동하는 로직이다

 

이것도 동일하게 MainActivity 에 로직을 추가한다

fun clearBackStack() {
    val fragmentManager: FragmentManager = supportFragmentManager
    fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
}

 

기존에는 TAG 값을 넣고 해당 TAG 를 없앴지만, null 을 넣으면 모든 stack 을 없애준다

 

5. 데이터 전달하기

특정 fragment 에서 다른 fragment 로 데이터 전달하는 로직을 만들어보자

 

1) MainFragment 에 EditText 추가

...
        <include
            android:id="@+id/inc_titlebar"
            android:layout_height="50dp"
            android:layout_width="match_parent"
            layout="@layout/inc_title_bar" />

		//추가
        <EditText
            android:id="@+id/et_message"
            android:layout_width="144dp"
            android:layout_height="35dp"
            android:background="#ffdddd"
            app:layout_constraintBottom_toTopOf="@id/btn_move_second"
            app:layout_constraintEnd_toEndOf="@id/btn_move_second"
            app:layout_constraintStart_toStartOf="@id/btn_move_second" />

        <Button
            android:id="@+id/btn_move_second"
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:text="go second"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
....

 

그리고 onViewCreated 에서 secondFragment 로 이동할대 해당 string 값을 전달해주자

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

    binding.apply {

        incTitlebar.btnBack.visibility = View.GONE
        incTitlebar.txtTitle.text = "MAIN"

        btnMoveSecond.setOnClickListener {
            val paramData = if(etMessage.text.isNotEmpty()) etMessage.text.toString() else "empty msg"
            val bundle = Bundle()
            bundle.putString("param", paramData)
            val act = activity as MainActivity
            act.openFragment(SecondFragment.newInstance().apply {
                arguments = bundle
            }, "2")
        }
...

 

editText 값이 empty 라면 empty msg 라는 string을 세팅하고 bundle을 만들어서 string 값울 붙인다.

그리고 SecondFragment 에서 newInstance 할때 해당 bundle을 가져갈수있도록 argments에 넣어준다

 

2) Second Fragment 에서 받기

class SecondFragment : Fragment() {

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

    var strParam = ""

    companion object {
        fun newInstance() : SecondFragment {
            val fragment = SecondFragment()
            val args = Bundle()
            fragment.arguments = args
            return fragment
        }
    }

    fun setJsonParam() {
        val bundle = arguments
        bundle?.let {
            strParam = it.getString("param", "").toString()
            Log.d("TLOG - SecondFragment", "받은값 : $strParam")
        }
    }

...

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
		
        //추가
        setJsonParam()
        
        binding.apply {
...

 

newInstance 에서 MainFragment 에서 보낸 Bundle 을 argument 에 저장하고,

해당 arguments 를 풀어서 strParam에 저장했다

 

 

 

전체 소스코드는 아래 깃주의 "movePage" 프로젝트에 위치해있다

https://github.com/Daseul727/Mobile-Skill-Up

728x90

+ Recent posts