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

네비게이션드로어 메뉴 클릭시 상단 탭 이동하는방법

0 추천
탭 레이아웃으로 상단탭을 이용해서 프래그먼트로 화면을 구사한 상황입니다. 탭 클릭시 프래그먼트는 잘 이동하고 네비게이션드로어에서 메뉴클릭 시 프래그먼트 이동도 문제 없는상황인데 네비게이션 드로어에서 메뉴클릭시 화면은 이동되나 상단탭에 있는 탭이 첫번 째 탭에서 이동하지 않는 상황입니다. 해결방법좀 질문드립니다.
kyuriezman (200 포인트) 님이 2021년 10월 13일 질문

3개의 답변

0 추천
Tab Layout과 BottomnNavigationView, 둘 간의 싱크가 되어야 할 것 같은데. 이렇게 하려면 네비게이션을 한곳으로 몰아서 거기에서만 실제적으로 프레그먼트로의 이동이 일어나도록 해야 할 것 같습니다.

가장 먼저 떠오르는 방법은, BottomNavigationVieew.setOnItemSelectedListener에서 selectTab을 호출하고, addOnTabSelectedListener 안에서 프레그먼트를 이동시키면 어떨가 싶네요.
spark (224,800 포인트) 님이 2021년 10월 13일 답변
BottomNavigationView.setOnitemSelectedListner에서 selectTab 호출하는게 무슨말 인지 이해가 어려운데 예시 작성가능할까요?ㅠㅠ 초보라서 죄송합니다.
먼저 작성하신 코드와 안되는 부분을 보여주실 수 있나요?
0 추천

님이 작성하신 코드는 제가 모르기 때문에 제가 테스트해 본 것만 말씀 드릴게요.

TabLayout에 TabLayout.OnTabSelectedListener를 설정하시고, BottomNavigationView에는  NaviationBarView.OnItemSelectedListener 를 설정합니다.

tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                updateSelectedBottomMenu(tab.getPosition());
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {
            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {
            }
        });

bottomNavView.setOnItemSelectedListener(new NavigationBarView.OnItemSelectedListener() {
        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {
            int tabPosition = getTabPosition(item);
            tabLayout.selectTab(tabLayout.getTabAt(tabPosition))
            return true;
        }
    });

    private int  getTabPosition(MenuItem item) {
        Menu menu = bottomNavView.getMenu();
        for (int i = 0; i < menu.size(); i++) {
                if (menu.getItem(i).getItemId() == item.getItemId()) {
                    return i;
                }
         }
       
         throw new NoSuchElementException("Canot find the tab posiition.");
   }

 

이렇게 하면 BottomNavgationView를 클릭하던, Tab 을 클릭하던  updateBottomMenu가 호출될 겁니다. 이 메소드 안에서 선택된 메뉴를 업데이트 합니다.

   private void updateSelectedBottomMenu(int position) [
        Menu menu = bottomNavView.getMenu();
        for (int i = 0; i < menu.size(); i++) {
            MenuItem menuItem = menu.getItem(i);
            menuItem.setChecked(false);
        }

        menu.getItem(position).setChecked(true);
   }

아제 탭을 누르던, BottomNaivagationView를 누르던, 현재 선택한 메뉴로 지정이 될 겁니다.

