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

1. 소개

logo_components_color_2x_web_96dp.png

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

MDC-101 Codelab에서는 머티리얼 구성요소(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/java 디렉터리에 있습니다. 시작하기 전에 해당 디렉터리로 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를 클릭합니다. 샘플 코드를 설치한 디렉터리로 이동하여 java -> Shrine을 선택하거나 컴퓨터에서 shrine을 검색하여 Shrine 프로젝트를 엽니다.
  2. Android 스튜디오 창 하단의 활동 표시기에 나타나는 것처럼 Android 스튜디오가 프로젝트를 빌드하고 동기화할 때까지 잠시 기다립니다.
  3. 이 시점에서 Android 스튜디오에 아래와 같은 빌드 오류가 발생할 수 있습니다. Android SDK나 빌드 도구가 누락되었기 때문입니다. Android 스튜디오의 안내를 따라 이러한 항목을 설치/업데이트하고 프로젝트를 동기화합니다.

F5H6srsw_5xOPGFpKrm1RwgewatxA_HUbDI1PWoQUAoJcT6DpfBOkAYwq3S-2vUHvweUaFgAmG7BtUKkGouUbhTwXQh53qec8tO5eVecdlo7QIoLc8rNxFEBb8l7RlS-KzBbZOzVhA

프로젝트 종속 항목 추가

프로젝트에는 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에서는 이를 작업 버튼이라고 합니다.

상단 앱 바의 스타일을 지정하고 프로그래매틱 방식으로 메뉴에 작업 버튼을 추가합니다.

먼저 툴바를 설정하는 메서드를 만들어 보겠습니다. 메서드는 id를 사용하여 툴바 참조를 가져오고 getActivity()를 사용하여 활동 참조도 가져와야 합니다. 활동이 null이 아니면 setSupportActionBar를 사용하여 ToolbarActionBar로 사용하도록 설정합니다.

ProductGridFragment.java

private void setUpToolbar(View view) {
   Toolbar toolbar = view.findViewById(R.id.app_bar);
   AppCompatActivity activity = (AppCompatActivity) getActivity();
   if (activity != null) {
       activity.setSupportActionBar(toolbar);
   }
}

다음으로 방금 추가한 setUpToolbar 메서드 바로 아래에서 onCreateOptionsMenu를 재정의하여 shr_toolbar_menu.xml의 콘텐츠를 툴바에 확장합니다.

ProductGridFragment.java

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
   menuInflater.inflate(R.menu.shr_toolbar_menu, menu);
   super.onCreateOptionsMenu(menu, menuInflater);
}

이제 다음을 사용하여 onCreateView() 메서드의 콘텐츠에 추가한 setUpToolbar 메서드 호출을 추가합니다.

ProductGridFragment.java

@Override
public View onCreateView(
       @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
   // Inflate the layout for this fragment with the ProductGrid theme
   View view = inflater.inflate(R.layout.shr_product_grid_fragment, container, false);

   // Set up the toolbar
   setUpToolbar(view);

   return view;
}

마지막으로 ProductGridFragment.javaonCreate() 메서드를 추가합니다. 메서드 본문에서 setHasOptionMenu의 매개변수를 true로 설정합니다.

메서드는 다음과 같이 표시됩니다.

ProductGridFragment.java

@Override
public void onCreate(Bundle savedInstanceState) {
   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.java 파일이 다음과 같이 표시됩니다.

ProductGridFragment.java

package com.google.codelabs.mdc.java.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 android.widget.Toolbar;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;


public class ProductGridFragment extends Fragment {

   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setHasOptionsMenu(true);
   }
  
   @Override
   public View onCreateView(
           @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
       // Inflate the layout for this fragment with the ProductGrid theme
       View view = inflater.inflate(R.layout.shr_product_grid_fragment, container, false);

       // Set up the toolbar
       setUpToolbar(view);

       return view;
   }
  
   private void setUpToolbar(View view) {
       Toolbar toolbar = view.findViewById(R.id.app_bar);
       AppCompatActivity activity = (AppCompatActivity) getActivity();
       if (activity != null) {
           activity.setSupportActionBar(toolbar);
       }
   }

   @Override
   public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
       menuInflater.inflate(R.menu.shr_toolbar_menu, menu);
       super.onCreateOptionsMenu(menu, menuInflater);
   }

}

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

d04e8aa3b27f4754.png

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

4. 카드 추가

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

카드 추가하기

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

shr_product_grid_fragment.xml에서 AppBarLayout 아래에 다음을 추가합니다.

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 2개가 포함되어 있습니다.

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

ProductCardRecyclerViewAdapter.java

package com.google.codelabs.mdc.java.shrine;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.google.codelabs.mdc.java.shrine.network.ImageRequester;
import com.google.codelabs.mdc.java.shrine.network.ProductEntry;

import java.util.List;

/**
* Adapter used to show a simple grid of products.
*/
public class ProductCardRecyclerViewAdapter extends RecyclerView.Adapter<ProductCardViewHolder> {

   private List<ProductEntry> productList;
   private ImageRequester imageRequester;

   ProductCardRecyclerViewAdapter(List<ProductEntry> productList) {
       this.productList = productList;
       imageRequester = ImageRequester.getInstance();
   }

