마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

액티비티 -> 프래그먼트 데이터 보내기 질문..

0 추천

http://www.masterqna.com/android/95609/%EC%95%A1%ED%8B%B0%EB%B9%84%ED%8B%B0-%ED%94%84%EB%9E%98%EA%B7%B8%EB%A8%BC%ED%8A%B8-%EA%B0%9D%EC%B2%B4-%EC%A0%84%EB%8B%AC%EA%B3%BC-%ED%94%84%EB%9E%98%EA%B7%B8%EB%A8%BC%ED%8A%B8-%EB%A7%A4%EB%8B%88%EC%A0%80%EC%97%90-%EB%8C%80%ED%95%9C-%EC%A7%88%EB%AC%B8

얼마전에 링크의 질문을 올렸었는데요

액티비티에서 -> 프래그먼트로 데이터 보낼시 getParcelable에서 에러가나고

원인이 xml파일의 <fragment>태그 였다..는 대충 이런 질문이었는데요..

그래서 답변자님께서 조언해주신대로

.

MainActivity.java

public class MainActivity extends AppCompatActivity {
    SampleData data;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ShowFragment showFragment = (ShowFragment) getSupportFragmentManager().findFragmentById(R.id.showfragment);
        if(showFragment != null)
            Log.d("프래그먼트 확인", "프래그먼트 확인");
        data = new SampleData("ABCD", "20201031");
        Bundle bundle = new Bundle();
        bundle.putParcelable("key", data);
        showFragment.setArguments(bundle);


    }
}

ShowFragment.java

public class ShowFragment extends Fragment {
    SampleData sampleData;
    TextView textView;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Bundle bundle = this.getArguments();
        sampleData = bundle.getParcelable("key");
        Log.d("확인용", "title : " + sampleData.title);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container2,
                             Bundle savedInstanceState) {

        ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_show, container2, false);
        textView = rootView.findViewById(R.id.title);
        textView.setText(sampleData.title);

        return rootView;
    }
}

 

으로 시도를 해보았는데요 그럼에도 불구하고 똑같이 에러가 납니다...

솔직하게 이번에는 될줄알았습니다 왜냐면 이전에는

xml에 <fragment> 를 사용해서 자동적으로 인플레이션인 상황에서

거기서 또 프래그먼트 매니저를 사용하려 했으니(정확하게는 맞는지 모르겠네요) 안되었지만..

이번에는 <fragment>로 자동으로 인플레이션되어 생성되는 프래그먼트를 참조하기위해

getSupportFragmentManger().findFragmentBtId 를 사용해 해당 프래그먼트를 참조해서 사용하려 했음에도

똑같은 에러가나는데..

도대체 원인이 뭔가요..? getSupportManager가 <fragment>가 생성되는 액티비티의 프래그먼트 매니저를 

단순 참조가아니라 생성해내는 구조인가요? 그래서 프래그먼트 매니저가 두개라서 오류가나는건지..

이번에는 잘 될줄알았는데 이유를 모르겠습니다..

 

 

codeslave (3,940 포인트) 님이 2020년 11월 3일 질문

4개의 답변

0 추천

님이  xml에 여전히 <fragment>를 사용하신 다고 했으니, 아래처럼, 바꾸시면 될 것 같은데요. 

//MainActivity.java
MovieFragment fragment = (MovieFragment) supportFragmentManager.findFragmentById(id);
if (fragment != null) {
   fragment.setMovie(movie);
}

//MovieFragment
private Movie movie;

