MDC-102 Android: 머티리얼 구조 및 레이아웃 (Kotlin)

1. 소개

logo_components_color_2x_web_96dp.png

머티리얼 구성요소(MDC)를 통해 개발자는 머티리얼 디자인을 구현할 수 있습니다. Google의 엔지니어와 UX 디자이너로 구성된 팀에서 만든 MDC는 아름답고 기능적인 수십 가지의 UI 구성요소가 특징이며 Android, iOS, 웹, Flutter.material.io/develop에서 제공됩니다.

Codelab MDC-101에서는 두 가지 머티리얼 구성요소(MDC)를 사용하여 로그인 페이지(텍스트 필드와 잉크 물결 모양이 있는 버튼)를 빌드했습니다. 이제 탐색, 구조, 데이터를 추가하여 이러한 기초를 확장해보겠습니다.

빌드할 항목

이 Codelab에서는 Shrine 앱(의류와 가정용품을 판매하는 전자상거래 앱)의 홈 화면을 빌드합니다. 다음 항목이 포함됩니다.

  • 상단 앱 바
  • 제품으로 가득 찬 그리드 목록

249db074eff043f4.png

이 Codelab의 MDC-Android 구성요소

  • AppBarLayout
  • MaterialCardView

필요한 항목

  • Android 개발에 관한 기본 지식
  • Android 스튜디오(아직 다운로드하지 않은 경우 여기에서 다운로드)
  • Android Emulator 또는 기기(Android 스튜디오를 통해 사용 가능)
  • 샘플 코드(다음 단계 참고)

Android 앱 빌드 경험 수준을 평가해 주세요.

초급 중급 고급

2. 개발 환경 설정

MDC-101에서 계속 진행

MDC-101을 완료했다면 이 Codelab을 위한 코드가 준비된 것입니다. 3단계: 상단 앱 바 추가로 건너뜁니다.

처음부터 새로 시작

시작 Codelab 앱 다운로드

시작 앱은 material-components-android-codelabs-102-starter/kotlin 디렉터리에 있습니다. 시작하기 전에 해당 디렉터리로 cd해야 합니다.

...또는 GitHub에서 클론

이 Codelab을 GitHub에서 클론하려면 다음 명령어를 실행하세요.

git clone https://github.com/material-components/material-components-android-codelabs
cd material-components-android-codelabs/
git checkout 102-starter

Android 스튜디오에서 시작 코드 로드

  1. 설정 마법사가 완료되고 Welcome to Android Studio 창이 표시되면 Open an existing Android Studio project를 클릭합니다. 샘플 코드를 설치한 디렉터리로 이동한 다음 kotlin -> Shrine을 선택하거나 컴퓨터에서 shrine을 검색하여 배송 프로젝트를 엽니다.
  2. Android 스튜디오 창 하단의 활동 표시기에 나타나는 것처럼 Android 스튜디오가 프로젝트를 빌드하고 동기화할 때까지 잠시 기다립니다.
  3. 이 시점에서 Android 스튜디오에 아래와 같은 빌드 오류가 발생할 수 있습니다. Android SDK나 빌드 도구가 누락되었기 때문입니다. Android 스튜디오의 안내를 따라 이러한 항목을 설치/업데이트하고 프로젝트를 동기화합니다.

KzoYWC1S7Se7yL8igi1vXF_mbVxAdl2lg5kb7RODrsVpEng0G6U3NK1Qnn0faBBZd2u71yMXioy9tD-7fv3NXvVO4N3EtMMeWDTmqBMMl6egd9R5uXX0T_SKmahbmRor3wZZHX0ByA

프로젝트 종속 항목 추가

프로젝트에는 MDC Android 지원 라이브러리의 종속 항목이 필요합니다. 다운로드한 샘플 코드에는 이 종속 항목이 이미 나열되어 있지만, 다음 단계를 수행하여 확인하는 것이 좋습니다.

  1. app 모듈의 build.gradle 파일로 이동하여 dependencies 블록에 MDC Android의 종속 항목이 포함되어 있는지 확인합니다.