   @NonNull
   @Override
   public ProductCardViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
       View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.shr_product_card, parent, false);
       return new ProductCardViewHolder(layoutView);
   }

   @Override
   public void onBindViewHolder(@NonNull ProductCardViewHolder holder, int position) {
       // TODO: Put ViewHolder binding code here in MDC-102
   }

   @Override
   public int getItemCount() {
       return productList.size();
   }
}

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

같은 패키지에서 ProductCardViewHolder를 확인할 수도 있습니다. 이 클래스는 나중에 수정할 수 있도록 카드 레이아웃에 영향을 미치는 뷰를 저장합니다.

ProductCardViewHolder.java

package com.google.codelabs.mdc.java.shrine;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import android.view.View;

public class ProductCardViewHolder extends RecyclerView.ViewHolder {

   public ProductCardViewHolder(@NonNull View itemView) {
       super(itemView);
       // TODO: Find and store views from itemView
   }
}

그리드를 설정하려면 먼저 shr_product_grid_fragment.xml에서 자리표시자 MaterialCardView를 삭제해야 합니다. 다음으로 카드 그리드를 나타내는 구성요소를 추가해야 합니다. 이 경우 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.java에 추가합니다.

ProductGridFragment.java

@Override
public View onCreateView(
       @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
   ...
   setUpToolbar(view);

   // Set up the RecyclerView
   RecyclerView recyclerView = view.findViewById(R.id.recycler_view);
   recyclerView.setHasFixedSize(true);
   recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2, GridLayoutManager.VERTICAL, false));
   ProductCardRecyclerViewAdapter adapter = new ProductCardRecyclerViewAdapter(
           ProductEntry.initProductEntryList(getResources()));
   recyclerView.setAdapter(adapter);
   int largePadding = getResources().getDimensionPixelSize(R.dimen.shr_product_grid_spacing);
   int smallPadding = getResources().getDimensionPixelSize(R.dimen.shr_product_grid_spacing_small);
   recyclerView.addItemDecoration(new ProductGridItemDecoration(largePadding, smallPadding));

   return view;
}

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

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

ProductGridFragment.java

package com.google.codelabs.mdc.java.shrine;

import android.os.Bundle;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toolbar;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager;


import com.google.codelabs.mdc.java.shrine.network.ProductEntry;

public class ProductGridFragment extends Fragment {

   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setHasOptionsMenu(true);
   }

   @Override
   public View onCreateView(
           @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
       // Inflate the layout for this fragment with the ProductGrid theme
       View view = inflater.inflate(R.layout.shr_product_grid_fragment, container, false);

       // Set up the toolbar
       setUpToolbar(view);

       // Set up the RecyclerView
       RecyclerView recyclerView = view.findViewById(R.id.recycler_view);
       recyclerView.setHasFixedSize(true);
       recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2, GridLayoutManager.VERTICAL, false));
       ProductCardRecyclerViewAdapter adapter = new ProductCardRecyclerViewAdapter(
               ProductEntry.initProductEntryList(getResources()));
       recyclerView.setAdapter(adapter);
       int largePadding = getResources().getDimensionPixelSize(R.dimen.shr_product_grid_spacing);
       int smallPadding = getResources().getDimensionPixelSize(R.dimen.shr_product_grid_spacing_small);
       recyclerView.addItemDecoration(new ProductGridItemDecoration(largePadding, smallPadding));

       return view;
   }

   private void setUpToolbar(View view) {
       Toolbar toolbar = view.findViewById(R.id.app_bar);
       AppCompatActivity activity = (AppCompatActivity) getActivity();
       if (activity != null) {
           activity.setSupportActionBar(toolbar);
       }
   }

   @Override
   public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
       menuInflater.inflate(R.menu.shr_toolbar_menu, menu);
       super.onCreateOptionsMenu(menu, menuInflater);
   }

}

빌드하고 실행합니다.

f9aeab846fc3bb4c.png

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

이미지 및 텍스트 추가하기

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

ProductCardViewHolder.java

package com.google.codelabs.mdc.java.shrine;

import androidx.recyclerview.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;

import com.android.volley.toolbox.NetworkImageView;

public class ProductCardViewHolder extends RecyclerView.ViewHolder {

   public NetworkImageView productImage;
   public TextView productTitle;
   public TextView productPrice;

   public ProductCardViewHolder(@NonNull View itemView) {
       super(itemView);
       productImage = itemView.findViewById(R.id.product_image);
       productTitle = itemView.findViewById(R.id.product_title);
       productPrice = itemView.findViewById(R.id.product_price);
   }
}

RecyclerView의 어댑터에서 ViewHolder,에서 onBindViewHolder() 메서드를 업데이트하여 각 뷰에 정보를 설정합니다.

ProductCardRecyclerViewAdapter.java

@Override
public void onBindViewHolder(@NonNull ProductCardViewHolder holder, int position) {
   if (productList != null && position < productList.size()) {
       ProductEntry product = productList.get(position);
       holder.productTitle.setText(product.title);
       holder.productPrice.setText(product.price);
       imageRequester.setImageFromUrl(holder.productImage, product.url);
   }
}

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

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

빌드 및 실행:

249db074eff043f4.png

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

6. 요약

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

다음 단계

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

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

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

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

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

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