본문 바로가기

Develop/android

[android / kotlin] bottom navigation bar를 만들어보자

안드로이드 앱 프로젝트를 하다보면, 굉장히 많이 쓰게 되는 bottom navigation bar.

개인적으로 안드로이드 프로젝트를 5번 정도 해봤는데, 그때마다 bottom navigation bar를 안 써본적이 없다.

그래서 하나하나 안드로이드 UI를 건드려볼 겸, 기본적으로 필요한 bottom navigation bar에 대해 정리하려고 한다.

 

bottom navigation bar를 만드는 방법은 조금씩 다른 방법으로 만들기도 하는데,

나는 첫 나의 안드로이드 스승님인 파트장오빠가 가르쳐준 방법인 Tab layout과 View pager를 이용하여

만들어보겠다.

 

 

 

일단, 내가 만들고자 하는 모양은 인스타그램과 같은 모양이다.

인스타그램을 보면 하단탭바에 5개의 버튼이 있고,

이 버튼을 누르면 페이지가 슝슝슝- 하고 변화되는 것을 볼 수 있다.

 

크게 보면 아래와 같은 모양이다.

 

 

그럼 인스타그램을 만들기 위해

먼저 라이브러리를 추가하자!

라이브러리를 추가하는 방법은 2가지 방법이다.

 

(1) Project Structure 이용하기

 

먼저 상단 메뉴에 File > Project Structure > Dependencies 를 클릭해준다.

 

 

그 이후 all dependencies에 있는 +를 눌러준 후, Linbrary Dependency를 눌러준다.

 

 

 

그 후 design을 검색하여, com.android.support.design으로 되어 있는 라이브러리를 클릭한 후, 확인을 한다.

 

그렇다면, Tab layout을 위한 준비과정은 끝!

 

(2) 직접 gradle에 입력해주기

 

 

 

직접 build.gragle(Module:app)을 통해 라이브러리를 넣어주는 방법이 있다.

 

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.core:core-ktx:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
    implementation 'com.android.support:design:28.0.0'
}

 

위에 보이는 것처럼, dependencies  부분에

implementation 'com.android.support:design:28.0.0'를 입력해주면 된다.

이 때 주의할 점은 모든 Support 라이브러리의 버전은 같아야 한다는 것이다. 나는 compileSdkVersion 29, targetSdkVersion 29이기

때문에 버전을 28.0.0를 하지만, 버전마다 이 부분은 달라지므로 각자 버전에 맞게 알아서 잘 조정해야한다.

혹시 compileSdkVersion 29, targetSdkVersion 29인데 implementation 'com.android.support:design:28.0.0'이 만약에 안된다면,

마이그래이션을 통해 implementation 'com.google.android.material:material:1.0.0'으로 바꿔주면 된다!

 

 

 

준비는 끝났다! Tab Layout와 Viewpager 만들어보자!

 

(1)  activity_main.xml 

모든 준비를 끝냈으면 본격적으로 Tab layout을 만들어보자.

먼저 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/vp_ac_main_frag_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/tl_ac_main_bottom_menu">
    </androidx.viewpager.widget.ViewPager>



    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tl_ac_main_bottom_menu"
        android:background="#FFFFFF"
        android:elevation="5dp"
        app:tabIndicatorColor="#40D39F"
        android:layout_width="match_parent"
        android:layout_height="52dp"
        android:layout_alignParentBottom="true">
    </com.google.android.material.tabs.TabLayout>


</RelativeLayout>

tablayout을 맨 밑에 고정을 시켜주고 그 위에 viewpager를 올려주었다.

나같은 경우는 relativelayout을 사용했지만, linearlayout이나 constraintlayout 등 본인 편할대로 사용하면 될 듯하다.

 

 

 

 

 

(2) bottom_navigation_tab.xml 

실질적으로 bottom navigation bar의 뷰가 될 xml을 만들어주자.

이 xml을 통해 텍스트도 넣을 수 있고, 아이콘도 넣을 수 있고 본인이 원하는 하단탭바 모양을 설정해줄 수 있다.

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="56dp"
    android:background="#ffffff"
    android:elevation="5dp"
    android:orientation="horizontal">

    <RelativeLayout
        android:id="@+id/btn_bottom_navi_home_tab"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1">
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:src="@drawable/selector_bottom_navi_home_icon" />
    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/btn_bottom_navi_search_tab"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1">
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:src="@drawable/selector_bottom_navi_search_icon" />
    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/btn_bottom_navi_add_tab"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1">
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:src="@drawable/selector_bottom_navi_add_icon" />
    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/btn_bottom_navi_like_tab"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1">
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:src="@drawable/selector_bottom_navi_like_icon" />
    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/btn_bottom_navi_my_page_tab"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1">
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:src="@drawable/selector_bottom_navi_my_page_icon" />
    </RelativeLayout>