api 'com.google.android.material:material:1.1.0-alpha06'
  1. (선택사항) 필요한 경우 build.gradle 파일을 수정하여 다음 종속 항목을 추가하고 프로젝트를 동기화합니다.
dependencies {
    api 'com.google.android.material:material:1.1.0-alpha06'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    implementation 'com.android.volley:volley:1.1.1'
    implementation 'com.google.code.gson:gson:2.8.5'
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21"
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:core:1.1.0'
    androidTestImplementation 'androidx.test.ext:junit:1.1.0'
    androidTestImplementation 'androidx.test:runner:1.2.0-alpha05'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0-alpha05'
}

시작 앱 실행

  1. Run/Play 버튼 왼쪽에 있는 빌드 구성이 app인지 확인합니다.
  2. 녹색 실행/재생 버튼을 눌러 앱을 빌드하고 실행합니다.
  3. 사용 가능한 기기에 Android 기기가 이미 나열되어 있으면 Select Deployment Target 창에서 8단계로 건너뜁니다. 나열되어 있지 않으면 Create New Virtual Device를 클릭합니다.
  4. Select Hardware 화면에서 Pixel 2와 같은 휴대전화 기기를 선택한 후 Next를 클릭합니다.
  5. System Image 화면에서 최신 Android 버전(가장 높은 API 수준 권장)을 선택합니다. 설치되어 있지 않은 경우 표시되는 다운로드 링크를 클릭하고 다운로드를 완료합니다.
  6. Next를 클릭합니다.
  7. Android Virtual Device(AVD) 화면에서 설정을 그대로 두고 Finish를 클릭합니다.
  8. 배포 대상 대화상자에서 Android 기기를 선택합니다.
  9. 확인을 클릭합니다.
  10. Android 스튜디오가 앱을 빌드하고 배포한 후 자동으로 대상 기기에서 앱을 엽니다.

완료되었습니다. MDC-101 Codelab의 Shrine 로그인 페이지가 표시됩니다.

4cb0c218948144b4.png

이제 로그인 화면이 제대로 표시되므로 앱을 제품으로 채워보겠습니다.

3. 상단 앱 바 추가

로그인 페이지가 닫히면 '축하합니다'라는 화면과 함께 홈 화면이 표시됩니다. 훌륭합니다. 그러나 이제 사용자는 실행할 작업이 없거나 앱 내에서 사용자의 위치를 파악할 수 없습니다. 이러한 문제를 해결하기 위해 탐색을 추가해보겠습니다.

머티리얼 디자인은 높은 수준의 사용성을 보장하는 탐색 패턴을 제공합니다. 가장 눈에 띄는 구성요소 중 하나는 상단 앱 바입니다.

탐색 기능을 제공하고 사용자가 다른 작업에 빠르게 액세스할 수 있도록 상단 앱 바를 추가해보겠습니다.

AppBar 위젯 추가

shr_product_grid_fragment.xml에서 '완료'가 포함된 <LinearLayout> 블록을 삭제합니다. TextView를 다음으로 바꿉니다.

shr_product_grid_fragment.xml

<com.google.android.material.appbar.AppBarLayout
   android:layout_width="match_parent"
   android:layout_height="wrap_content">

   <androidx.appcompat.widget.Toolbar
       android:id="@+id/app_bar"
       style="@style/Widget.Shrine.Toolbar"
       android:layout_width="match_parent"
       android:layout_height="?attr/actionBarSize"
       app:title="@string/shr_app_name" />
</com.google.android.material.appbar.AppBarLayout>

이제 shr_product_grid_fragment.xml가 다음과 같이 표시됩니다.

shr_product_grid_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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=".ProductGridFragment">

   <com.google.android.material.appbar.AppBarLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content">

       <androidx.appcompat.widget.Toolbar
           android:id="@+id/app_bar"
           style="@style/Widget.Shrine.Toolbar"
           android:layout_width="match_parent"
           android:layout_height="?attr/actionBarSize"
           app:title="@string/shr_app_name" />
   </com.google.android.material.appbar.AppBarLayout>
  
