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

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/java . Перед началом работы обязательно 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» . Перейдите в каталог, где вы установили примеры кода, и выберите java -> shrine (или найдите shrine на своем компьютере), чтобы открыть проект Shrine.
  2. Подождите немного, пока Android Studio соберет и синхронизирует проект, как это отобразится в индикаторах активности в нижней части окна Android Studio.
  3. На этом этапе Android Studio может выдавать ошибки сборки, поскольку у вас отсутствует Android SDK или инструменты сборки, например, показанные ниже. Следуйте инструкциям в Android Studio, чтобы установить/обновить их и синхронизировать ваш проект.

F5H6srsw_5xOPGFpKrm1RwgewatxA_HUbDI1PWoQUAoJcT6DpfBOkAYwq3S-2vUHvweUa FgAmG7BtUKkGouUbhTwXQh53qec8tO5eVecdlo7QIoLc8rNxFEBb8l7RlS-KzBbZOzVhA

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

Проекту необходима зависимость от библиотеки поддержки 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 , добавьте следующее в XML-компонент Toolbar (который вы только что добавили в свой макет):

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, установите Toolbar для использования в качестве ActionBar с помощью setSupportActionBar :

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

Теперь добавьте вызов метода setUpToolbar , который мы добавили в содержимое метода onCreateView() следующим образом:

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

Наконец, добавьте метод onCreate() в файл ProductGridFragment.java . В теле метода установите параметр 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

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

В контейнер можно добавить следующие элементы: заголовок, миниатюру или аватар, подзаголовок, разделители и даже кнопки и значки. Например, созданная нами карточка содержит два элемента 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.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
   }
}

Для настройки сетки сначала необходимо удалить заглушку MaterialCardView из shr_product_grid_fragment.xml . Затем следует добавить компонент, представляющий нашу сетку карточек. В данном случае добавьте компонент 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.java после вызова setUpToolbar(view) и перед оператором return :

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

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

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

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

249db074eff043f4.png

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

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

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

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

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

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

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

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

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

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