spark (224,800 포인트) 님이 2021년 10월 13일 답변
spark님이 2021년 10월 13일 수정
public class MainActivity extends AppCompatActivity
            implements NavigationView.OnNavigationItemSelectedListener, FragmentCallback{

    Toolbar toolbar;

    Fragment1 fragment1;
    Fragment2 fragment2;
    Fragment3 fragment3;
    Fragment4 fragment4;
    Fragment5 fragment5;
    DrawerLayout drawer;



    @Override
    public void onBackPressed() {
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        int id = item.getItemId();

        if (id == R.id.menu1) {
            Toast.makeText(this, "첫번째 메뉴 선택됨.", Toast.LENGTH_LONG).show();
            onFragmentSelected(0, null);
        } else if (id == R.id.menu2) {
            Toast.makeText(this, "두번째 메뉴 선택됨.", Toast.LENGTH_LONG).show();
            onFragmentSelected(1, null);
        } else if (id == R.id.menu3) {
            Toast.makeText(this, "세번째 메뉴 선택됨.", Toast.LENGTH_LONG).show();
            onFragmentSelected(2, null);
        }

        drawer.closeDrawer(GravityCompat.START);

        return true;
    }




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

        toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        drawer =findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.addDrawerListener(toggle);
        toggle.syncState();

        NavigationView navigationView = findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);

        ActionBar actionBar = getSupportActionBar();
        actionBar.setDisplayShowTitleEnabled(false);

        fragment1 = new Fragment1();
        fragment2 = new Fragment2();
        fragment3 = new Fragment3();
        fragment4 = new Fragment4();
        fragment5 = new Fragment5();



        getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment1).commit(); //첫 번째 화면을 부름



        TabLayout tabs = findViewById(R.id.tabs);
        tabs.addTab(tabs.newTab().setText("Devices"));
        tabs.addTab(tabs.newTab().setText("Pods"));
        tabs.addTab(tabs.newTab().setText("Accessories"));
        tabs.addTab(tabs.newTab().setText("Deals"));
        tabs.addTab(tabs.newTab().setText("Support"));
        tabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                int position = tab.getPosition();
                Log.d("MainActivity", "선택된 탭 : " + position);

                Fragment selected = null;
                if (position == 0) {
                    selected = fragment1;
                } else if (position == 1) {
                    selected = fragment2;
                } else if (position == 2) {
                    selected = fragment3;
                } else if (position == 3) {
                    selected = fragment4;
                } else if (position == 4) {
                    selected = fragment5;
                }
                getSupportFragmentManager().beginTransaction()
                        .replace(R.id.container, selected).commit();
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {
            }


            @Override
            public void onTabReselected(TabLayout.Tab tab) {
            }
        });

    }
    @Override
    public void onFragmentSelected(int position, Bundle bundle ) {
        Fragment curFragment = null;

        if(position == 0){
            curFragment=fragment1;
            toolbar.setTitle("첫 번째 화면");

        }else if (position == 1){
            curFragment=fragment2;
            toolbar.setTitle("두 번째 화면");
        }else if (position ==2){
            curFragment=fragment3;
            toolbar.setTitle("세 번째화면");
        }
        getSupportFragmentManager().beginTransaction().replace(R.id.container, curFragment).commit();
    }


}

이게 제가 코딩한 코드인데 onFragmentSelected를 클릭할 때 tab position을 바꾸고싶은데 어떻게 해야할까요 ㅜㅜ 위에 글 이해가 어느정도 되는데 테스트해봤는데 자꾸 안되서요 부탁드립니다!
어떤 부분이 안되는지 구체적으로 말씀해 보시겠어요? 위에 알려드린 코드는 제가 테스트를 해본건데요.
spark님 죄송한데 저 테스트 한 코드 프로젝트 메일로 보내주실 수 있나요? yongkyou@naver.com으로 보내주시면 감사하겠습니다.

getTabPosition 에서 자꾸오류가 나서 코딩한거 보고 제가 다시 질문하겠습니다. 메일로 보내주시면 감사하겠습니다.
0 추천

어느 부분에서 막히시는지 정확히는 모르겠지만, 님의 코드를 아래처럼 변경해 보세요.

@Override
protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     // 다른 코드 생략 

     tabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
               @Override
               public void onTabSelected(TabLayout.Tab tab) {
                     int position = tab.getPosition();
                     navigateAt(position);
               }

               @Override
               public void onTabUnselected(TabLayout.Tab tab) {
                }

               @Override
               public void onTabReselected(TabLayout.Tab tab) {
               }
          });
     }
}

@Override
public boolean onNavigationItemSelected(MenuItem item) {
     int tabPosition = getTabPosition(item);
     tabs.selectTab(tabs.getTabAt(tabPosition));
     return true;
}

// Tab과  BottomNavigationView의 메뉴가 동일하고 같은 순서로 배열되어 있다고 가정합니다.
private int  getTabPosition(MenuItem item) {
     Menu menu = bottomNavView.getMenu();
     for (int i = 0; i < menu.size(); i++) {
          if (menu.getItem(i).getItemId() == item.getItemId()) {
               return i;
          }
     }
        
     throw new NoSuchElementException("Canot find the tab posiition.");
}

private final List<Fragment> fragments = Arrays.asList(fragment1, fragment2, fragment3, fragment4, fragment5);

private void navigateAt(position: int)  {
     Fragment selected = fragments.get(position);
     getSupportFragmentManager()
               .beginTransaction()
               .replace(R.id.container, selected)
               .commit();

      checkBottomMenuAt(position);
} 