</FrameLayout>

대다수 앱 바에는 제목 옆에 버튼이 있습니다. 메뉴 아이콘을 추가해 보겠습니다.

탐색 아이콘 추가

계속 shr_product_grid_fragment.xml에서 방금 레이아웃에 추가한 Toolbar XML 구성요소에 다음을 추가합니다.

shr_product_grid_fragment.xml

app:navigationIcon="@drawable/shr_menu"

shr_product_grid_fragment.xml는 다음과 같이 표시됩니다.

shr_product_grid_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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=".ProductGridFragment">
  
   <com.google.android.material.appbar.AppBarLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content">

       <androidx.appcompat.widget.Toolbar
           android:id="@+id/app_bar"
           style="@style/Widget.Shrine.Toolbar"
           android:layout_width="match_parent"
           android:layout_height="?attr/actionBarSize"
           app:navigationIcon="@drawable/shr_menu"
           app:title="@string/shr_app_name" />
   </com.google.android.material.appbar.AppBarLayout>
  
</FrameLayout>

작업 버튼 추가 및 상단 앱 바 스타일 지정

앱 바의 끝쪽에 버튼을 추가할 수도 있습니다. Android에서는 이를 작업 버튼이라고 합니다. 상단 앱 바의 스타일을 지정하고 메뉴에 작업 버튼을 프로그래매틱 방식으로 추가합니다.

ProductGridFragment.ktonCreateView 함수에서 setSupportActionBar를 사용하여 activityToolbarActionBar로 사용하도록 설정합니다. inflater로 뷰를 만든 후에는 이 작업을 실행할 수 있습니다.

ProductGridFragment.kt

override fun onCreateView(
       inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
   // Inflate the layout for this fragment with the ProductGrid theme
   val view = inflater.inflate(R.layout.shr_product_grid_fragment, container, false)

   // Set up the toolbar.
   (activity as AppCompatActivity).setSupportActionBar(view.app_bar)

   return view;
}

다음으로, 방금 툴바를 설정하기 위해 변경한 메서드 바로 아래에서 onCreateOptionsMenu를 재정의하여 shr_toolbar_menu.xml의 콘텐츠를 툴바에 확장합니다.

ProductGridFragment.kt

override fun onCreateOptionsMenu(menu: Menu, menuInflater: MenuInflater) {
   menuInflater.inflate(R.menu.shr_toolbar_menu, menu)
   super.onCreateOptionsMenu(menu, menuInflater)
}

마지막으로 ProductGridFragment.kt에서 onCreate()를 재정의하고 super()를 호출한 후 true를 사용하여 setHasOptionMenu를 호출합니다.

ProductGridFragment.kt

override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   setHasOptionsMenu(true)
}

위의 코드 스니펫은 XML 레이아웃의 앱 바를 이 활동의 작업 모음으로 설정합니다. 콜백 onCreateOptionsMenu는 메뉴로 사용할 항목을 활동에 알려줍니다. 이 경우 R.menu.shr_toolbar_menu의 메뉴 항목이 앱 바에 배치됩니다. 메뉴 파일에는 '검색'과 '필터'라는 두 가지 항목이 있습니다.

shr_toolbar_menu.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/search"
       android:icon="@drawable/shr_search"
       android:title="@string/shr_search_title"
       app:showAsAction="always" />
   <item
       android:id="@+id/filter"
       android:icon="@drawable/shr_filter"
       android:title="@string/shr_filter_title"
       app:showAsAction="always" />
</menu>

변경 후 ProductGridFragment.kt 파일은 다음과 같이 표시됩니다.

ProductGridFragment.kt

package com.google.codelabs.mdc.kotlin.shrine