</LinearLayout>

보면 이미지뷰를 통해 탭 아이콘을 넣어주었는데, src에 본인이 원하는 에셋을 연결해주면 된다.

(이미지나 drawable 등이 필요 없으면 텍스트뷰로 그냥 간단하게 구현해도 된다)

나는 클릭할때랑 클릭안했을 때 아이콘 모양을 바꾸게 하기 위해

drawable에 selector.xml를 만들어서 imageview에 연결해주었다.

혹시, 정적인 아이콘이나 텍스트가 아닌, 누를때 바뀌는 모양을 하고 싶은 분들은 

아래처럼 따라하시면 된다!

 

 

res 하위에 있는 drawable 폴더에 xml 파일을 추가해준 후, 원하는 이름으로 설정해서 추가를 해준다.

그리고 추가한 xml파일마다 아래와 같은 코드형태를 적어주면 된다!

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">


    <item
        android:drawable="@drawable/tabbar_search_unclick"
        android:state_selected="false"/>
    <item
        android:state_selected="true"
        android:drawable="@drawable/tabbar_search_click" />

</selector>

 

코드를 보면 대략 느낌적으로 알 수 있을 것 같은데, state_selected가 false일때는 클릭하지 않았을 때의 아이콘을.

state_selected가 true일때는 클릭했을 당시에 보여질 아이콘을 연결시켜주면 된다.

해당 작업을 탭의 갯수만큼 반복해주면 된다.

 

그러면 xml 파일이 해당 이미지처럼 보여질것이다

 

 

(3) 5개의 fragment

 

인스타그램을 보면 다섯개의 탭 아이콘이 있고, 눌렀을 때 바뀌는 다섯개의 페이지들이 있다.

이를 위해 우리가 activity_main.xml에 viewpager를 만들어 놓았는데,

이 viewpager에서 실제로 변화시켜줄 다섯개의 페이지들을 만들어줄 것이다!

 

 

 

일단 아래 사진과 같이 new > fragment> blank 를 클릭해준다.

 

 

 

 

그러면 아래 사진처럼 창이 나오는데,

 

이 때 원하는 fragment 이름을 지정해주고,

create layout XML을 체크한다. 이러면 해당 프래그먼트에 연결되는 xml이 자동 생성된다.

(그냥 일일히  만들고 싶다면, 그냥 코틀린 파일이랑 xml 파일이랑 따로따로 만들어서 연결해주면 된다.)

이때 Include 뭐시기하는 저 두 체크박스는 풀어주는게 좋다.

안 풀어주면 자동으로 매소드랑 콜백함수들이 타타탁- 붙어서 나오는데, 나중에 코드칠때 많이 헷갈리게 된다.

따라서 자동 생성이 아니라,  메소드랑 callback 함수들은 필요한걸 직접 입력해줄것이다.

 

그렇게 finish를 누르면 xml이랑 fragment 코틀린 파일이랑 만들어지는 것을 볼 수 있다!

인스타그램에서는 다섯개의 탭 버튼에 연결할 다섯개의 fragment가 필요하니까, 다섯개를 만들어줄 것이다!

 

fragment.kt  파일 예시
xml 파일 예시

이런식으로 다섯개를 만들었으면 프레그먼트도 준비 끝!

 

 

 

(4) 그렇다면 adapter를 만들어보자! (MainFragmentStatePagerAdapter.kt)

 

먼저 adapter가 무엇인지 설명을 드리자면,
보여지는 View와 그 View에 올릴 Data를 연결시켜주는 일종의 오작교와 같은 역할을 해준다.

adapter가 없으면 view와 data는 이루어질 수 없는 사랑이다!

이 adapter는 listview,recyclerview 등등 여러 곳에서 많이 만나게 될 것이다.

 

여튼, 우리는 그들을 연결시켜주기 위해 adapter를 만들어줄것이다.

아래 사진처럼 java 하위에 new > Kotiln File/Class를 선택하여  MainFragmentStatePagerAdapter.kt 을 만들어주자!

이름은 본인이 보기 편한대로 지어도 된다. 나는 메인액티비티에서 쓰는 프레그먼트를 연결시켜줄 어댑터라고 해서 저런식으로 지었다.