private void checkBottomMenuAt(int position) [
     Menu menu = bottomNavView.getMenu();
     for (int i = 0; i < menu.size(); i++) {
         MenuItem menuItem = menu.getItem(i);
         menuItem.setChecked(false);
     }
 
     menu.getItem(position).setChecked(true);
}

 

그렇게 어려운 코드는 아니기 때문에 이해가 가시리라 생각합니다.

spark (224,800 포인트) 님이 2021년 10월 14일 답변
spark님이 2021년 10월 14일 수정
다른 좀 더 괜찮아 보이는 방법은 Backstack 에 리스너를 달아서 현재 어떤 프레그먼트가 보이는지 체크해서 BottomNavigationView를 업데이트하는 방법일 것 같습니다.
getSupportFragmentManager().addFragmentOnAttachListener(new FragmentOnAttachListener() {
            @Override
            public void onAttachFragment(@NonNull FragmentManager fragmentManager, @NonNull Fragment fragment) {
                  int menuPosition = fragments.indexOf(fragment);
                  checkBottomMenuAt(menuPosition);
            }
        });

위처럼 리스너를 달면, 어떤 프레그먼트가 추가되었는지 확인이 가능하네요.  다른 더 나은 방법도 있겠지만, 당장은 생각이 안나네요. 이렇게 하시면 기존 코드를 굳이 수정하지 않고  checkBottomMenuAt메소드만 추가하시면 될 것 같네요.
궁금한게 있는데 tabs 나 bottomNavigationView을 on create안에서 findViedById 를 해서 찾으면 밑에 함수에서 tabs나 bottomNavigationView를 부를 수 가없는데 어떻게 해결하나요? on create밖에서 선언하면 오류가나는데요..?
액티비티의 멤버변수로 선언해서 사용하세요.
private void navigateAt(int position)  {
        Fragment selected = fragments.get(position);
        getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.container, selected)
                .commit();

        checkBottomMenuAt(position);
    }

이 부분에서 .replace 에서 오류가 발생합니다!
그냥 오류는 너무 막연하니, 오류메세지를 자세하게 말씀해 보시겠어요?
죄송한데

private final List<Fragment> fragments = Arrays.asList(fragment1, fragment2, fragment3, fragment4, fragment5);
 
private void navigateAt(position: int)  {
     Fragment selected = fragments.get(position);
     getSupportFragmentManager()
               .beginTransaction()
               .replace(R.id.container, selected)
               .commit();
 
      checkBottomMenuAt(position);
}
이 구문을 좀 설명해주실수있나요 저는 이미 프래그먼트가 선언되있어서 오류가 나는거 같거든요!
private final List<Fragment> fragments = Arrays.asList(fragment1, fragment2, fragment3, fragment4, fragment5);
위의 리스트를 쓴 주된 이유는 아래처럼 프레그먼트들을 초기화하고
fragment1 = new Fragment1();
fragment2 = new Fragment2();
fragment3 = new Fragment3();
fragment4 = new Fragment4();
fragment5 = new Fragment5();        

메뉴가 눌린 위치에 해당하는 프레그먼트들을 찾을 때
Fragment selected = null;
                if (position == 0) {
                    selected = fragment1;
                } else if (position == 1) {
                    selected = fragment2;
                } else if (position == 2) {
                    selected = fragment3;
                } else if (position == 3) {
                    selected = fragment4;
                } else if (position == 4) {
                    selected = fragment5;
                }
위처럼, 동일한 코드가 반복이 되기 때문에 그렇게 했어요. 어차피 메뉴가 눌린 포지션으로 프레그먼트를 찾기 때문에 리스트를 사용해도 될 것으로 보입니다. 프로그래밍에서 반복된 코드는 문제의 원상이 되기 때문에 가능하면 반복적인 코드를 없애기 위한 겁니다. 위처럼 하면 변경사항이 생기게 되면 최악의 경우는 똑같은 걸 5번 해야 할 수도 있거든요. List를 사용하는 것보다 더 나은 접근방법이 있다면 당연히 그걸 사용하시면 됩니다.

그리고 에러에 대해서 구체적으로 설명을 안해주셔서 어디가 잘못되었는지는 말씀드리기가 좀 힘드네요. 제가 고친 코드가 문제가 있다면 님이 하셨던 방법대로 해보세요.
에러에 대한 정확한 정보를 알려드릴려면 어느부분을 보여줘야하는지 잘모르겠는데 혹시 어느부분을 가르쳐드려야할까요?ㅠㅠ
에러메세지나 에러로그를 복사해서 올려보세요.
...