탭(Tab) : 몇 개의 버튼을 두고, 그 중 하나의 버튼을 눌러 서브 화면을 전환하는 방식.
내비게이션(Navigation) 위젯이라고 불리기도 한다.
상단 탭 : 액션바에 탭 기능을 넣어 보여주는 방식으로 제공됨
하단 탭 : 별도의 위젯으로 제공됨(BottomNavigationView) (사실 이걸로 상단탭을 만들어도 됨. 탭의 위치를 옮길 수 있기 때문)
<상단 탭 만들기>
1. activity_main을 아래와 같은 형태로 만들어준다.
<activity_main.xml>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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=".MainActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#A71E1E"
android:elevation="1dp"
android:theme="@style/ThemeOverlay.AppCompat.Dark">
<TextView
android:id="@+id/titleText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="타이틀"
android:textAppearance="@style/Base.TextAppearance.Widget.AppCompat.Toolbar.Title"
android:textSize="24sp"
android:textStyle="bold"
android:typeface="serif" />
</androidx.appcompat.widget.Toolbar>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/background_light"
android:elevation="1dp"
app:tabGravity="fill"
app:tabMode="fixed"
app:tabSelectedTextColor="?colorAccent"
app:tabTextColor="@color/black"/> //////////////////// 문제 발생 1
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
</FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</RelativeLayout>
CoordinatorLayout : 이 안에 여러 레이아웃을 함께 넣으면 알아서 레이아웃들의 간격이나 위치가 결정된다. 전체 화면의 위치를 잡아줌.
AppBarLayout : 액션바. 이 코드에선 Toolbar(타이틀이 나오는 가장 상단의 바), TabLayout(탭)을 추가함.
Toolbar : 텍스트뷰로 제목("타이틀") 표시. tabMode=fixed, tabGravity=fill로 설정하여 [탭] 버튼들이 동일한 크기를 갖게 함.
FrameLayout : 프래그먼트가 들어갈 곳.
문제 발생 1. 탭에서 선택한 버튼만 글자가 보임.
원인 : 책에서 나온 코드 그대로 작성했는데, 테마는 책이랑 다른 걸 사용함.
테마별로 ?colorAccent나 ?colorPrimaryDark 등 색이 다른가봄. 암튼 책에서 선택한 ?colorPrimaryDark 컬러가 이 테마에서는 하얀색이었던 것이다.. (위 사진의 탭을 자세히 살펴보면 글자가 하얀색으로 희미하게 보이는 것을 알 수 있다.)
해결 : 검정색으로 지정해줌.
탭 글자가 제대로 나오지 않는 에러인 줄 알았는데... 어이가 없군
문제 발생 2. 프래그먼트가 출력되지 않음
기본화면에서부터 프래그먼트가 나타나지 않았다.
원인 : AppbarLayout에서 layout_height = wrap_content가 아닌 match_parent로 작성했다.
해결 : wrap_content로 수정했다.
청사진을 보면 frameLayout(container)이 잘 배치된 것을 볼 수 있다.
xml 파일에서 문제가 발생한 경우엔 코드뿐만 아니라 청사진도 한번 살펴보자.
2. 프래그먼트 만들기 (눌러진 탭에 따라 변경되는 프래그먼트)
<fragment_1.xml>
<Fragment1.java>
package org.techtown.tab;
public class Fragment1 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_1, container, false);
}
}
프래그먼트의 형태는 매우 단순하다. 복사-붙여넣기로 Fragment2, Fragment3의 xml과 java 파일을 만들자.
프래그먼트의 xml에서 각각의 배경색만 바꾸어준다.(구분 가능하도록)
3. <MainActivity.java>
package org.techtown.tab;
public class MainActivity extends AppCompatActivity {
Toolbar toolbar;
Fragment1 fragment1;
Fragment2 fragment2;
Fragment3 fragment3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayShowTitleEnabled(false);
// setDisplayShow Custom Enabled 메서드로 잘못 사용했음..
// 위 메서드를 사용하면 프로젝트명이 보이지 않는다.
fragment1 = new Fragment1();
fragment2 = new Fragment2();
fragment3 = new Fragment3();
getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment1).commit();
TabLayout tabs = findViewById(R.id.tabs);
tabs.addTab(tabs.newTab().setText("통화 기록"));
tabs.addTab(tabs.newTab().setText("스팸 기록"));
tabs.addTab(tabs.newTab().setText("연락처"));
tabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
int position = tab.getPosition();
Log.d("MainActibity", "선택된 탭 : " + position);
Fragment selected = null;
if(position==0){
selected = fragment1;
}
else if(position==1){
selected = fragment2;
}
else if(position==2){
selected = fragment3;
}
getSupportFragmentManager().beginTransaction().replace(R.id.container, selected).commit();
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
}
3 - (1) Toolbar 객체를 액션바로 설정하기 (setSupportActionBar)
Toolbar toolbar;
처음에 Toolbar 객체를 참조할 때 빨간색 표시가 뜬다. Toolbar 클래스가 여러 개이기 때문에 import를 자동으로 진행할 수 없어서 발생하는 문제이다.
아래와 같이 Alt+Enter를 눌러서 androidx로 시작되는 패키지 안의 Toolbar를 import 해주도록 한다.
toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
setSupportActionBar는 액션바가 없을 경우에만 동작한다. 즉, 액션바가 없는 테마를 선택해야한다.
res/values/themes 안의 themes.xml에서 테마를 대충 뒤에 NoActionBar가 포함된 걸로 설정해준다.
3 - (2) 액션바에 프로젝트명 안 보이게 하기
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayShowTitleEnabled(false);
setDisplayShowTitleEnabled()에 false를 넣어주면 액션바의 Title이 보이지 않는다.
(실수한 것 : 여기서 대충 자동입력해주는 거로 치다가.. 나중에 보니 Title이 아닌 Custom을 가려주는 메서드를 사용하고 있었다는 것을 깨달았다.)
3 - (3) 기본 화면 설정
getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment1).commit();
fragment1이 기본 화면으로 나타나게 설정한다.
3 - (4) 탭 만들기 (tabs.addTab)
TabLayout tabs = findViewById(R.id.tabs);
tabs.addTab(tabs.newTab().setText("통화 기록"));
tabs.addTab(tabs.newTab().setText("스팸 기록"));
tabs.addTab(tabs.newTab().setText("연락처"));
3 - (5) 선택된 탭에 따라 보여지는 프래그먼트 바꾸기
탭 리스너를 설정.
- onTabSelected : tab의 상태가 선택 상태로 변경.
- onTabUnselected : tab의 상태가 선택되지 않음으로 변경.
- onTabResected : 이미 선택된 상태의 tab이 사용자에 의해 다시 선택됨.
이 중 onTabSelected에 코드를 작성한다.
tabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
int position = tab.getPosition();
Log.d("MainActibity", "선택된 탭 : " + position);
Fragment selected = null;
if(position==0)
selected = fragment1;
else if(position==1)
selected = fragment2;
else if(position==2)
selected = fragment3;
getSupportFragmentManager().beginTransaction().replace(R.id.container, selected).commit();
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {}
@Override
public void onTabReselected(TabLayout.Tab tab) {}
});
getPosition으로 어느 탭이 눌렸는지 그 값을 가져온다. (왼쪽 탭부터 순서대로 0, 1, 2, ...)
4. 실행 결과
누른 탭에 따라 아래쪽 화면(프래그먼트)이 변경되는 것을 확인할 수 있었다.
<하단 탭 만들기>
1. 새 프로젝트를 만든 뒤, res 안에 menu 폴더를 만든다.
menu 폴더 안에 menu_bottom이라는 xml 파일을 추가한다.
<menu_bottom.xml>
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/tab1"
app:showAsAction="ifRoom"
android:enabled="true"
android:icon="@android:drawable/ic_dialog_email"
android:title="이메일"/>
<item
android:id="@+id/tab2"
app:showAsAction="ifRoom"
android:enabled="true"
android:icon="@android:drawable/ic_dialog_info"
android:title="정보"/>
<item
android:id="@+id/tab3"
app:showAsAction="ifRoom"
android:enabled="true"
android:icon="@android:drawable/ic_dialog_map"
android:title="위치"/>
</menu>
xmlns:app을 추가한다.
<item> 태그 3개를 추가하여 탭 3개의 id, title, icon, showAsAction 등을 설정해준다.
2. activity_main을 작성한다.
대충 아래와 같이 디자인뷰로 만든 뒤 코드뷰에서 수정하면 편하다.
<activity_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:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#A71E1E"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:itemIconTint="@color/black"
app:itemTextColor="@color/white"
app:menu="@menu/menu_bottom" />
</androidx.constraintlayout.widget.ConstraintLayout>
app:layout_behavior가 궁금하긴 한데 검색해보니 좀 내용이 깊은 것 같다. 나중에 알아보자
bottom_navigation
- itemIconTint : 아이콘 색상
- itemTextColor : 텍스트 색상
3. 상단 탭 만들기 프로젝트에서 만든 fragment1,2,3 파일을 복사하여 하단 탭 만들기 프로젝트에 붙여넣기한다.
4. <MainActivity.java>
package org.techtown.chapter5_4_2;
public class MainActivity extends AppCompatActivity {
Fragment1 fragment1;
Fragment2 fragment2;
Fragment3 fragment3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragment1 = new Fragment1();
fragment2 = new Fragment2();
fragment3 = new Fragment3();
getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment1).commit();
BottomNavigationView bottomNavigation = findViewById(R.id.bottom_navigation);
bottomNavigation.setOnNavigationItemSelectedListener(
new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
int itemId = item.getItemId();
if (itemId == R.id.tab1) {
getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment1).commit();
Toast.makeText(getApplicationContext(), "첫번째 탭 선택 됨", Toast.LENGTH_LONG).show();
return true;
} else if (itemId == R.id.tab2) {
getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment2).commit();
Toast.makeText(getApplicationContext(), "두번째 탭 선택 됨", Toast.LENGTH_LONG).show();
return true;
} else if (itemId == R.id.tab3) {
getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment3).commit();
Toast.makeText(getApplicationContext(), "세번째 탭 선택 됨", Toast.LENGTH_LONG).show();
return true;
}
return false;
}
});
}
}
주의 1. onNavigationItemSelected이다. 자동입력 시 Reselected로 될 수 있으니.. 주의하기
주의 2. switch case 문에서 Constraint expression required 에러가 발생한다.
(Android Gradle 플러그인 8.0.0부터 최적화된 빌드 속도를 위해 리소스(ex. R.id)가 더 이상 선언되지 않기에 발생하는 오류라고 한다.)
해결하는 방법이 여러개가 있는데, 나는 if문으로 바꾸는 방식을 택했다.
switch문에 커서를 올린 뒤 Alt+Enter로 switch->if로 간단하게 바꿀 수 있다.
5. 실행 결과
굳
리스너만 나오면 코드가 너무 길어지니까 일단 복잡해보인다..
'TIL > 안드로이드 스튜디오' 카테고리의 다른 글
5장 바로가기 메뉴(NavigationDrawer) 만들기 (0) | 2024.12.23 |
---|---|
5장 - 뷰페이저 만들기 (ViewPager2) (0) | 2024.12.22 |
5장 - 액션바 사용하기 (0) | 2024.12.19 |
5장 - 프래그먼트 수명주기 & 분할화면 만들기 (0) | 2024.12.16 |
5장 - 프래그먼트 기초1 : 2개의 화면전환 (0) | 2024.12.16 |