import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import com.google.codelabs.mdc.kotlin.shrine.network.ProductEntry
import kotlinx.android.synthetic.main.shr_product_grid_fragment.view.*

class ProductGridFragment : Fragment() {

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setHasOptionsMenu(true)
   }

   override fun onCreateView(
           inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
       // Inflate the layout for this fragment with the ProductGrid theme
       val view = inflater.inflate(R.layout.shr_product_grid_fragment, container, false)

       // Set up the tool bar
       (activity as AppCompatActivity).setSupportActionBar(view.app_bar)

       return view;
   }

   override fun onCreateOptionsMenu(menu: Menu, menuInflater: MenuInflater) {
       menuInflater.inflate(R.menu.shr_toolbar_menu, menu)
       super.onCreateOptionsMenu(menu, menuInflater)
   }
}

빌드하고 실행합니다. 홈 화면이 다음과 같이 표시됩니다.

d04e8aa3b27f4754.png

이제 툴바에는 탐색 아이콘과 제목, 오른쪽에 두 개의 작업 아이콘이 있습니다. 툴바는 또한 툴바가 콘텐츠와 다른 레이어에 있음을 나타내는 은은한 그림자를 사용하여 고도를 표시합니다.

4. 카드 추가

앱에 구조가 생겼으니 이제 콘텐츠를 카드에 배치하여 정리해 보겠습니다.

카드 추가하기

이제 상단 앱 바 아래에 카드 하나를 추가해보겠습니다. 카드에는 이미지 영역, 제목, 보조 텍스트 라벨이 있어야 합니다. AppBarLayout 아래의 shr_product_grid_fragment.xml에 다음을 추가합니다.

shr_product_grid_fragment.xml

<com.google.android.material.card.MaterialCardView
   android:layout_width="160dp"
   android:layout_height="180dp"
   android:layout_marginBottom="16dp"
   android:layout_marginLeft="16dp"
   android:layout_marginRight="16dp"
   android:layout_marginTop="70dp"
   app:cardBackgroundColor="?attr/colorPrimaryDark"
   app:cardCornerRadius="4dp">

   <LinearLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:layout_gravity="bottom"
       android:background="#FFFFFF"
       android:orientation="vertical"
       android:padding="8dp">

       <TextView
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:padding="2dp"
           android:text="@string/shr_product_title"
           android:textAppearance="?attr/textAppearanceHeadline6" />

       <TextView
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:padding="2dp"
           android:text="@string/shr_product_description"
           android:textAppearance="?attr/textAppearanceBody2" />
   </LinearLayout>
</com.google.android.material.card.MaterialCardView>

빌드 및 실행:

f6184a55ccb5f920.png

이 미리보기에서는 카드가 왼쪽 가장자리에서 삽입되어 있으며 모서리가 둥글고 그림자 (카드의 고도를 표현)가 있음을 확인할 수 있습니다. 전체 요소를 '컨테이너'라고 합니다. 컨테이너를 제외하고 그 안에 포함된 모든 요소는 선택사항입니다.

헤더 텍스트, 썸네일 또는 아바타, 부제목 텍스트, 구분선, 버튼 및 아이콘과 같은 요소를 컨테이너에 추가할 수 있습니다. 예를 들어 방금 만든 카드는 LinearLayoutTextView 두 개 (하나는 제목용, 다른 하나는 보조 텍스트용)를 포함하고 있으며 카드 하단에 정렬됩니다.

카드는 보통 다른 카드와 함께 컬렉션으로 표시됩니다. 이 Codelab의 다음 섹션에서는 그리드에 컬렉션으로 배치합니다.

5. 카드 그리드 만들기

화면에 여러 카드가 표시될 때마다 하나 이상의 컬렉션으로 그룹화됩니다. 그리드의 카드는 동일 평면상에 있습니다. 즉, 카드는 서로 동일한 휴면 고도를 공유합니다. 단, 카드를 선택하거나 드래그하는 경우는 예외이지만 이 Codelab에서는 다루지 않습니다.

카드 그리드 설정하기