public void setMovie(Movie movie) {
     this.movie = movie;
     // Do something
}
spark (226,420 포인트) 님이 2020년 11월 3일 답변
spark님이 2020년 11월 3일 수정
제 권장사항은 <fragment>를 xml에서 없애고 <FragmentContainerView>를 사용하셔서 동적으로 Fragment를 생성하는 방법입니다. <fragment>는 간편하게 화면에  Fragment가 코딩없이 생성되거나 headless fragment라고 지금은 사용하지 않는 라이사이플컴포넌트가 생기기전에 트릭을 사용하기 위해 사용하던 방법 중의 하나예요. 개인적으로는 <fragment>를 사용한 코드는 학습용 말고는 본 적이 없긴 해요.
<fragment>를 유지하고 싶으시고 번들을 프레그먼트로 전달하고 싶으시다면 FragmentFactory라는 클래스가 있습니다. 이 클래스를 이용하면 Fragment 생성에 관여할 수 있습니다. 이건 보통 ViewModelFactory가 ViewModel을 injection하기 위해 DI 라이브러리에 사용되는 것처럼 Fragment 생성자에 파라미터를 전달하기 위해 사용됩니다.
0 추천

제가 간단히 테스트 해 봤는데, 별 이상없이 잘 동작하는데요. 제가 해봤던 것을 공유할게요. 그리고 참고로 Transition은 사용할 수 없지만 setArgument를 이용해도 값은 잘 넘어갑니다.

Movie.java
 

public class Movie {
    private final int id;
    private final String name;

    public Movie(int id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Movie{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}

 

main_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <fragment
        android:id="@+id/myFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:tag="MyFragment"
        android:name="com.mark.sunhgun.park.myapplication.ui.main.MainFragment" />

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/testBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        android:layout_marginEnd="16dp"
        android:src="@android:drawable/ic_input_add"
        android:layout_gravity="right|bottom"
        android:contentDescription="TODO" />
</FrameLayout>

 

MainActivity.java
 

public class MainActivity extends AppCompatActivity {

    private FloatingActionButton testBtn;
    private int count = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_activity);
        setupView();
    }

    private void setupView() {
        testBtn = findViewById(R.id.testBtn);
        testBtn.setOnClickListener(v -> {
            MainFragment fragment = (MainFragment) getSupportFragmentManager().findFragmentById(R.id.myFragment);
            if (fragment != null) {
                fragment.setMovie(new Movie(count++, "Movie" + count));
            }
        });
    }
}

main_fragment.xml
 

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.main.MainFragment">

    <TextView
        android:id="@+id/message"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

MainFrament.xml

public class MainFragment extends Fragment {

    private TextView messageTxt;
    private Movie movie;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.main_fragment, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        
        messageTxt = requireView().findViewById(R.id.message);
    }

    public void setMovie(Movie movie) {
        this.movie = movie;
        messageTxt.setText(movie.toString());
    }
}

 

앱실행 모습

spark (226,420 포인트) 님이 2020년 11월 3일 답변
답변감사합니다.
선생님 그런데 제가 이전글에서도 그렇고 본문에서도 초장에만 살짝 언급했지마<fragment>
태그를 사용하고, 'setArgument'로 데이터를 전송했을때
프래그먼트에서 getArgument().getParcelable()에서 null obejct에러가 난다는것이었습니다..ㅜ선생님이 하신대로 함수를 정의한대로ㅠ데이터를 전달할수있겠지만..
위의 에러가 나는이유가 궁금합니다
.ㅜ
데이터를 onCreate에서 받고 계시네요. 그럼, 당연히 Null이 나오겠죠? 언제  Fragment.onCretae가 호출되는지 이해하실 필요가 있어요. 라이프사이클이라고 들어보셨을 거예요. <fragment>를 쓰고 계시기 때문에 안드로이드 시스템에 의해 프레그먼트가 자동으로 생성되고 나서 님이 setArgument를 하기도 전에 onAttach -> onCreate -> onCreateView -> onViewCreated -> onStart -> onResume 순으로 호출됩니다. 따라서 님이 액티비티에서 setArgument 할 때는 이미 onCreate는 시스템에 의해 호출이 되고 난 상태이기 때문에 당연히 onCreate에서 값을 찍어보면 null이 나와야 겠네요. 님과 같은 상황에서는 굳이 setArgument를 사용할 이유가 없습니다. 그냥 setter  메소드를 MovieFragment로 만드신 다음 필요한 처리를 해주시면 됩니다. 굳이 setArgument를 호출하셔야 하겠다면,

