코딩 (독학)/★ Kotlin

[CH.2] JDTALK 메인화면 구성 (카카오톡 클론코딩) Bottom Navigation Bar 구성하기 - Android studio(with.Kotlin)

짱득이 2022. 1. 19. 17:07
300x250
반응형
 시작에 앞서
개인 프로젝트, 코딩 과정을 상세하게 기록하고 리뷰하기 위해 작성된 블로그입니다.
공부와 함께 진행되는 스파르타식 프로젝트이므로, 부족한 부분이 많을 수 있습니다.
Feedback은 언제나 환영입니다.

본 포스트는 [다예하다] 님 (https://everybody-yeah.tistory.com/6)의 포스트를
기반으로 참고하여 작성되었습니다. 굉장히 깔끔한 정리를 해 주신 [다예하다]님 감사드립니다.


[Ch.2] 메뉴 탭 만들기

Configuration ?

 

  • Project Name : JDTalk_Clone
  • Language : Kotlin (Android Studio)

먼저 초기 세팅으로 만들어야 하는 메뉴 탭은 아래처럼 3가지를 제작하는 것을 목표로 잡았다.

왼쪽부터 친구/채팅/더보기 탭 이다.

각각의 메뉴 탭을 왔다 갔다 하기 위해서 Navigation Bar를 이용할 것이다.

여기에서 또 새로운 용어가 등장을 해버린다.

 

Android Application Project를 진행하면 가장 보편적으로 사용되는 Bottom Navigation Bar이다.

주로 Tab layout과 View Pager를 이용하여 이번 포스트를 써 내려가 보려 한다.

 

먼저, Bottom Navigtaion Bar는 아래 사진을 참고하면,

카카오톡 화면의 아래쪽을 보면 친구, 채팅, 검색, 쇼핑, 더보기 등의 버튼이 있다.

 

각각의 버튼을 누를 때마다 페이지가 변경되는 것을 확인할 수 있는데, 이것이 Navigation Bar를 활용한 변화이고,

아래쪽에 위치했기 때문에 흔히 Bottom Navigation Bar라고 부른다.

 

출처 : https://everybody-yeah.tistory.com

 

 

각 화면을 논리적으로 분할한다면 왼쪽과 같은 모양이 된다.

 

 

(View Pager / Tab Layout으로 분리된 화면)

 

View Pager는 Tab에 따라 각종 프래그먼트와 리스트(친구 목록, 채팅 목록 등)들이, 

 

Tab Layout에는 메뉴 탭(친구, 채팅, 더보기 등)들이 보이게 된다.

 

조금 더 자세한 설명은 글과 함께 써내려 가겠다.

 

 

 

 

 


선행 작업_라이브러리 추가 (선택)

 

먼저, 선택이라고 한 이유는 나중에 빨간 줄이 왕창 떴을 때 라이브러리를 뭘 추가해야 할지 알게 되기 때문에 초반 선행 필수 작업이라고 할 수는 없지만... 어찌 보면 언제든 해 줘야 할 작업인 것도 맞기 때문에 그냥 하는 게 편할 것 같다.

 

 

라이브러리를 추가하는 방법은 2가지가 있는데, 편한 방법을 사용하면 될 듯하다.

 

(1) Project Structure 이용하기

 

1. 상단 메뉴에 File > Project Structure > Dependencies를 클릭

2. All Dependencies 바로 아래 있는 +를 누르고, Library Dependency를 클릭.

3. Search 란에 design을 검색하고, com.android.support:design으로 되어있는 Library를 클릭, Apply, OK 한다.

  • 분명 build.gradle (:app)에 방금 추가한 dependencies : implementation 이 Error 가 날 텐데, 아마 SDK 버전이 맞지 않아서 이런 오류가 뜨는 확률이 높다.

위 사진에서 version을 28.0.0으로 추가했고, 필자가 사용하는 SDK는 31 버전인데 왜 최신 버전에 31까지 나열되어있지 않은지 잘 모르겠다.

    implementation 'com.android.support:design:28.0.0'

위와 같은 코드를 아래와 같이 변경했다.

    implementation 'com.android.support:design:31.0.0'

그 이후, build.gradle을 수정하였으므로 우상단에 뜨는 Project와의 Sync Now를 클릭하니, 정상 작동하였다.

추후에 부디 문제가 발생하지 않길 비나이다..

 

(2) build.gradle에 직접 추가하기

출처 : https://everybody-yeah.tistory.com/6

코드를 알고 있는 고수라면 직접 build.gradle(Module:app)에 라이브러리명(+버전명)을 dependencies에 직접 넣어주는 방법도 있다. 위에서도 말했듯이, 아래와 같은 코드를 추가하였다. 물론 dependencies 안에!

dependencies {
	...
	implementation 'com.android.support:design:31.0.0'
    ...
    }

주의해야 할 점은 모든 Support 라이브러리의 버전이 같아야 하는 건 확실하므로, 알아서 잘 딱 깔끔하고 센스 있게 조정하여 맞는 버전으로 바꿔줘야 할 듯하다..;;

 

최신 버전의 Library가 맞는지 확인하기는 린트를 사용하였지만, 다양한 방법이 있다.

 


라이브러리 최신 버전 확인하기

Android Studio를 사용해 Application을 Build 하려면 Gradle을 사용하게 되고, 많고 다양한 Library를 추가하여 사용하게 된다.

 

이렇게 다양한 외부 라이브러리를 사용하게 되면 각각의 라이브러리가 최신 버전인지 확인해 줘야 하고, 버전이 업데이트되었다면 확인하여

다시 동기화하고 빌드해서 앱을 확인해야 한다. (대부분의 라이브러리 프로젝트의 소스는 Github에서 호스팅하고 있다.)

 

이 과정에서 라이브러리 업데이트는 필연으로 발생할 수 있는 문제지만, 라이브러리가 업데이트되었는지 확인하는 작업은 아주 불편하다.

때문에, Android Studio에서 앱이 사용하는 라이브러리의 최신 버전을 확인할 수 있는 방법들이 있는데, 간단히 알고 넘어가려고 한다.

(알면 좋은 거니까)

 

출처 : http://sjava.net/

 

(1) 린트(Lint) 사용

 - Android Studio에서 외부 라이브러리의 최신 버전을 쉽게 확인할 수 있도록 린트 기능을 제공

 - 사용은 가능하지만, Default Config로 되어있지는 않기 때문에 설정에서 별도로 추가해 주어야 한다.

 

(1-1). 옵션 추가 방법

  • Android Studio에서 File -> Setting -> Editor -> Inspections에서 Newer를 검색한다.
  • Newer Library Versions Available 옵션을 체크하고, OK 하면 옵션 추가 완료.

출처 : http://sjava.net/

(1-2). 린트를 실행하여 최신 버전을 확인하는 방법

  • 상단 메뉴에서 Analyze -> Run Inspection by Name..... 을 선택
  • Enter inspection name 창에서 Newer를 검색하여 Newer Library Versions Available을 선택
  • Whole Project 범위로 선택하여 확인하면 아래와 같은 결과가 나오게 된다.

출처 : http://sjava.net/

만약 모두 최신 버전의 라이브러리로 올바르게 최신화되어있다면 이상이 없으므로 린트의 결과가 없다고 나온다.

 

이 외에도 다양한 방법이 존재하는데, 아래와 같다.

방법은 많지만, 가장 편리한 방법으로 적절히 적용하여 사용하면 될 듯 하기에..

본인은 린트가 가장 간단하고 편해 보이더라..ㅎㅎ

 


 

Tab Layout & View Pager 만들기

웬만한 선행 준비는 끝났다. 본격적으로 메인 화면을 구성할 차례다.

[Ch.1] JDTALK 어플 시작화면 SPLASH 효과주기 를 구성하면서 Splash 효과 이후에 메인 화면으로 마땅히 둘 것이 없어서

아래와 같은 화면을 구성한 채 방치하였다.

 

 


 

(1) activity_main.xml 수정

* Before Sample Code *

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

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="splash 끝! 이제 메인이당"
        android:textSize="30sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

* Modified Code *

전체적인 앱 완성 결과물을 생각했을 때 Relative Layout이 적절하다고 판단하여 사용하기도 하였고, 참고한 블로그 역시나 Linear Layout이나 Constraint Layout 등 본인이 편할 대로 사용하면 된다고 한다.

맞는 말이면서, 사실 본인도 공부하는 입장이기 때문에 잘 따라가면서 때에 따라 적절히 수정할 생각이다.

<?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.viewpager2.widget.ViewPager2
        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"
        />

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

 - 레이아웃 구성을 RelativeLayout으로 변경하였다.

 - Viewpager2와 TabLayout을 사용하였고, Viewpager2에 layout_above 옵션을 통해 tablayout 요소의 위에 두었다.

 - elevation과 tabIndicatorColor를 줌으로써 뷰에 약간의 디자인을 추가한다. (이건 일단 따라 해 본 것)

 

여기에서 참고할 점은 ViewPager가 Android 공식 문서상 ViewPager2로 이전 되었다는 것이다.

라이브러리의 개선 버전이 2이며, 향상된 기능을 제공함과 동시에 ViewPager의 고질적 문제를 해결했다는 발표이다.


(2) bottom_navigation_tab.xml 생성 및 작성

 

이제 activity_main.xml에 기반하여 실질적인 bottom navigation bar의 View가 되어 줄 xml을 만들어야 한다.

본 xml을 통해 Text, Icon 등을 추가할 수 있고, 본인이 원하는 하단 탭 바의 모양을 설정해 줄 수도 있다.

 

[ layout 마우스 우 클릭 ] ▶ [ New ] ▶ [ XML ] ▶ [ Layout XML File ]

Layout File Name : bottom_navigation_tab          /          Root Tag : LinearLayout

 

그리고 아래와 같이 코드를 작성 해 주었다.

<?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="match_parent"
    >

    <RelativeLayout
        android:id="@+id/btn_bottom_navi_friends_tab"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1">
        </RelativeLayout>

    <RelativeLayout
        android:id="@+id/btn_bottom_navi_chat_tab"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1">
        </RelativeLayout>

    <RelativeLayout
        android:id="@+id/btn_bottom_navi_other_tab"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1">
        </RelativeLayout>

</LinearLayout>

이렇게 하면 각각의 <RelativeLayout /> 에 대하여 weight (가중치) 값 1을 가지게 되고, 3개의 요소 (friends_tab, chat_tab, other_tab)가 상대적인 크기의 형태로 배치하게 된다.

이제 아이콘을 넣어서 확실히 구분할 수 있도록 완성도를 높여 주어야 하는데 단순히 정적인 이미지나 텍스트는 밋밋하므로 클릭하기 전/후 로 나누어 모양이 변화하는 Image Asset src를 연결 해 주려고 한다.

 

오른쪽과 사진 같이 /drawable에 클릭하기 전/후의 Icon Image와 해당 Image를 논리적으로 클릭하기 전/후로 나눠 줄 icon.xml을 생성한다.

 

(1) 각각 친구, 채팅, 더보기에 대하여 클릭하기 전/후 이미지 총 6개

(2) 친구, 채팅, 더보기의 요소를 논리적으로 나눌 icon.xml 총 3개

 

 

 

 

위에 생성한 selector.xml은 아래와 같이 [ New ] ▶ [ Drawable Resource File ] 로 생성할 수 있다.

 

 

* selector_bottom_navi_friends_icon.xml (친구 탭)

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

    <item
        android:drawable="@drawable/friends_unclick"
        android:state_selected="true"
        />

    <item
        android:drawable="@drawable/friends_clicked"
        android:state_selected="false"
        />
</selector>

 

* selector_bottom_navi_chat_icon.xml (채팅 탭)

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

    <item
        android:drawable="@drawable/chat_unclick"
        android:state_selected="true"
        />

    <item
        android:drawable="@drawable/chat_clicked"
        android:state_selected="false"
        />
</selector>

 

* selector_bottom_navi_other_icon.xml (더보기 탭)

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

    <item
        android:drawable="@drawable/other_unclick"
        android:state_selected="true"
        />

    <item
        android:drawable="@drawable/other_clicked"
        android:state_selected="false"
        />
</selector>

 

* oooo_icon.xml에 대한 전체적인 요약

  • drawable은 말 그대로 Image Asset 을 불러와 붙이는 것.
  • android:state_selected="true | false" 는 클릭하거나 안했을 당시에 보여질 아이콘을 나눠준 것.
  • 탭의 개수만큼 icon.xml을 생성하면 된다.

이후에 다시 위 작업을 전체적으로 넣어 줄 bottom_navigtaion_tab.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="match_parent"
    >

    <RelativeLayout
        android:id="@+id/btn_bottom_navi_friends_tab"
        .....
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:src="@drawable/selector_bottom_navi_friends_icon" />
        </RelativeLayout>

    <RelativeLayout
        android:id="@+id/btn_bottom_navi_chat_tab"
        .....
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:src="@drawable/selector_bottom_navi_chat_icon" />
        </RelativeLayout>

    <RelativeLayout
        android:id="@+id/btn_bottom_navi_other_tab"
        .....
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:src="@drawable/selector_bottom_navi_other_icon" />
        </RelativeLayout>

</LinearLayout>

기존에 추가했던 각각의 <RelativeLayout />의 요소에 각각 ImageView 태그를 추가하고

Image src는 아까 만들었던 클릭하기 전/후에 대한 icon.xml을 각각의 탭에 맞게 넣어준다.

 

* 첫번째 에러 발생

원인을 찾고 따라 가 보니 아래와 같은 Warning이 있었다.

이정도면 Warning이 아니고 Error 정도로 표시 해 주는게 더 고마울 것 같은데...ㅎ..;

 

일단 해결을 위해 contentDescription이 무엇인지부터 찾아 보았다.

* contentDescription ?
ImageView, ImageButton, CheckBox와 같은 시각적으로 정보를 전달하는 보기에서 해당 속성을 사용한다.
예를 들어, 시각장애인의 경우 이미지와 같은 정보들은 제대로 받아들일 수 없는 경우가 많은데 이를 대체 할 설명을 달아주는 역할을 해 주는것이 contentDescription이다.

또는, View 내용을 파악하거나, 개선 작업을 할 때 해당 기능 덕분에 각 View를 구분하는데 큰 도움이 된다.

Google Android Support Center는 TextView를 제외한 시각적인 의미가 있는 콘텐츠에 대하여 위와 같은 이유들로 하여금 요소에 대한 의미, 설명을 제공하고 제시된 정보를 이해하는데 도움을 주는 기능인 contentDescription과 같은 라벨링을 권장하고 있다.

더 궁금하다면 더 알아봐도 좋지만, 눈이 아플것이다.

 

이제 이것을 해결 해 줘야 하는데 필자는 코린이 이기에.. 어떠한것이든 해결을 위해서는 구글링 몇번이고 해야 하는 처지인데.. 안드로이드 스튜디오가 나를 살려주었다.

 

세상의 발전은 위대하다.

앞에 보이는 Fix 버튼을 누르면 어느 위치에서 어떻게 수정해야 하는지 알려준다.

.xml에서 [Design] 탭의 속성에서 해당 ImageView의 [contentDescription]을 작성하면 모든 문제가 해결된다.

 

이제는 알았으니, 하드 코딩을 원한다면 아래와 같이 직접 작성 해도 해결된다. 결과물은 똑같다.

<RelativeLayout
        android:id="@+id/btn_bottom_navi_friends_tab"
        .....
        <ImageView
            .....
            android:contentDescription="@string/this_is_friends_icon"
            android:src="@drawable/selector_bottom_navi_friends_icon" />
        </RelativeLayout>

이와 같은 형식으로 description을 작성한다. (나머지도 각 탭에 맞는 description을 남겨주면 된다.)

(이후에, 기존의 ImageView들도 모두 Description을 작성 해 주었다. 짜잘하게 넘겼던 오류들이 모두 해결되더라;;)

 

그러고 추가한 요소들을 수평이면서 적절한 크기가 되게 하고, 약간의 보너스로 elevation 효과를 추가한다.

elevation 효과는 그림자 효과인데 사실 아직까지 큰 차이는 모르겠다. 나중에 기회가 된다면 이빠이 키워서 테스트 해 봐야겠다. 코드는 아래와 같이 추가하였다.

<?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:elevation="5dp"
    android:orientation="horizontal" >
    
    ......
    .....
    ...
    ..
    .
    
</LinearLayout>

 

이렇게 해 주면 [ bottom_navigation_tab.xml ] 의 [ Design ] 탭에서 확인한 결과가 아래와 같아 질 것이다.

 

View 띄워주는 작업도 안해놓고 왜 실행 안되냐고 징징거린거 안비밀;;

글이 너무 길어져서 다음 포스트에서 계속 하겠다.

728x90
반응형