Side Proeject - Carrot Market
중고 거래 앱 당근 마켓의 간단한 기능을 구현한 프로젝트
GitHub -> https://github.com/hwayeon351/CarrotMarket
학습 회고
오늘은 중고거래 앱인 당근마켓의 간단한 기능을 구현해보는 프로젝트를 시작하였다.
홈 화면, 채팅 기능이 있는 채팅 화면, 나의 정보를 보여주는 마이페이지 화면으로 크게 기능을 나누기 위해 Bottom Navigation Menu를 만들고, 홈 화면에 중고 거래 목록을 보여주는 RecyclerView를 구현하였다.
오늘 공부한 내용
- BottomNavigationView
BottomNavigationView는 앱에 Bottom Navigation Bar를 표현해주는 View이다.
사용자는 Bottom navigation bar를 통해 하단 탭을 이용해서 쉽게 top-level View를 탐색하거나 전환할 수 있다.
보통 3개에서 5개 사이의 top-level View를 가질 때 Bottom navigation var를 사용하면 좋다.
HideBottomViewOnScrollBehavior를 기반으로 한 bar는 View를 스크롤을 사용해서 사라지도록 할 수 있다.
이때, View는 CoordinatorLayout 내에 놓여있어야 한다.
menu 리소스 파일을 명시해서 Bottom navigation bar의 메뉴를 쉽게 늘릴 수 있다.
각각의 메뉴 아이템에 title, icon을 설정해 줄 수 있다.
중고 거래 하단에 홈, 채팅, 마이페이지로 크게 화면을 나누기 위해 BottomNavigationView를 사용하였다.
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavigationView"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:itemIconTint="@drawable/selector_menu_color"
app:itemRippleColor="@null"
app:itemTextColor="@color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="@menu/bottom_navigation_menu" />
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/home"
android:icon="@drawable/ic_baseline_home_24"
android:title="@string/home" />
<item
android:id="@+id/chatList"
android:icon="@drawable/ic_baseline_chat_24"
android:title="@string/chatList" />
<item
android:id="@+id/myPage"
android:icon="@drawable/ic_baseline_person_pin_24"
android:title="@string/myPage" />
</menu>
BottomNavigationView에 setOnNavigationItemSeletedListener를 지정해서 네비게이션 메뉴 아이템이 선택되면 화면이 전환되는 replaceFragment() 메서드가 호출되도록 하였다.
val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottomNavigationView)
replaceFragment(homeFragment)
bottomNavigationView.setOnNavigationItemSelectedListener {
when (it.itemId) {
R.id.home -> replaceFragment(homeFragment)
R.id.chatList -> replaceFragment(chatListFragment)
R.id.myPage -> replaceFragment(myPageFragment)
}
true
}
private fun replaceFragment(fragment: Fragment){
supportFragmentManager.beginTransaction()
.apply {
replace(R.id.fragment_container, fragment)
commit()
}
}
- Fragment
- Fragment가 도입된 목적
Android가 프래그먼트를 처음 도입한 때는 Android 3.0(API 레벨 11)부터이다.
태블릿과 같은 큰 화면에서 역동적이고 유연한 UI 디자인을 지원하기 위한 목적으로 프래그먼트가 도입되었다고 한다.
예를 들어, 뉴스 앱에서 프래그먼트를 사용한다면 좌측에는 기사 목록을 보여주고(프래그먼트 1) 우측에는 선택한 기사 내용을 보여줄 수 있다.(프래그먼트 2)
두 프래그먼트는 모두 한 액티비티에서 나타나며, 각 프래그먼트에 수명 주기 콜백 메서드가 있어 각자 사용자의 입력 이벤트를 따로 처리하게 된다.
따라서 사용자는 기사를 선택하고 기사를 읽는 데에 각각 다른 액티비티를 사용하는 것이 아니라, 같은 액티비티 내에서 두 기능을 이용할 수 있다.
- Fragment란?
Fragment는 FragmentActivity 내의 어떤 동작이나 UI의 일부를 나타낸다.
즉, 프래그먼트는 모듈식 섹션이라고 생각하면 된다.
따라서 여러 개의 프래그먼트를 하나의 액티비티에 결합해서 창이 여러개인 UI를 빌드할 수 있으며, 하나의 프래그먼트를 여러 액티비티에서 재사용할 수 있다.
Fragment는 Activity처럼 자체적인 수명 주기를 가지고 있기 때문에 사용자의 입력 이벤트를 수신하고 액티비티 실행 중에 추가하고 삭제할 수 있다.
다른 액티비티에 재사용할 수 있는 '하위 액티비티'처럼 생각할 수 있다.
따라서 하나의 프래그먼트를 재사용할 수 있음을 염두하고 디자인하는 것이 중요하다.
프래그먼트의 조합을 여러 디바이스 화면 크기에 맞춰 변경할 수 있기 때문이다.
예를 들어, 태블릿의 경우에는 여러 프래그먼트를 하나의 화면(액티비티)에 띄울 수 있고, 휴대전화의 경우 하나의 프래그먼트만 하나의 액티비티에 띄우는 단일 창 UI를 제공해야할 수도 있다.
프래그먼트는 항상 액티비티 내에서 호스팅되어야 하기 때문에 해당 프래그먼트의 수명 주기는 호스트 액티비티의 수명 주기에 직접적으로 영향을 받는다.
호스팅한 액티비티가 Pause되는 경우에는 그 안의 모든 프래그먼트도 일시정지되며 액티비티가 소멸되면 모든 프래그먼트도 소멸되게 된다.
액티비티가 실행 중인 동안에는(또한 Resume되어 수명 주기 상태에 있는 경우) 각 프래그먼트를 추가하거나 제거하는 등 개별적으로 조작할 수 있다. 또한, 프래그먼트 트랜잭션을 수행할 때 액티비티가 백 스택을 추가하여 관리할 수 있다.
즉, 각 백 스택 항목은 발생한 프래그먼트 트랜잭션의 기록이 된다.
백 스택을 사용하면 사용자는 프래그먼트 트랜잭션을 거꾸로 돌리는 뒤로 이동이 가능하다.
프래그먼트를 액티비티 레이아웃에 추가하면, 해당 프래그먼트는 액티비티의 뷰 계층 내에서 ViewGroup에 들어가고 자체적인 뷰 레이아웃을 정의한다.
액티비티의 레이아웃 파일에서 <fragment> 요소로 프래그먼트를 만들거나, Kotlin 파일에서 프래그먼트를 선언해서 액티비티 레이아웃에 프래그먼트를 삽입할 수 있다.
- Fragment 수명 주기
프래그먼트의 하위 클래스를 생성해서 프래그먼트를 생성한다.
프래그먼트를 생성해서 액티비티에 추가하면 해당 프래그먼트의 수명주기가 시작된다.
- onCreate() - 프래그먼트를 생성하면 시스템에서 onCreate()를 호출한다. onCreate()를 오버라이딩할 때 프래그먼트가 일시정지되거나 중단되었다가 다시 재개되었을 때 유지하고자 하는 것을 초기화해야 한다.
- onCreateView() - 프래그먼트가 UI를 처음으로 그리는 시점에 시스템은 onCreateView()를 호출한다. View를 반환해서 프래그먼트 레이아웃의 루트 뷰를 반환해야 한다. 만약 프래그먼트가 UI를 제공하지 않는다면 null을 반환해주면 된다. LayoutInflater를 제공하므로 xml에서 정의한 레이아웃 리소스를 inflate한다. container는 상위 ViewGroup을 가리키며 이 안에 프래그먼트 레이아웃이 삽입된다. saveInstanceState는 프래그먼트가 재개되는 중일 때 프래그먼트의 이전 인스턴스에 대한 데이터를 Bundle로 제공한다.
class ExampleFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { // Inflate the layout for this fragment return inflater.inflate(R.layout.example_fragment, container, false) } }
- onPause() - 사용자가 해당 프래그먼트에서 벗어나갈 때 시스템에서 가장 먼저 호출하는 메서드이다. 보통 현재 사용자의 세션을 넘어서 지속되어야 하는 변경 사항을 커밋한다.
- Activity에 Fragment 추가하기
1. 액티비티의 레이아웃 파일 안에서 프래그먼트를 선언하는 방법
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:name="com.example.news.ArticleListFragment"
android:id="@+id/list"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment android:name="com.example.news.ArticleReaderFragment"
android:id="@+id/viewer"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
시스템은 액티비티 레이아웃을 생성할 때 레이아웃에 지정된 각 프래그먼트를 인스턴스화 한다.
그리고 각각의 프래그먼트에 onCreateView() 메서드를 호출해서 레이아웃을 검색한다.
시스템은 프래그먼트가 반환한 View를 <fragment> 요소에 곧바로 삽입한다.
2. Activity Kotlin 파일에서 프래그먼트를 ViewGroup에 추가하는 방법
액티비티 내에서 프래그먼트를 추가하고 제거하고 교체하는 트랜잭션을 수행하려면 FragmentTransaction에서 가져온 API를 사용해야 한다. 그리고 fragmentTransaction에 add() 메서드를 호출해서 추가할 프래그먼트와 프래그먼트를 삽입할 뷰를 지정하면 된다.
FragmentTransaction을 변경하고 나면 commit()을 호출해야 변경 내용이 적용된다.
val fragmentManager = supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
val fragment = ExampleFragment()
fragmentTransaction.add(R.id.fragment_container, fragment)
fragmentTransaction.commit()
- Fragment 관리하기
액티비티 내의 프래그먼트를 관리하려면 FragmentManager을 사용해야 한다.
액티비티에서 getSupportFragmentManager()를 호출해서 FragmentManager를 가져올 수 있다.
FragmentManager가 하는 일은 다음과 같다.
- 액티비티 내에 존재하는 프래그먼트를 findFragmentId()나 findFragmentByTag()로 가져온다.
- popBackStack()을 사용해서 프래그먼트를 백 스택에서 꺼낸다.
- 백 스택에 변경 내용이 있는지 알아보기 위해서 addOnBackStackChangedListener() 리스너를 등록한다.
- FragmentTransaction을 열어서 프래그먼트를 추가하거나 제거하거나 교체하는 트랜잭션을 수행할 수 있다.
- Fragment Transaction
액티비티에 커밋한 변경사항의 집합을 트랜잭션이라고 한다.
이러한 트랜잭션은 FragmentTransaction 내의 API를 사용해서 수행할 수 있다.
해당 액티비티가 관리하는 백 스택에 수행된 트랜잭션을 저장해서 사용자가 역 탐색을 할 수가 있다.
add(), remove(), replace()와 같은 FragmentTransaciton API를 사용해서 트랜잭션을 수행하고 변경 사항을 액티비티에 적용하기 위해 commit()을 호출해야 한다.
만약, 백 스택을 추가하려면 commit()을 호출하기 전에 addToBackStack()을 호출하면 된다. 백 스택에 추가되면 사용자가 Back 버튼을 눌러 이전 프래그먼트 상태로 되돌아 갈 수 있다.
commit()을 호출하기 전에 수행된 모든 변경사항이 addToBackStack()을 통해 하나의 트랜잭션으로 추가되며, Back 버튼을 누르면 모두 한꺼번에 되돌려진다.
addToBackStack()을 호출하지 않으면 해당 프래그먼트에 수행된 트랜잭션이 소멸되므로 사용자가 다시 이전으로 되돌아가 탐색할 수 없다.
val newFragment = ExampleFragment()
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.fragment_container, newFragment)
transaction.addToBackStack(null)
transaction.commit()
https://developer.android.com/guide/components/fragments?hl=ko
'Android > Side Projects' 카테고리의 다른 글
당근마켓 앱 개발하기 (0) | 2022.02.21 |
---|---|
당근마켓 앱 개발하기 (0) | 2022.02.20 |
Tinder 앱 개발하기 (0) | 2022.02.18 |
Tinder 앱 개발하기 (0) | 2022.02.17 |
Tinder 앱 개발하기 (0) | 2022.02.16 |