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

프래그먼트 간의 전환시 데이터보존하기 질문좀 드립니다

0 추천

현재 이러한 내비게이션 드로어로 구성된 화면이 있습니다.

여기서 영화 포스터가 보이는 해당화면은 프래그먼트이고, 이 프래그먼트는 viewpager2로 구현되어있습니

다.  viewpager2를 구성하는 아이템들은 리사이클러뷰로 구성되어있습니다

여기서 상세보기 버튼 클릭시 

이러한 화면으로 이동됩니다. 상세보기 화면입니다

이 화면 또한 프래그먼트입니다. 즉 프래그먼트-> 프래그먼트간의 화면전환입니다.

이 화면을 스크롤해 내려보면

이러한 화면이 나옵니다. 모두보기, 작성하기 등 기능들이 있습니다. 모두보기는 

모든 리뷰댓글들을 보여주며, 작성하기 버튼은 리뷰를 작성할 수 있는 액티비입니다.

문제는 맨 첫사진인 영화목록 프래그먼트 화면에서 상세보기 화면으로 전환후에

이 햄버거메뉴를 통해 다시 영화목록으로 돌아가고 나서

다시 상세보기 버튼을 누르면 프래그먼트의 전환이 되질 않습니다.

코드입니다.

public class MovieList extends AppCompatActivity implements FragmentCallback {
    private AppBarConfiguration mAppBarConfiguration;
    MovieDetailFragment movieDetailFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.movie_list);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        DrawerLayout drawer = findViewById(R.id.drawer_layout);
        NavigationView navigationView = findViewById(R.id.nav_view);
        AppBarLayout actionBar = findViewById(R.id.appbar);

//       movieDetailFragment = new MovieDetailFragment();

        mAppBarConfiguration = new AppBarConfiguration.Builder(
                R.id.nav_movie, R.id.nav_gallery, R.id.nav_slideshow)
                .setDrawerLayout(drawer)
                .build();
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
        NavigationUI.setupWithNavController(navigationView, navController);
    }

    @Override
    public boolean onSupportNavigateUp() {
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        return NavigationUI.navigateUp(navController, mAppBarConfiguration)
                || super.onSupportNavigateUp();
    }

    // 상세보기화면으로 전환하는 화면
    public void replaceFragement() {
//        getSupportFragmentManager().beginTransaction().replace(R.id.nav_host_fragment, movieDetailFragment).commit();
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        movieDetailFragment = new MovieDetailFragment();
        ft.replace(R.id.nav_host_fragment, movieDetailFragment);
        ft.addToBackStack(null);
        ft.commit();
        Log.d("MovieList.java", "replaceFragement() 호출");
    }
}

 

보시면 onCreate() 내에 

movieDetailFragment = new MovieDetailFragment();

이 코드가 주석처리 되어있고 replaceFragment()내에도 있는데 onCreate에 있는게 원래 코드입니다

원래 코드로 진행하면 함수호출만되고 프래그먼트의 전환이 이루어지지 않습니다.

원래 코드로 했을때 화면전환이 딱한번만 이루어지고 다시 돌아와서는 왜 안되는지 잘모르겠습니다.

이유좀 알려주세요...ㅜㅜ

그래서 지금 코드처럼 replaceFragment() 옮겼더니 정상적적으로 잘 작동은되나.

함수를 호출할때마다 프래그먼트를 새로 생성해내니 기존에 프래그먼트 전환을해서

썼던 리뷰등이 모두 새로 초기화가 되어버립니다..

어떻게하면 화면전환도 계속되고 전환시에 썼던 리뷰등의 데이터를 유지할 수 있을까요?

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

2개의 답변

0 추천
엄청 궁금한게 있는데 왜 네이게이션 컴포넌트를 사용하시면서 MovieDetailsFragment를 보여줄 때는 네이게이션 컴포넌트를 이용하지 않으시나요? 이렇게 네이게이션컴포넌트를 사용하시면서 수동으로  fragmentmanger를 호출하시면, 네이게이션컴포넌트가 이미 백스택을 자체 관리하기 때문에 님이 다시 백스택을 컨트롤 하시려고 하면, 예상치 않은 문제가 발생할 확률이 높아요. 네이게이션컴포넌트에 의존하시거나 아니면, 네이게이션컴포넌트를 버리고 fragmentmanager를 사용하시거나 하세요.