그리고 fragment랑 adapter를 구분시켜주려고 패키징을 해놨는데, 귀찮으면 안해도 된다.

 

 

그러면 이렇게 MainFragmentStatePagerAdapter.kt 파일이 만들어져있다!

 

그럼 MainFragmentStatePagerAdapter.kt을 건드려보자!

class MainFragmentStatePagerAdapter(fm : FragmentManager, val fragmentCount : Int) : FragmentStatePagerAdapter(fm) {
    override fun getItem(position: Int): Fragment? {
        when(position){
            0 -> return HomeFragment()
            1 -> return SearchFragment()
            2 -> return AddFragment()
            3 -> return LikeFragment()
            4 -> return MyPageFragment()
            else -> return null
        }
    }

    override fun getCount(): Int = fragmentCount // 자바에서는 { return fragmentCount }

}

MainFragmentStatePagerAdapter.kt에 해당 코드를 입력해주자!

이 때, 빨간 줄이 타다다닥 - 많이 나올텐데, 당황하지말고 import를 열심히 해주면

import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
import com.example.bottomnav.Fragment.*

이런식으로 import되어있는게 보이고, 빨간줄은 다 사라져있을 것이다.

이렇게 되면 adapter 준비는 끝났다!

 

 

 

(5)그러면  adapter와 앞에서 만든 view들을 MainActivity에서 조립해보자.

 

MainActivity.kt파일을 보면 

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

 

이런식으로 기본적인 코드만 있는 것을 볼 수 있다. 우리는 oncreate 밖에다가 view와 adapter를 조립시켜줄 함수 하나를

만들어줄 것이다.

 

    private fun configureBottomNavigation(){
        vp_ac_main_frag_pager.adapter = MainFragmentStatePagerAdapter(supportFragmentManager, 5)

        tl_ac_main_bottom_menu.setupWithViewPager(vp_ac_main_frag_pager)

        val bottomNaviLayout: View = this.layoutInflater.inflate(R.layout.bottom_navigation_tab, null, false)

        tl_ac_main_bottom_menu.getTabAt(0)!!.customView = bottomNaviLayout.findViewById(R.id.btn_bottom_navi_home_tab) as RelativeLayout
        tl_ac_main_bottom_menu.getTabAt(1)!!.customView = bottomNaviLayout.findViewById(R.id.btn_bottom_navi_search_tab) as RelativeLayout
        tl_ac_main_bottom_menu.getTabAt(2)!!.customView = bottomNaviLayout.findViewById(R.id.btn_bottom_navi_add_tab) as RelativeLayout
        tl_ac_main_bottom_menu.getTabAt(3)!!.customView = bottomNaviLayout.findViewById(R.id.btn_bottom_navi_like_tab) as RelativeLayout
        tl_ac_main_bottom_menu.getTabAt(4)!!.customView = bottomNaviLayout.findViewById(R.id.btn_bottom_navi_my_page_tab) as RelativeLayout




    }

activity_main.xml을 확인하면 

viewpager의 id를 vp_acc_main_frag_pager로 주었다. 

이 viewpager와 우리가 만든 adapter와 연결 시켜주기 위해

vp_ac_main_frag_pager.adapter = MainFragmentStatePagerAdapter(supportFragmentManager, 5)를 적어주었고, 여기서 5는 

프래그먼트의 갯수이다.

 

tablayout과 viewpager를 연결시켜주기 위해 tl_ac_main_bottom_menu.setupWithViewPager(vp_ac_main_frag_pager)를 적은 후,

activity_main.xml에 있는 탭 레이아웃에 커스텀한 하단탭바 뷰를 사용하기 위해 

val bottomNaviLayout: View = this.layoutInflater.inflate(R.layout.bottom_navigation_tab, null, false)를 추가해준다.

 

그 후

tl_ac_main_bottom_menu.getTabAt(0)!!.customView = bottomNaviLayout.findViewById(R.id.btn_bottom_navi_home_tab) as RelativeLayout

와 같이 각 탭에 맞는 버튼들을 일일히 다 연결시켜준다.

해당 함수를 다 만들었으면 oncreate 안에 함수를 호출해주자!

 

   override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        configureBottomNavigation()
    }

 

이렇게 되면 Tab layout과 View pager가 다 연결되었다!

 

 

 

(6)결과물

 

 

'Develop > android' 카테고리의 다른 글

[android] 안드로이드 적응형 런처 아이콘 가이드  (0) 2021.03.04