제공된 shr_product_card.xml 파일을 살펴보세요.

shr_product_card.xml

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   app:cardBackgroundColor="@android:color/white"
   app:cardElevation="2dp"
   app:cardPreventCornerOverlap="true">

   <LinearLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:orientation="vertical">

       <com.android.volley.toolbox.NetworkImageView
           android:id="@+id/product_image"
           android:layout_width="match_parent"
           android:layout_height="@dimen/shr_product_card_image_height"
           android:background="?attr/colorPrimaryDark"
           android:scaleType="centerCrop" />

       <LinearLayout
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:orientation="vertical"
           android:padding="16dp">

           <TextView
               android:id="@+id/product_title"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:text="@string/shr_product_title"
               android:textAppearance="?attr/textAppearanceHeadline6" />

           <TextView
               android:id="@+id/product_price"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:text="@string/shr_product_description"
               android:textAppearance="?attr/textAppearanceBody2" />
       </LinearLayout>
   </LinearLayout>
</com.google.android.material.card.MaterialCardView>

이 카드 레이아웃에는 이미지가 있는 카드 (이 경우 URL에서 이미지를 로드하고 표시할 수 있는 NetworkImageView)와 두 개의 TextViews가 포함되어 있습니다.

다음으로 Google에서 제공한 ProductCardRecyclerViewAdapter을(를) 살펴보세요. ProductGridFragment와 동일한 패키지에 있습니다.

ProductCardRecyclerViewAdapter.kt

package com.google.codelabs.mdc.kotlin.shrine

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView

import com.google.codelabs.mdc.kotlin.shrine.network.ProductEntry

/**
* Adapter used to show a simple grid of products.
*/
class ProductCardRecyclerViewAdapter(private val productList: List<ProductEntry>) : RecyclerView.Adapter<ProductCardViewHolder>() {

   override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductCardViewHolder {
       val layoutView = LayoutInflater.from(parent.context).inflate(R.layout.shr_product_card, parent, false)
       return ProductCardViewHolder(layoutView)
   }

   override fun onBindViewHolder(holder: ProductCardViewHolder, position: Int) {
       // TODO: Put ViewHolder binding code here in MDC-102
   }

   override fun getItemCount(): Int {
       return productList.size
   }
}

위의 어댑터 클래스는 그리드의 콘텐츠를 관리합니다. 각 뷰가 주어진 콘텐츠로 무엇을 해야 하는지 결정하기 위해 곧 onBindViewHolder()의 코드를 작성할 예정입니다.

동일한 패키지에서 ProductCardViewHolder도 살펴볼 수 있습니다. 이 클래스는 나중에 수정할 수 있도록 카드 레이아웃에 영향을 미치는 뷰를 저장합니다.

ProductCardViewHolder.kt

package com.google.codelabs.mdc.kotlin.shrine

import android.view.View
import androidx.recyclerview.widget.RecyclerView

class ProductCardViewHolder(itemView: View) //TODO: Find and store views from itemView
   : RecyclerView.ViewHolder(itemView)

그리드를 설정하려면 먼저 shr_product_grid_fragment.xml에서 자리표시자 MaterialCardView를 삭제해야 합니다. 다음으로 카드 그리드를 나타내는 구성요소를 추가해야 합니다. 여기서는 RecyclerView를 사용합니다. AppBarLayout XML 구성요소 아래의 shr_product_grid_fragment.xml에 RecyclerView 구성요소를 추가합니다.

shr_product_grid_fragment.xml

<androidx.core.widget.NestedScrollView
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:layout_marginTop="56dp"
   android:background="@color/productGridBackgroundColor"
   android:paddingStart="@dimen/shr_product_grid_spacing"
   android:paddingEnd="@dimen/shr_product_grid_spacing"
   app:layout_behavior="@string/appbar_scrolling_view_behavior">

   <androidx.recyclerview.widget.RecyclerView
       android:id="@+id/recycler_view"
       android:layout_width="match_parent"
       android:layout_height="match_parent" />