showFragment.setArgument(...)
showFragment.doSomethingAfterArgumentUpdated()

처럼 setArgument하신 다음 showFragment에 argument를 업데이트 했다는 것을 알려주셔야 겠죠. 그러니 굳이 setArgument를 쓰실 이유가 없다는 겁니다.

도움이 되셨기를 바래요.
선생님 답변 감사합니다.ㅜㅜ생명주기라는것 때문에 <fragment>태글 인해 프래그먼트가 먼저 생성?되고 액티비티의 onCreate가 호출되기전에 프래그먼트 생명주기에서 onCreate()가 먼저 호출되기때문에 데이터를 보내적 없어서 null 을 참조하게 돼서 에러가 나는것이었군요..ㅜ이제야 알게돼서 너무 기쁘네요.
선생님 답변보고 생명주기 찾아보면서 직접 호출해보면서 이해를 해보니생명주기의 중요성을 깨달았습니다.
추가로 그래도 setArgument로 할 수있는 방법이 없을까 찾아보다가
생명주기에서 onActivityCreated()가 있는데 이건 프래그먼트가 액티비티에 연결되고 나면 호출이 된다고하는군요..그래서 명주기 함수마다 로그를 찍어 확인해보니 실제로
프래그먼트의 onCreate() -> onCreateView() -> 액티비티의 onCreate() -> 프래그먼트의 onActivityCreated() 순으로 로그가 찍히는걸 확인했습니다.
그래서 onCreate에서 먼저생성되기때문에 getArgument로 데이터를 받아오지 못하는 문제를 액티비티에 연결되고 나서 액티비티가 데이터를 받으면 되지 않을까해서 onActivityCreated로 getArgument해서 데이터를 추출해보았습니다.

결론은 데이터가 무사히 받아는 집니다만 ㅎㅎ..
이제 프래그먼트의 onCreateView에서 실제 xml에 데이터를 셋팅시키기면 당연 에러가 납니다.
이유는 onCreateView는 onCreate후에 호출되고, onActivityCreated는 제일 나중에 호출되기때문에 onCreate에서 없는데이터를 setText하려니 null objecr reference 에러가 납니다..

뭐 결론은 왜 받을 수 없었는지 이해는 해서 기분은 좋습니다. 최종 목표인 데이터를 적용시키지는 못했지만요.ㅎㅎ
그리고 생명주기를 찾는 과정에서 선생님이 조언해주신 setter 함수? set함수?를 만들어서 하라고 해주신부분도 우연치않게 찾게 되었는데 해당자료에서
프래그먼트에서 newInstance를 사용해서 Bundle로 데이터를 받아와서 셋팅하는 이유가, 프래그먼트는 디폴트생성자(빈생성자) 밖에 가질수 없고 또 안드로이드가 메모리가 부족해 액티비티를 파괴하거나 프래그먼트도 파괴할수 있는데 이 프래그먼트를 재생성할때 빈생성자를 호출하는데 이때 또 newInstance를 통해 받은 Bundle이 저장되어있다가 프래그먼트가 재생성될때 다시 넘어와서 뭐 셋팅된다! 그래서 onSaveInstanceState()를 따로 하지않아도 된다 이런식으로 설명이 되어있는데..맞는지는 모르겠지만 이렇다하더라구요.

제 수준의 실력이나 프로젝트에서는 저정도 수준까지는 사용하지 않아도 될것같지만 <fragment>태그를 사용했을때는 데이터를 넘길때 선생님이 말씀하신
set함수를 정의하고 파라미터를 넘기는 방식밖에는 없을것 같습니다.
doSomethingAfterArgumentUpdated() 이라는 함수는 잘모르겠습니다
구글링을 해봤는데 직접정의하는 함수같은데..argument를 업데이트했다는걸 알리는 함수도 있나요?

