이 강의에서는 카카오의 책 검색 API를 이용해서 책 검색 결과를 표시하는 앱을 만들 것입니다. 여기서 보여드리는 구현방법은 정답이 아니라 그저 수많은 구현 방법 중 하나일 뿐이니 얼마든지 더 나은 여러분들만의 다른 방법이 있을 수 있다는 점을 기억하시고 강의를 학습하여 주시기 바랍니다.
그럼 이번 영상에서는 앱의 초기구조를 작성하도록 하겠습니다.
우선은 BookSearchApp 이라는 이름으로 빈 프로젝트를 만들어줍니다. 우선은 Preference > Build, Execution, Deployment > Build Tools > Gradle 메뉴를 확인하고 만약 Gradle JDK 버전이 1.8로 되어있다면 11로 변경합니다. 그리고 파일을 저장할 때마다 코드문법을 자동으로 검토하고 수정해주는 Save Actions 플러그인도 설정해 줍니다.
이제 화면을 세 개 만들도록 하겠습니다. ui > view 패키지 아래에 New > Fragment > Blank Fragment를 선택해 빈 프래그먼트 3개를 만들어줍니다. 각 프래그먼트 클래스의 이름은 SearchFragment, FavoriteFragment, SettingsFragment 라고 하겠습니다. MainActivity 클래스도 view 패키지 아래로 이동시킵니다.
다음은 액티비티와 프래그먼트에 View Binding을 적용하겠습니다. 이 프로젝트에서는 기능이 많은 Data Binding은 사용할 필요가 없기 때문에 속도가 빠른 View Binding을 사용하겠습니다.
우선은 build.gradle에서 View Binding을 활성화합니다.
android {
+ buildFeatures {
+ viewBinding true
+ }
}
그리고 MainActivity에 View Binding을 설정합니다.
class MainActivity : AppCompatActivity() {
+ private val binding: ActivityMainBinding by lazy {
+ ActivityMainBinding.inflate(layoutInflater)
+ }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ setContentView(binding.root)
}
}
그리고 각 프래그먼트에도 View Binding을 설정해줍니다. 내용은 모두 동일하며 바인딩 클래스의 이름만 바꿔주면 됩니다. 붙여넣기 할 때 import가 반영되지 않을 수 있으므로 정확한 바인딩클래스가 적용되었는지 꼭 확인하시기 바랍니다. 여기서는 FavoriteFragment의 내용만 보여드리도록 하겠습니다.
class FavoriteFragment : Fragment() {
private var _binding: FragmentFavoriteBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentFavoriteBinding.inflate(inflater, container, false)
return binding.root
}
override fun onDestroyView() {
_binding = null
super.onDestroyView()
}
}
그리고 프래그먼트 구분을 위해 string.xml을 써서 프래그먼트 이름을 텍스트 뷰에 표시하도록 하겠습니다.
<resources>
<string name="app_name">BookSearchApp</string>
<string name="search">Search Books</string>
<string name="favorite">Favorite Books</string>
<string name="settings">Settings</string>
</resources>
각 프래그먼트의 레이아웃은 다음과 같이 ConstraintLayout으로 변경하여 줍니다.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.fragment.FavoriteFragment">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/favorite" />
</androidx.constraintlayout.widget.ConstraintLayout>
프래그먼트의 전환을 하기 위해 화면에 BottomNavigationView를 추가하겠습니다. 우선은 내비게이션 뷰에 표시할 search, favorite, settings 벡터 아이콘을 drawable 폴더에 추가하고 res > menu에 bottom_navigation_menu.xml을 작성합니다.
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/fragment_search"
android:title="@string/search"
android:icon="@drawable/ic_search_24" />
<item android:id="@+id/fragment_favorite"
android:title="@string/favorite"
android:icon="@drawable/ic_favorite_24" />
<item android:id="@+id/fragment_settings"
android:title="@string/settings"
android:icon="@drawable/ic_settings_24" />
</menu>
다음은 activity_main.xml에 BottomNavigationView와 프래그먼트를 표시할 FrameLayout을 추가합니다. 내비게이션 뷰의 높이는 Material Design의 가이드인 56dp로 정했습니다.
<?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=".ui.MainActivity">
<FrameLayout
android:id="@+id/frame_layout"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/bottom_navigation_view"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation_view"
android:layout_width="0dp"
android:layout_height="56dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="@menu/bottom_navigation_menu" />
</androidx.constraintlayout.widget.ConstraintLayout>
그리고 MainActivity에서 BottomNavigationView를 구성합니다. setOnItemSelectedListener를 써서 각 버튼이 선택되었을 때 해당되는 프래그먼트가 표시되도록 하면 됩니다.
private fun setupBottomNavigationView() {
binding.bottomNavigationView.setOnItemSelectedListener { item ->
when (item.itemId) {
R.id.fragment_search -> {
supportFragmentManager.beginTransaction()
.replace(R.id.frame_layout, SearchFragment())
.commit()
true
}
R.id.fragment_favorite -> {
supportFragmentManager.beginTransaction()
.replace(R.id.frame_layout, FavoriteFragment())
.commit()
true
}
R.id.fragment_settings -> {
supportFragmentManager.beginTransaction()
.replace(R.id.frame_layout, SettingsFragment())
.commit()
true
}
else -> false
}
}
}
마지막으로 앱이 처음 실행되었을 경우에만 화면에 SearchFragment를 표시하도록 합니다. Activity가 재생성되었을 경우에는 첫번째 화면을 표시할 필요가 없기 때문입니다. 앱이 처음 실행되었는지 여부는 savedInstanceState 값으로부터 판정합니다.
override fun onCreate(savedInstanceState: Bundle?) {
...
setupBottomNavigation()
if (savedInstanceState == null) {
binding.bottomNavigationView.selectedItemId = R.id.fragment_search
}
}