</androidx.core.widget.NestedScrollView>

shr_product_grid_fragment.xml은 다음과 같이 표시됩니다.

shr_product_grid_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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=".ProductGridFragment">

   <com.google.android.material.appbar.AppBarLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content">

       <androidx.appcompat.widget.Toolbar
           android:id="@+id/app_bar"
           style="@style/Widget.Shrine.Toolbar"
           android:layout_width="match_parent"
           android:layout_height="?attr/actionBarSize"
           app:navigationIcon="@drawable/shr_menu"
           app:title="@string/shr_app_name" />
   </com.google.android.material.appbar.AppBarLayout>

   <androidx.core.widget.NestedScrollView
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:layout_marginTop="56dp"
       android:background="@color/productGridBackgroundColor"
       android:paddingStart="@dimen/shr_product_grid_spacing"
       android:paddingEnd="@dimen/shr_product_grid_spacing"
       app:layout_behavior="@string/appbar_scrolling_view_behavior">

       <androidx.recyclerview.widget.RecyclerView
           android:id="@+id/recycler_view"
           android:layout_width="match_parent"
           android:layout_height="match_parent" />

   </androidx.core.widget.NestedScrollView>

</FrameLayout>

마지막으로 onCreateView()에서 setUpToolbar(view)를 호출한 후와 return 문 앞에 RecyclerView 초기화 코드를 ProductGridFragment.kt에 추가합니다.

ProductGridFragment.kt

override fun onCreateView(
       inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
   // Inflate the layout for this fragment with the ProductGrid theme
   val view = inflater.inflate(R.layout.shr_product_grid_fragment, container, false)

   // Set up the toolbar.
   (activity as AppCompatActivity).setSupportActionBar(view.app_bar)

   // Set up the RecyclerView
   view.recycler_view.setHasFixedSize(true)
   view.recycler_view.layoutManager = GridLayoutManager(context, 2, RecyclerView.VERTICAL, false)
   val adapter = ProductCardRecyclerViewAdapter(
           ProductEntry.initProductEntryList(resources))
   view.recycler_view.adapter = adapter
   val largePadding = resources.getDimensionPixelSize(R.dimen.shr_product_grid_spacing)
   val smallPadding = resources.getDimensionPixelSize(R.dimen.shr_product_grid_spacing_small)
   view.recycler_view.addItemDecoration(ProductGridItemDecoration(largePadding, smallPadding))

   return view;
}

위의 코드 스니펫에는 RecyclerView를 설정하는 데 필요한 초기화 단계가 포함되어 있습니다. 여기에는 RecyclerView의 레이아웃 관리자 설정, RecyclerView의 어댑터 초기화 및 설정이 포함됩니다.

이제 ProductGridFragment.kt 파일이 다음과 같이 표시됩니다.

ProductGridFragment .kt

package com.google.codelabs.mdc.kotlin.shrine

import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.codelabs.mdc.kotlin.shrine.network.ProductEntry
import kotlinx.android.synthetic.main.shr_product_grid_fragment.view.*

class ProductGridFragment : Fragment() {

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setHasOptionsMenu(true)
   }

   override fun onCreateView(
           inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
       // Inflate the layout for this fragment with the ProductGrid theme
       val view = inflater.inflate(R.layout.shr_product_grid_fragment, container, false)

       // Set up the toolbar.
       (activity as AppCompatActivity).setSupportActionBar(view.app_bar)

       // Set up the RecyclerView
       view.recycler_view.setHasFixedSize(true)
       view.recycler_view.layoutManager = GridLayoutManager(context, 2, RecyclerView.VERTICAL, false)
       val adapter = ProductCardRecyclerViewAdapter(
               ProductEntry.initProductEntryList(resources))
       view.recycler_view.adapter = adapter
       val largePadding = resources.getDimensionPixelSize(R.dimen.shr_product_grid_spacing)
       val smallPadding = resources.getDimensionPixelSize(R.dimen.shr_product_grid_spacing_small)
       view.recycler_view.addItemDecoration(ProductGridItemDecoration(largePadding, smallPadding))

       return view;
   }

   override fun onCreateOptionsMenu(menu: Menu, menuInflater: MenuInflater) {
       menuInflater.inflate(R.menu.shr_toolbar_menu, menu)
       super.onCreateOptionsMenu(menu, menuInflater)
   }
}