감사합니다!
사실과 좀 다른 부분이 있네요.
번들을 넘기는 이유는 안드로이 시스템의 시리얼라이제이션 특성 때문입니다. 그래서 원시타입이 아니면 Serializable이나  Parcelable을 넘기도록 되어 있는 거구요. setArgument로 데이터를 넘긴다고 프레그먼트가 프로세스종료 후 재성성될 때 자동복구되지 않아요. 개발자가 복구될 수 있도록 조치를 취해주어야만 가능해요. 간단한 테스틀 해보시면 됩니다. 앱을 실행하신 후에 홈버튼을 눌러서 앱을 백그라운드로 가게 만드신 다음 안드로이드 스튜디오 로그캣에 가셔서 프로세스 종료 버튼을 누르세요. 그 다음 디바이스나 애뮬레이터의  프로세스 관리자를 누르셔서 앱을 거기서 시작하세요. 그리고 님이 넘기셨던 데이터가 살아있는지 확인해 보세요.
<fragment>를 사용하면 프레그먼트를 동적으로 생성하는 때와는 다르게 아마 프레그먼트 자체는 완전히 소멸되지 않을 겁니다.
님이 말씀하신 포인트가 개발자들이 <fragment> 대신에 FragmentManger의  transition을 사용하는 이유예요. 안드로이드에서 라이프사이클은 정말 중요해요. 이걸 제대로 처리 안해서 자기도 모르게 크래쉬되는 앱들이 엄청나게 많아요. 님 말처럼 안드로이드는 모바일 시스템이므로 언제 프로세스가 죽을지도 모르는 리스크가 있어요. 이걸 방지하기 위해서 saveInstance 같은 거를 써서 처리를 하는 겁니다. setArgument 했다고 해서 프로세스가 종료되었다가 안드로이가 다시 생성할 때 메모리에 남아있는게 아닙니다.  개발자가 메모리에 남아있게 만들어야 합니다. 안드로이드가 뷰를 이전 상태로 복구하기위해 다시 생성하거든요. 그래서 액티비티나 프레그먼트의 onCreate에 보시면 Bundle saveInstance라는 매개변수가 존재하는 거예요. 프로세스종료 전에 필요한 정보를 저장해주고 그걸 프로세스 종료되고 재성성되고 나서 가져다 쓰기 위해서. 님이 setArgument해서 넘겼다면 이걸 onStop같은데서 saveInstance를 통해 저장하고 그걸 onCreate 같은데서 처리를 해주셔야 해요.
제 권장사항은 다시 말씀드리지만 프레그먼트가 딱 한개라면  <fragment>를 쓰셔도 될 것 같긴한데(이 경우 그냥 액티비티만 사용하는게 더 나을 수도 있겠네요), 그렇지 않으면, FragmentContainerView를 사용하시고 FragmentManger를 통해 동적으로 프레그먼트를 관리하는 방법입니다. 대부분의 개발자들이 표준처럼 사용하는 방법을 사용하시는게 문제가 생겨도 쉽게 해결할 수 있습니다.
그리고 동적으로 프레그먼트를 생성하실 때에는 액티비티의 onCreate에서 saveInstance 상태를 체크하셔야 해요.
if (saveInstace == null) {
    addMyFragment()
}
처럼, 번들이 널인 경우, 즉, 프로세스종료 되었다가 안드로이드가 뷰를 다시 복구하려고 하는 경우가 아닐 때만 프레그먼트를 생성하셔야 해요.
감사합니다 이제야 좀 알듯말듯한것같습니다!
항상 보던 saveInstance가 뭔지했는데 그런역할이 있었군요. 꽤나 중요한 포인트를 설명해주신것 같습니다. 감사함니다
그런데 제가 굳이 <fragment>를 고집한 이유가 제가 공부하고 있는 프로젝트이서
내비게이션 드로어를 사용하는데 이 액티비티의 xml에서
내비게이션 그래프를 사용하기 위해 <fragment>를 사용하기때문이었습니다.
다른 레이아웃으로 바꾸면 프래그먼트도 그냥 동적으로 생성하고 데이터도 setArgument
get Argument 했을텐데. 저 프래그먼트 태그거 내비게이션 그래프랑 관련있어서
쉽사리 건드리지 못했습니다. 건드려서 다른 레이아웃으로 바꾸게되면
내비게이션 드로어의 의미가 없어져버려서요..
물론 내비게이션그래프를 기능을유지하면서 레이아웃으로 바꾸는방법이 있는지는 모르겠으나..적어도 지금상황에선 그랬네요. 혹시 말씀하신 Fragment ContainerView를 사용하면 내비게이션 그래프의 역할도 유지하면서 프래그먼트도 동적으로ㅠ생성하며 데이터도 이제껏 질문드렸던 방법으로 전달가능할까요?

