[안드로이드] RecyclerView와 Adapter 사용시 화면 안나오는 문제

프로그래밍/Android 관련2018. 2. 11. 23:26

안녕하세요. 개발자 드리머즈입니다.


안드로이드 초보 개발자라 많은 이슈들을 접하게 되네요. 이번에 볼 이슈는 Fragment에서 RecyclerView를 사용할 때 겪을 수 있는 화면이 갱신되지 않는 이슈입니다. 좀 더 정확한 문제 상황을 보겠습니다.


기본적인 레이아웃 구성입니다. 하단에 TabLayout을 이용해서 만든 Tab A와 Tab B가 위치하고 있으며 그 위에는 FrameLayout이 있습니다. 어느 Tab 버튼을 누르느냐에 따라 FrameLayout 부분에 관련 화면이 나오게 하는 아주 흔한 패턴입니다.(ViewPager는 사용하지 않음)


처음에 이 액티비티가 실행됐을 때는 아래 사진처럼 FrameLayout에 배치된 Fragment안의 RecyclerView가 정상적으로 보였습니다.

이 상황에서 Tab B를 클릭합니다.

그러면 Tab B와 관련된 화면이 잘 나옵니다. 여기서 중요한 것은 Tab B의 화면이 무엇이냐 하는 것이 아닙니다. Tab B가 실행되면서 Tab A의 Fragment가 종료됐다는 것입니다. 여기서 다시 Tab A를 클릭합니다.



그러면 위 화면처럼 RecyclerView가 나와야 하는 부분에 아무것도 나오지 않았습니다. 이 상황에서는 아무리 Tab B를 갔다가 Tab A를 클릭해도 계속 아무것도 나오지 않았습니다. 정상적으로 나오는 경우는 딱 1번 이 전체 화면을 보여주는 액티비티가 처음 실행될 때였습니다.


무엇이 문제인지 몰라 삽질을 많이 했습니다. 스택오버플로우에서 검색을 해봐도 저와 비슷한 사례가 안보였습니다. 문제의 원인은 Fragment의 Life Cycle과 관련이 있었습니다. 

현재 레이아웃 구조를 보면 Activity가 가장 큰 개념이며 이 액티비티에 의해 Fragment가 영향을 받고 이 Fragment는 다시 RecyclerView에게 영향을 줍니다.

Tab B가 클릭되면 액티비티는 Tab A의 Fragment 대신 Tab B의 Fragment를 보여주게 됩니다. 이 때 Fragment가 Life Cycle에 따라 바뀌게 됩니다. 

아래에 Fragment의 Life Cycle에 대한 그림이 있습니다. onDestroyView()와 onDetach() 함수는 fragment가 제거될 때 불립니다.




Tab A의 Fragment에 showItemList()라는 함수를 만들었습니다. Fragment에 RecyclerView 화면을 보여주기 위해 RecyclerView와 Adapter 관련 설정을 하는 간단한 함수입니다.

1
2
3
4
public void showItemList(){
    mAdapter = new ItemAdapter(itemFullInfoMap);
    mRecyclerView.setAdapter(mAdapter);
}
cs


문제는 이 함수가 언제 불리느냐 였습니다. Tab A의 life cycle만 고려했을 때 정상적으로 동작하는 경우에는(처음 액티비티를 실행시켰을 때)


onAttach() -> onCreateView() -> onViewCreated() -> onActivityCreated() -> showItemList()


Fragment의 생성과 관련된 함수들이 모두 실행된 다음에 showItemList() 함수가 불렸습니다.

하지만 이와 달리 Tab B를 클릭한 뒤에 Tab A를 클릭했을 때는 아래와 같이


showItemList() -> onAttach() -> onCreateView() -> onViewCreated() -> onActivityCreated()


showItemList() 함수가 먼저 호출된 다음에 Fragment의 생성 함수들이 불렸습니다. 

일반적으로 생각해도 Fragment의 생성이 끝난 뒤에 showItemList()가 불리는 게 정상같아 보입니다. 


아래 코드는 문제가 발생한 Activity의 Fragment 교체와 관련된 부분의 코드입니다.


MainActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    private void setCurrentTabFragment(int tabPosition)
    {
        switch (tabPosition)
        {
            case :
                replaceFragment(fragmentItemList);
                fragmentItemList.showItemList();
                break;
            case :
                replaceFragment(fragmentChart);
                break;
            case :
                replaceFragment(fragmentUsers);
                break;
            default :
                break;
        }
    }
 
    public void replaceFragment(Fragment fragment) {
        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        ft.replace(R.id.frame_container, fragment);
        ft.commit();
    }
cs


6번째 라인에서 fragment를 변경한 다음에 7번째 라인에서 fragmentItemList.showItemList()함수를 호출했었습니다. 저는 6번째 Fragment를 교체하는 코드가 모두 끝난 후 7번째 라인의 코드가 실행될 것이라고 생각했습니다(=동기적 실행). 그런데 그렇지 않았습니다. 왜냐하면 문제가 발생했을 때 함수 실행 순서를 보면 오히려 7번째 코드가 먼저 실행된 다음에 6번째 코드가 실행됐으니까요. 비동기적으로 실행되는 것 같습니다.

그래서 showItemList() 함수와 같이 RecyclerView의 Adapter를 설정하는 함수는 Fragment 내부의 onActivityCreated()함수 안에 넣어서 원하는 시간에 호출되게 해야할 것 같습니다.


1
2
3
4
5
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    showItemList();
}
cs

onActivityCreated() 함수 안에 showItemList() 함수를 호출되게 수정하니 화면이 정상적으로 잘 나옵니다.

작성자

Posted by 드리머즈

관련 글

댓글 영역