참고로 네이게이션컴포넌트를 통해 데이터를 공유하는 방법은

1. SafeArgs
2. Sharing ViewModel within the same navigation graph
3. Bundle with navigation function
4. Shared memory or storage
등을 사용하시면 됩니다.
spark (224,800 포인트) 님이 2020년 10월 20일 답변
spark님이 2020년 10월 20일 수정
네비게이션 컴포넌트라는게 정확하게 뭘 말씀하시는건가요?
네비게이션 그래프 말씀하시는건지요..?
0 추천

네. Jetpack NavigationComponent요. 
https://developer.android.com/guide/navigation

소스에 나오는 R.id.host_fragment 는 네이게이션 컴포넌트가 사용하는 Navigation Host라고 하는데, 이건 님이 마음대로 건드리시면 곤란해요.이 컴포넌트를 사용하시게 될 경우에는, 해당 라이브러리에 대한 거의 완벽한 이해없이 백스택을 건드리시거나 커스터마이징하시게 되면, 예기치 않은 문제가 쉽게 생겨요. 제가 프로젝트에 사용 중이라 이런 저런 문제를 발견하고 있거든요. 프로젝트가 작은 사이즈가 아니라면 절대 사용을 권장하고 싶지는 않지만, 일단 사용 중이라면 NavigationComponent에 해당 기능을 위임하는 것이 안전해요.
그리고 반드시 MainThread 안에서만 네이게이션컴포넌트를 호출하시구요, 기본적으로 다른 프레그먼트로 이동하게 되면 앞에 있던 프레그먼트의 뷰가 Destory되기 때문에, 다른 화면에 갔다 왔을 때, 뷰가 제대로 복구되는지 확인해 보셔야 해요. 여기와 관련해서 버그가 있는게 확실해 보이거든요.
님의 이슈를 제대로 확인하실려면, 네이게이션 컴포넌트에 브레포인트를 걸어놓으시고 백스택이 어떻게 변경되는지 추적해보시는게 일단 생각해볼 수 있는 방법인 거 같습니다. 그리고 님의 코드를 네비게이션을 이용해 변경하면,

// 상세보기화면으로 전환하는 화면
    public void replaceFragement() {
//        getSupportFragmentManager().beginTransaction().replace(R.id.nav_host_fragment, movieDetailFragment).commit();
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        movieDetailFragment = new MovieDetailFragment();
        ft.replace(R.id.nav_host_fragment, movieDetailFragment);
        ft.addToBackStack(null);
        ft.commit();
        Log.d("MovieList.java", "replaceFragement() 호출");
    }

private void goToMovieDetails() {
    navController.navigate(MovieDetails 네비게이션 ID)
}

 추가로, 네이게이션 코드는 뷰에서 바로 NavController를 호출하기 보다는 한번 감싸서 네이게이션 전용 클래스에 위임을 해주시는 것이 코드를 중앙화할 수 있기 때문에, 추상화, 인캡슐레이션, 코드 중복 줄이기 등 객체지향 프로그래밍의 원칙에 좀 더 가깝게 코딩할 수 있습니다. 기본적인 아이디어는 아래와 같고, Dependency Inject을 사용하면, 코드가 좀 더 유연해 질 수 있을 겁니다.

Interface Navigator {
      fun navigate(param: NavParam)
}

class AppNavigator(private val navController: NavController) : Navigator {
      
     override fun navigate(param: NavParam) {
         val destination = param.toDestination()
         navController.navigate(destination)
     }
}

data class NavParam (
    val navId: Int,
    val navOption: NavOption,
    val args: Any
)

interface MovieFlow {
    fun showDetails()
}

class MovieFlowImpl(
    private val navigator: Navigator
)l: MovieFlow {
    override fun showDetails() {
        val navParam = NavParam(...)
        navigator.navigate(navParam)
    }
}

class MainActivity: AppCompatActivity {

   private val movieFlow = MovieFlowImpl(NavigatorImpl(navController))

   fun goToMovieDetails() {  
        movieFlow.showDetails()
   }

}

 ​

spark (224,800 포인트) 님이 2020년 10월 21일 답변
...