감사합니다
저도 작년 말부터 시작한 프로제트에서 네이게이션 컴포넌트를 사용해 오고 있어요. 개인적으로는 맘에 들지않는 라이브러이지만, 이미 많이 진행되어서 다른 클래스 안에 랩핑을 해서 쓰고 있어요.
네이게이션 컴포넌트라면 <fragment>를 쓸 이유가 없어요. 구글의 가이드에도 싱글 액티비티에 FragmentContainer를 네비게이션 호스트로 쓰는 구조만을 언급해요. 그리고 이렇게 쓰도록 디자인된 라이브러리이구요. 만약 네이게이션 컴포넌트를 쓰셨는데, <fragment>를 사용하고 계시다면 그건, 뭔가 이상한데요. 다시 말씀드리지만 네이게이션 컴포넌트는 메인액티비티에 NavHost 컨테이너 하나만 존재하면 됩니다. 나머지는 네이게이션 컴포넌트의 몫이예요. 님이 백스택을 신경써야할 이유가 없어요. 그리고 프레그먼트 간에 데이터를 넘기시는 것은 SafeArg라는 추가 라이브러리를 사용하시거나 navController.navigate 을 통해서 번들을 파라미터로 전달하시면 됩니다.
네이게이션 컴포넌트를 쓰시면 님이 FragmentManager를 직접 호출 할 일이 없어야 해요. 그럼, 뭔가 이상한 거예요. navController 를 통해서 원하는 곳으로 네비게이션이 이루어지도록 하셔야 해요.
네이게이션 컴포넌트의 기본적인 사용법을 찾아보세요.
https://developer.android.com/guide/navigation/navigation-getting-started

프레그먼트 생성은 님이 전혀 신경쓰실 필요 없구요. 그냥 네이게이션 그래프에 프레그먼트만 등록해 주면 되구요. 데이터 전달도 SaveArgs나 navigate 메소드를 통해 쉽게 처리됩니다.
<fragment>를 사용하는 이유는 네비게이션 컴포넌트를 처음부터 제가 만든게아니라,
안드로이드이서 제공하는 네비게이션 드로어 기본액티비티를 생성하면 그중에 navhos컨테이너로 <fragment>가 되어있었습니다ㅜ그래서 뭐가 안좋고 별로였는지 알수 없었습니다..
그럼 이 <fragment>를 FragmentContainer로 바꿀순없나요?

제가 하려고하는것이 지금..메인액티비티가 현재 이 내비게이션드로어(내비게이션 컴포넌트) 액티비티이고 첫 보이는 화면이 nav_graph중 start로지정된 화면(프래그먼트)이.. viewpager2로 구성되어있고 이건 리사이클러뷰 아이템으오 각페이지가 구현되어있습니다.
제가 할건 api에서 데이터를 받아와서 이 데이터를 프래그먼트로 보내서 각 페이지마다 데이터를 알맞게 세팅해줘야하는데요

