MDC-102 Android: структура и макет материала (Kotlin)

1. Введение

logo_components_color_2x_web_96dp.png

Компоненты Material (MDC) помогают разработчикам внедрять Material Design. Созданные командой инженеров и UX-дизайнеров Google, MDC включают в себя десятки красивых и функциональных компонентов пользовательского интерфейса и доступны для Android, iOS, веб-приложений и Flutter.material.io/develop

В практическом занятии MDC-101 вы использовали два компонента Material Components (MDC) для создания страницы входа в систему: текстовые поля и кнопки с эффектом чернильной ряби. Теперь давайте расширим эту основу, добавив навигацию, структуру и данные.

Что вы построите

В этом практическом занятии вы создадите главный экран для приложения Shrine , приложения электронной коммерции, продающего одежду и товары для дома. Он будет содержать:

  • Панель приложений верхнего уровня
  • Список товаров в виде сетки

249db074eff043f4.png

Компоненты MDC-Android в этом практическом занятии.

  • AppBarLayout
  • MaterialCardView

Что вам понадобится

  • Базовые знания разработки под Android.
  • Android Studio (скачайте её здесь, если у вас её ещё нет).
  • Эмулятор или устройство Android (доступное через Android Studio)
  • Пример кода (см. следующий шаг)

Как бы вы оценили свой уровень опыта в разработке Android-приложений?

Новичок Средний Профессионал

2. Настройте среду разработки.

Продолжение темы MDC-101?

Если вы прошли курс MDC-101, ваш код должен быть готов для этого практического занятия. Перейдите к шагу 3: Добавьте верхнюю панель приложения .

Начинать с нуля?

Скачайте стартовое приложение Codelab.

Стартовое приложение находится в каталоге material-components-android-codelabs-102-starter/kotlin . Перед началом работы обязательно cd в этот каталог.

...или клонируйте его с GitHub

Чтобы клонировать этот код с GitHub, выполните следующие команды:

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

Загрузите стартовый код в Android Studio.

  1. После завершения работы мастера установки и появления окна «Добро пожаловать в Android Studio» нажмите « Открыть существующий проект Android Studio» . Перейдите в каталог, где вы установили примеры кода, и выберите kotlin -> shrine (или найдите shrine на своем компьютере), чтобы открыть проект Shipping.
  2. Подождите немного, пока Android Studio соберет и синхронизирует проект, как это отобразится в индикаторах активности в нижней части окна Android Studio.
  3. На этом этапе Android Studio может выдавать ошибки сборки, поскольку у вас отсутствует Android SDK или инструменты сборки, например, показанные ниже. Следуйте инструкциям в Android Studio, чтобы установить/обновить их и синхронизировать ваш проект.

KzoYWC1S7Se7yL8igi1vXF_mbVxAdl2lg5kb7RODrsVpEng0G6U3NK1Qnn0faBBZd2u71yMXioy9tD-7fv3NXvVO4N3EtMMeWDTmqBMMl6egd9R5uXX0T_SKmahbmRor3wZZHX0ByA

Добавьте зависимости проекта

Проекту необходима зависимость от библиотеки поддержки Android MDC . В загруженном вами примере кода эта зависимость уже должна быть указана, но для уверенности рекомендуется выполнить следующие шаги.

  1. Перейдите к файлу build.gradle модуля app и убедитесь, что блок 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. Убедитесь, что конфигурация сборки слева от кнопки «Запуск/Воспроизведение» — это app .
  2. Нажмите зеленую кнопку «Запустить/Играть» , чтобы собрать и запустить приложение.
  3. В окне «Выбор целевого устройства развертывания» , если у вас уже есть устройство Android в списке доступных устройств, перейдите к шагу 8. В противном случае нажмите «Создать новое виртуальное устройство» .
  4. На экране «Выбор оборудования» выберите телефонное устройство, например Pixel 2 , а затем нажмите «Далее» .
  5. На экране «Образ системы» выберите последнюю версию Android , предпочтительно с самым высоким уровнем API. Если она не установлена, нажмите на появившуюся ссылку «Загрузить» и завершите загрузку.
  6. Нажмите «Далее» .
  7. На экране «Виртуальное устройство Android (AVD)» оставьте настройки без изменений и нажмите «Готово» .
  8. Выберите устройство Android в диалоговом окне выбора целевого устройства для развертывания.
  9. Нажмите «ОК» .
  10. Android Studio собирает приложение, развертывает его и автоматически открывает на целевом устройстве.

Успех! Вы должны увидеть страницу входа в Shrine из практического занятия MDC-101.

4cb0c218948144b4.png

Теперь, когда экран входа в систему выглядит хорошо, давайте добавим в приложение несколько товаров.

3. Добавьте верхнюю панель приложений.

Главный экран появляется после закрытия страницы входа, и на нем отображается надпись «Вы это сделали!». Отлично! Но теперь пользователю не нужно ничего делать, и он не понимает, где находится в приложении. Чтобы решить эту проблему, пора добавить навигацию.