빌드 및 실행:

f9aeab846fc3bb4c.png

이제 카드를 사용할 수 있습니다. 아직 아무것도 표시되지 않으므로 제품 데이터를 추가해 보겠습니다.

이미지 및 텍스트 추가하기

각 카드에 이미지, 제품 이름, 가격을 추가합니다. ViewHolder 추상화는 각 카드의 뷰를 보유합니다. ViewHolder에서 다음과 같이 세 개의 뷰를 추가합니다.

ProductCardViewHolder.kt

package com.google.codelabs.mdc.kotlin.shrine

import android.view.View
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView

import com.android.volley.toolbox.NetworkImageView

class ProductCardViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

   var productImage: NetworkImageView = itemView.findViewById(R.id.product_image)
   var productTitle: TextView = itemView.findViewById(R.id.product_title)
   var productPrice: TextView = itemView.findViewById(R.id.product_price)
}

아래와 같이 각 제품 뷰의 제목, 가격, 제품 이미지를 설정하도록 ProductCardRecyclerViewAdapteronBindViewHolder() 메서드를 업데이트합니다.

ProductCardRecyclerViewAdapter.kt

override fun onBindViewHolder(holder: ProductCardViewHolder, position: Int) {
   if (position < productList.size) {
       val product = productList[position]
       holder.productTitle.text = product.title
       holder.productPrice.text = product.price
       ImageRequester.setImageFromUrl(holder.productImage, product.url)
   }
}

위 코드는 ViewHolder를 사용하여 각 카드로 무엇을 해야 하는지 RecyclerView의 어댑터에 알려줍니다.

여기서는 각 ViewHolderTextView에 텍스트 데이터를 설정하고 ImageRequester를 호출하여 URL에서 이미지를 가져옵니다. ImageRequester는 편의를 위해 제공된 클래스이며 Volley 라이브러리를 사용합니다. 이 Codelab의 범위를 벗어나는 주제이지만 직접 코드를 탐색해도 됩니다.

빌드 및 실행:

249db074eff043f4.png

이제 제품이 앱에 표시됩니다.

6. 요약

앱에는 사용자를 로그인 화면에서 제품을 볼 수 있는 홈 화면으로 안내하는 기본적인 흐름이 있습니다. 코드 몇 줄만으로 제목과 버튼 세 개가 있는 상단 앱 바와 앱 콘텐츠를 표시하는 카드 그리드를 추가했습니다. 이제 홈 화면이 기본적인 구조와 실행 가능한 콘텐츠로 간단하고 기능적입니다.

다음 단계

상단 앱 바와 카드, 텍스트 입력란, 버튼으로 이제 MDC-Android 라이브러리의 핵심 Material Design 구성요소 네 가지를 사용했습니다. MDC-Android 카탈로그를 방문하여 더 많은 구성요소를 살펴볼 수 있습니다.

앱이 완전히 작동하지만 아직 특정 브랜드나 스타일을 표현하지는 않습니다. MDC-103: 색상, 모양, 고도, 유형을 사용한 머티리얼 디자인 테마에서 이러한 구성요소의 스타일을 맞춤설정하여 생동감 있고 현대적인 브랜드를 표현합니다.

적절한 시간과 노력을 들여 이 Codelab을 완료할 수 있었습니다.

매우 동의함 동의함 보통 동의하지 않음 전혀 동의하지 않음

앞으로 머티리얼 구성요소를 계속 사용하고 싶습니다.

매우 동의함 동의함 보통 동의하지 않음 전혀 동의하지 않음