그래서 데이터 전달이 프래그먼트 -> 프래그먼트가아니라.
메인 액티비티(내비게이션 드로어)에서 데이터를 받아서. navgraph의 시작화면인 프래그먼트로 데이터를 보내려고했습니다.
여기서도 저도 그래서 이전에 계속 질문드릴때 데이터 전달하는 방법에서
safeargs라는걸 보기는 했는데요. 이걸 이용해서 내비게이션드로어에서 데이터를 프래그먼트로보내는 방식으로 사용할수있을까요?

휴대폰으로 작성하는거라 내용이 엉망이네요 양해부탁드립니다 감사합니다
0 추천

네. 제가 잠깐 안들로이드 스튜디오에서 새 프로젝을 만들어보니 content_main.xml이 다음처럼 생성되네요.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:showIn="@layout/app_bar_main">

    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/mobile_navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>

 

여기서 중요한 항목은 android:name과 app:navGraph 입니다. 이것만 제대로 설정되어 있으시다면 다른 건 할 필요없으신 것 같아요. 네비게이션 컴포넌트에 네이게이션을 위임하시면 <fragment>도 동작을 잘 하는 걸로 보이네요. FragmentContainerView를 사용하실 경우에는 navController 설정하는 부분을 좀 수정하셔야할 것 같아요. 안그러면 앱크래쉬가 나네요.

container_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:showIn="@layout/app_bar_main">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/mobile_navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>

컴포넌트 태그를 androidx.fragment.app.FragmenetContainerView로 바꾸시고, MainActrvity.java로 가셔서, navController 설정하시는 부분을 아랫처럼 수정하세요.

NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
NavController navController = navHostFragment.getNavController();

다른 거는 하실 일이 없구요. 이제 프레그먼트 전환시에는 navController.navigate()를 호출해서 이동하세요. FragmentManager는 이용하지 마시구요.

spark (226,420 포인트) 님이 2020년 11월 4일 답변
감사합니다.
<fragment>를쓰나 fragmentcontainer를 쓰나, 데이터 전달을하고자할때는
그냥 똑같이 safeargs 라이브러리를 추가해서 navigate해주면 될까요?
네. 데이터 전달하는 방법은 동일합니다. safeargs는 쓰셔도 되고 안쓰셔도 돼요.
아 선생님 그럼 safeargs를 안쓴다고 했을때, <Fragmentcontainer>를 쓸때에도 데이터를 보낼때 <fragment>와 달리 setArgument를 써서 보낼 수 있죠?
지금 선생님이 언급해주신 방법들중 데이터를 보내는 방법이

1.<fragment>를 썼을때, 프래그먼트에 set메소드를 정의해서 데이터를 넘긴다.

2.<fragment>를 썼을때,  safeargs 라이브러리를 추가해서 navigate해서 번들을 보낸다

3.<FragmentContainer>를 썼을때, 마찬가지로 safeargs를 써서 navigate해서 번들을 보낸다.

4.<FragmentContainer>를 썼을때, setArgument해서 보낸다 이정도인가요?

4번의 경우에, 지난글의 댓글에서 xml에서 <FragmentConatiner>를 정의하고 코드상에서 add,replace등 해서 사용하라고 하셨는데
이걸 사용하려면 프래그먼트 매니저가 필요하지않나요?
그런데 내비게이션컴포넌트를 제가 쓰고있는 상황이니까 위댓글에서
이걸 쓰면 프래그먼트 매니저를 제가 직접 호출하는 일이 없어야한다고 했으니..
<FragmentContainer>를 사용하고 setArgument 해서 데이터를 보내는 방법은 별로인 방법이겠죠?
0 추천