Material Design предлагает шаблоны навигации, обеспечивающие высокую степень удобства использования. Одним из наиболее заметных компонентов является верхняя панель приложений.

Для обеспечения навигации и быстрого доступа пользователей к другим действиям добавим верхнюю панель приложения.

Добавьте виджет AppBar

В TextView shr_product_grid_fragment.xml удалите блок <LinearLayout> , содержащий текстовое поле "You did it!", и замените его следующим:

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 они называются кнопками действий . Мы оформим верхнюю панель приложения и добавим кнопки действий в её меню программным способом.

В функции onCreateView файла ProductGridFragment.kt установите Toolbar activity в качестве ActionBar с помощью setSupportActionBar . Это можно сделать после создания представления с помощью 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)
}

Наконец, переопределите onCreate() в ProductGridFragment.kt , и после вызова super() вызовите setHasOptionMenu с true :

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. Добавьте карту

Теперь, когда наше приложение имеет некоторую структуру, давайте организуем контент, разместив его в виде карточек.

Добавить карту

Начнём с добавления одной карточки под верхней панелью приложения. Карточка должна содержать область для изображения, заголовок и метку для дополнительного текста. Добавьте следующий код в файл 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

На этом предварительном просмотре вы можете увидеть, что карточка отстоит от левого края, имеет закругленные углы и тень (которая подчеркивает возвышение карточки). Весь элемент называется «контейнером». За исключением контейнера, все элементы внутри него являются необязательными.

В контейнер можно добавить следующие элементы: заголовок, миниатюру или аватар, подзаголовок, разделители и даже кнопки и значки. Например, созданная нами карточка содержит два элемента TextView (один для заголовка, другой для дополнительного текста) в LinearLayout , выровненных по нижнему краю карточки.

Карты обычно выставляются в виде коллекции с другими картами. В следующем разделе этого практического занятия мы расположим их в виде коллекции в виде сетки.

5. Создайте сетку из карточек.

Когда на экране присутствует несколько карточек, они группируются в одну или несколько коллекций. Карточки в сетке лежат в одной плоскости, то есть имеют одинаковую высоту в состоянии покоя (если только их не поднимают или не перетаскивают, но мы не будем рассматривать это в данном практическом занятии).

Расположите карточки в сетке.

Ознакомьтесь с предоставленным нами файлом 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>

Этот макет карточки содержит карточку с изображением (в данном случае, NetworkImageView , который позволяет загружать и отображать изображения по URL-адресу) и два TextViews .

Далее, взгляните на предоставленный нами 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)

Для настройки сетки сначала необходимо удалить заглушку MaterialCardView из shr_product_grid_fragment.xml . Затем следует добавить компонент, представляющий нашу сетку карточек. В данном случае мы будем использовать RecyclerView. Добавьте компонент RecyclerView в файл shr_product_grid_fragment.xml ниже компонента AppBarLayout XML-файле:

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() добавьте код инициализации RecyclerView в файл ProductGridFragment.kt после вызова setUpToolbar(view) и перед оператором return :

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)
}

Обновите метод onBindViewHolder() в ProductCardRecyclerViewAdapter , чтобы установить заголовок, цену и изображение товара для каждого представления товара, как показано ниже:

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)
   }
}

Приведённый выше код указывает адаптеру нашего RecyclerView , что делать с каждой карточкой, используя ViewHolder .

Здесь текстовые данные устанавливаются в каждом из TextView элементов ViewHolder , а для получения изображения по URL-адресу вызывается ImageRequester . Класс ImageRequester предоставлен нами для вашего удобства и использует библиотеку Volley (это тема, выходящая за рамки данного практического занятия, но вы можете изучить код самостоятельно).

Соберите и запустите:

249db074eff043f4.png

Наши товары теперь отображаются в приложении!

6. Подведение итогов

Наше приложение имеет простую структуру, которая ведет пользователя от экрана входа в систему к главному экрану, где можно просматривать товары. Всего несколькими строками кода мы добавили верхнюю панель приложения с заголовком и тремя кнопками, а также сетку карточек для отображения контента приложения. Теперь наш главный экран прост и функционален, имеет базовую структуру и содержит интерактивный контент.

Следующие шаги

Для верхней панели приложения, карточки, текстового поля и кнопки мы использовали четыре основных компонента Material Design из библиотеки MDC-Android! Вы можете ознакомиться с еще большим количеством компонентов, посетив каталог MDC-Android.

Хотя наше приложение полностью функционально, оно пока не отражает какой-либо конкретный бренд или стиль. В курсе MDC-103: Тематическое оформление в стиле Material Design с использованием цвета, формы, рельефа и шрифта , мы настроим стиль этих компонентов, чтобы выразить яркий, современный бренд.

Мне удалось выполнить это практическое задание за разумное время и с разумными затратами усилий.

Полностью согласен Соглашаться Нейтральный Не согласен Категорически не согласен

Я хотел бы и в будущем продолжать использовать компоненты Material.

Полностью согласен Соглашаться Нейтральный Не согласен Категорически не согласен