네이게이션 컴폰넌트를 사용하시면 1, 4번은 사용하시면 안됩니다. 모든 걸 NavController에게 위임하셔야 해요. 님이 임의대로 트랜지션을 만들거나 님의 정의한 방법대로 값을 넘기는 것은 님이 알 수 없는 에러를 만드는 원인이 되요.  제 경험상, 적어도 네비게이션 컴포넌트는 그럴 소지가 아주 커요. 
그리고 사용법은 아주 간단합니다.

Bundle myBundle = getMyBundle();
navController.navigate(R.id.next_fragment, null, myBundle);

그리고 받는 쪽에서는 님이 했던 방식으로 argument에서 값을 읽어오면 됩니다.

한가지 제안을 드리자면, 님이 OOP의 추상화라는 개념을 이애하시고 좋아하신다면, 바로 네이게이션 컴포넌트를 사용하지 마시고 랩퍼 클래스로 감싸서 사용하세요. 앱에서 네이게이션 컴포넌트를 많이 사용할 수록 네이게이션 컴포넌트를 바로 사용하는 경우 네게이션이 어디로 흘러가는지도 통제가 어렵고 코드도 금방 지저분해져요.

public interface HomeFlow {
    public void start();
    public void showMovie();
    public void showMovieDetails(Movie movie);
}

public class HomeFlowImpl implements HomeFlow {
     private Navigator navigator;

     public HomeFlowImpl(Navigator navigator) {
            this.Navigator = navigator;
     }

     @Override
     public void start() {
        navigator.goTo(...);
     }
    
     @Override
     public void showMovie() {
         navigator.goTo(...);
     }

     @Override   
     public void showMovieDetails(Movie movie) {
         navigator.goTo(...);
     }
}

public interface Navigator {
    public void goTo(NavParam param) {

     }
}

public class NavigatorImpl impements Navigator {
     private NavController navController;

     public NavigatorImpl(NavController navController) {
          this.navController = navController;
     }

    @Override
    public void goTo(NavParam param) {
          navController.navigate(param.toNavOptions());
   }
}  

아이디어는 간단합니다. 그냥 인터페이스와 구현 클래스를 만들고 그걸로 NavController를 감싸서 사용하는 겁니다. 그리고 화면마다 Flow라는 클래스를 만들어서 그 화면과 관련된 네이게이션을 이 녀석이 관리하는 겁니다.

이게 그냥 네비게이션 컴폰넌트를 바로 사용하는 것보다는 몇 배는 좋은 구조라고 생각합니다. 한가지 예로, 화면 전환시 로그인 체크를 해야한다면, 간단히 NaviatorImpl 안에서 로그인이 안되었다면, 로그인 화면으로 이동하게 하면 됩니다. 그리고 최초 사용자 앱이용시 온보딩 화면들이 보여야 한다면 Flow 안에서 사용자 상태를 체크하여 OnBoardFlow로 가게 하거나 LoginFlow로 가게 만들 수 있겠죠.

spark (226,420 포인트) 님이 2020년 11월 5일 답변
감사합니다 결국 내비컴포넌트 액티비티에서 프래그먼트로 데이터를 전달하려면
프래그먼트 매니저를 건들지 말아야하고 직접 프래그먼트에 메소드를 정의해서 객체를 전달하는 방법도 추천안하신다하시니 safeArgs 라이브러리를 추가해서
navigate해서 보내는 방법밖에 없는거군요...액티비티 -> 프래그먼트로 데이터 전달 safeargs라는걸 개발자 문서 참고해서 해보겠씁니다..

또 뒤에 언급해주신방법도 제가 공부해보도록하겠습니다. 조금 어려워는 보이지만 해보겠습니다 감사합니다!
네. 그게 공식적으로 허용되는 방법입니다. 그리고 네비게이션 컴포넌트를 사용할 때는 다른 프레그먼트로부터 결과값을 받을 수 있도록 되어 있습니다. 그런 기능이 필요하실 때 찾아 보시면 좋을 듯.
...