MDC-101 Android: основы материальных компонентов (MDC) (Java)

logo_components_color_2x_web_96dp.png

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

material.io/develop

Что такое Material Design и Material Components для Android?

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

Для приложений Android Material Components for Android ( MDC Android ) объединяет дизайн и разработку с библиотекой компонентов для обеспечения согласованности во всем приложении. По мере развития системы Material Design эти компоненты обновляются, обеспечивая единообразную реализацию с точностью до пикселя и соответствие стандартам Google для фронтенд-разработки. MDC также доступен для веб-приложений, iOS и Flutter.

В этой лабораторной работе вы создадите страницу входа, используя несколько компонентов MDC Android.

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

Эта лабораторная работа — первая из четырёх, которые помогут вам создать приложение Shrine — приложение электронной коммерции для Android, продающее одежду и товары для дома. В ней будет показано, как настроить компоненты под любой бренд или стиль с помощью MDC-Android.

В этой лабораторной работе вы создадите страницу входа для Shrine, которая будет содержать:

  • Два текстовых поля: одно для ввода имени пользователя, другое для пароля
  • Две кнопки: одна для «Отмена» и одна для «Далее»
  • Название приложения (Shrine)
  • Изображение логотипа Shrine

Компоненты Android MDC в этой лабораторной работе

  • Текстовое поле
  • Кнопка

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

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

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

Новичок Средний Опытный

Запустить Android Studio

При запуске Android Studio должно появиться окно с надписью «Добро пожаловать в Android Studio». Однако, если вы запускаете Android Studio впервые, следуйте инструкциям мастера установки Android Studio, используя значения по умолчанию . Загрузка и установка необходимых файлов может занять несколько минут, поэтому можете оставить его в фоновом режиме, пока вы работаете со следующим разделом.

Загрузите стартовое приложение Codelab

Загрузить стартовое приложение

Стартовое приложение находится в каталоге material-components-android-codelabs-101-starter/java .

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

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

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

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

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

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

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

  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 должен быть запущен в вашем эмуляторе. Вы должны увидеть название Shrine и логотип Shrine прямо под ним.

Давайте взглянем на код. В нашем примере кода мы предоставили простую структуру навигации Fragment для отображения фрагментов и навигации между ними.

Откройте MainActivity.java в каталоге shrine -> app -> src -> main -> java -> com.google.codelabs.mdc.java.shrine . Он должен содержать следующее:

MainActivity.java

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

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;

public class MainActivity extends AppCompatActivity implements NavigationHost {

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.shr_main_activity);

       if (savedInstanceState == null) {
           getSupportFragmentManager()
                   .beginTransaction()
                   .add(R.id.container, new LoginFragment())
                   .commit();
       }
   }

   /**
    * Navigate to the given fragment.
    *
    * @param fragment       Fragment to navigate to.
    * @param addToBackstack Whether or not the current fragment should be added to the backstack.
    */
   @Override
   public void navigateTo(Fragment fragment, boolean addToBackstack) {
       FragmentTransaction transaction =
               getSupportFragmentManager()
                       .beginTransaction()
                       .replace(R.id.container, fragment);

       if (addToBackstack) {
           transaction.addToBackStack(null);
       }

       transaction.commit();
   }
}

Это действие отображает файл макета R.layout.shr_main_activity , определенный в shr_main_activity.xml .

Видно, что в onCreate(), MainActivity.java запускает транзакцию Fragment для отображения LoginFragment . LoginFragment. Именно его мы будем изменять в этой лабораторной работе. В активности также реализован метод navigateTo(Fragment) , определённый в NavigationHost , который позволяет любому фрагменту переходить к другому фрагменту.

Нажмите Command + Click (или Control + Click ) shr_main_activity в файле активности, чтобы открыть файл макета, или перейдите к файлу макета в app -> res -> layout -> shr_main_activity.xml .

shr_main_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:id="@+id/container"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".MainActivity" />

Здесь мы видим простой <FrameLayout> , который служит контейнером для любых фрагментов, отображаемых в активности. Откроем LoginFragment.java .

LoginFragment.java

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

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;

/**
* Fragment representing the login screen for Shrine.
*/
public class LoginFragment extends Fragment {

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

       // Snippet from "Navigate to the next Fragment" section goes here.

       return view;
   }

   // "isPasswordValid" from "Navigate to the next Fragment" section method goes here
}

LoginFragment заполняет файл макета shr_login_fragment и отображает его в onCreateView() . Давайте посмотрим на файл макета shr_login_fragment.xml , чтобы увидеть, как выглядит страница входа.

shr_login_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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"
   android:background="@color/loginPageBackgroundColor"
   tools:context=".LoginFragment">

   <LinearLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:clipChildren="false"
       android:clipToPadding="false"
       android:orientation="vertical"
       android:padding="24dp"
       android:paddingTop="16dp">

       <ImageView
           android:layout_width="64dp"
           android:layout_height="64dp"
           android:layout_gravity="center_horizontal"
           android:layout_marginTop="48dp"
           android:layout_marginBottom="16dp"
           app:srcCompat="@drawable/shr_logo" />

       <TextView
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_gravity="center_horizontal"
           android:layout_marginBottom="132dp"
           android:text="@string/shr_app_name"
           android:textAllCaps="true"
           android:textSize="16sp" />

       <!-- Snippet from "Add text fields" section goes here. -->

       <!-- Snippet from "Add buttons" section goes here. -->

   </LinearLayout>
</ScrollView>

Здесь мы видим <LinearLayout> с <ImageView> наверху, представляющим логотип «Shrine».

Далее следует тег <TextView> , представляющий метку «SHRINE». Текст этой метки — строковый ресурс с именем @string/shr_app_name . Если нажать Command + Click (или Control + Click ) на имя строкового ресурса или открыть app -> res -> values -> strings.xml , можно увидеть файл strings.xml , в котором определены строковые ресурсы. При добавлении дополнительных строковых ресурсов они будут определены здесь. Каждый ресурс в этом файле должен иметь префикс shr_ , указывающий на то, что он является частью приложения Shrine.

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

Для начала добавим на страницу входа два текстовых поля для ввода имени пользователя и пароля. Мы будем использовать компонент MDC Text Field, который включает встроенную функцию отображения плавающей метки и сообщений об ошибках.

Добавьте XML

В shr_login_fragment.xml добавьте два элемента TextInputLayout с дочерним элементом TextInputEditText внутри <LinearLayout> под меткой «SHRINE» <TextView> :

shr_login_fragment.xml

<com.google.android.material.textfield.TextInputLayout
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_margin="4dp"
   android:hint="@string/shr_hint_username">

   <com.google.android.material.textfield.TextInputEditText
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:inputType="text"
       android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.textfield.TextInputLayout
   android:id="@+id/password_text_input"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_margin="4dp"
   android:hint="@string/shr_hint_password">

   <com.google.android.material.textfield.TextInputEditText
       android:id="@+id/password_edit_text"
       android:layout_width="match_parent"
       android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>

В приведенном выше фрагменте кода представлены два текстовых поля, каждое из которых состоит из элемента <TextInputLayout> и дочернего элемента <TextInputEditText> . Текст подсказки для каждого текстового поля указывается в атрибуте android:hint .

Мы добавили два новых строковых ресурса для текстового поля — @string/shr_hint_username и @string/shr_hint_password . Откройте strings.xml , чтобы увидеть эти строковые ресурсы.

strings.xml

...
<string name="shr_hint_username">Username</string>
<string name="shr_hint_password">Password</string>
...

Добавить проверку входных данных

Компоненты TextInputLayout предоставляют встроенную функцию обратной связи об ошибках.

Чтобы отобразить сообщение об ошибке, внесите следующие изменения в shr_login_fragment.xml :

  • Установите атрибут app:errorEnabled в значение true для элемента Password TextInputLayout . Это добавит дополнительный отступ для сообщения об ошибке под текстовым полем.
  • Установите атрибут android:inputType в значение " textPassword " для элемента Password TextInputEditText . Это скроет вводимый текст в поле пароля.

С этими изменениями текстовые поля в shr_login_fragment.xml должны выглядеть следующим образом:

shr_login_fragment.xml

<com.google.android.material.textfield.TextInputLayout
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_margin="4dp"
   android:hint="@string/shr_hint_username">

   <com.google.android.material.textfield.TextInputEditText
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:inputType="text"
       android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.textfield.TextInputLayout
   android:id="@+id/password_text_input"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_margin="4dp"
   android:hint="@string/shr_hint_password"
   app:errorEnabled="true">

   <com.google.android.material.textfield.TextInputEditText
       android:id="@+id/password_edit_text"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:inputType="textPassword"/>
</com.google.android.material.textfield.TextInputLayout>

Теперь попробуйте запустить приложение. Вы увидите страницу с двумя текстовыми полями: «Имя пользователя» и «Пароль»!

Посмотрите на анимацию плавающей этикетки:

Далее мы добавим на страницу входа две кнопки: «Отмена» и «Далее». Мы будем использовать компонент MDC Button, в который встроен культовый эффект чернильной ряби в стиле Material Design.

Добавьте XML

В shr_login_fragment.xml добавьте элемент <RelativeLayout> к элементу <LinearLayout> под элементами TextInputLayout . Затем добавьте два элемента <MaterialButton> к элементу <RelativeLayout> .

Полученный XML-файл должен выглядеть следующим образом:

shr_login_fragment.xml

<RelativeLayout
   android:layout_width="match_parent"
   android:layout_height="wrap_content">

   <com.google.android.material.button.MaterialButton
       android:id="@+id/next_button"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignParentEnd="true"
       android:layout_alignParentRight="true"
       android:text="@string/shr_button_next" />

   <com.google.android.material.button.MaterialButton
       android:id="@+id/cancel_button"
       style="@style/Widget.MaterialComponents.Button.TextButton"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_marginEnd="12dp"
       android:layout_marginRight="12dp"
       android:layout_toStartOf="@id/next_button"
       android:layout_toLeftOf="@id/next_button"
       android:text="@string/shr_button_cancel" />

</RelativeLayout>

Вот и всё! При запуске приложения при нажатии каждой кнопки будет появляться чернильная рябь.

Наконец, мы добавим код Java в LoginFragment.java , чтобы подключить кнопку «ДАЛЕЕ» к другому фрагменту. Вы заметите, что каждому компоненту, добавленному в макет, присвоен id . Мы будем использовать эти id для ссылки на компоненты в нашем коде, а также для проверки ошибок и навигации.

Давайте добавим приватный логический метод isPasswordValid в LoginFragment.java под onCreateView() с логикой для определения корректности пароля. Для целей этой демонстрации мы просто убедимся, что пароль содержит не менее 8 символов:

LoginFragment.java

/*
   In reality, this will have more complex logic including, but not limited to, actual
   authentication of the username and password.
*/
private boolean isPasswordValid(@Nullable Editable text) {
   return text != null && text.length() >= 8;
}

Затем добавьте прослушиватель кликов к кнопке «Далее», который устанавливает и сбрасывает ошибку на основе метода isPasswordValid() который мы только что создали. В onCreateView() этот прослушиватель кликов должен быть расположен между строкой инфлятора и строкой return view .

Далее добавим прослушиватель нажатия клавиш к паролю TextInputEditText для отслеживания событий нажатия клавиш, которые сбрасывают ошибку. Этот прослушиватель также должен использовать isPasswordValid() для проверки корректности пароля. Вы можете добавить его непосредственно под прослушивателем нажатия клавиш в onCreateView() .

Теперь ваш метод onCreateView() должен выглядеть примерно так:

LoginFragment.java

@Override
public View onCreateView(
       @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
   // Inflate the layout for this fragment
   View view = inflater.inflate(R.layout.shr_login_fragment, container, false);
   final TextInputLayout passwordTextInput = view.findViewById(R.id.password_text_input);
   final TextInputEditText passwordEditText = view.findViewById(R.id.password_edit_text);
   MaterialButton nextButton = view.findViewById(R.id.next_button);

   // Set an error if the password is less than 8 characters.
   nextButton.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View view) {
           if (!isPasswordValid(passwordEditText.getText())) {
               passwordTextInput.setError(getString(R.string.shr_error_password));
           } else {
               passwordTextInput.setError(null); // Clear the error
           }
       }
   });

   // Clear the error once more than 8 characters are typed.
   passwordEditText.setOnKeyListener(new View.OnKeyListener() {
       @Override
       public boolean onKey(View view, int i, KeyEvent keyEvent) {
           if (isPasswordValid(passwordEditText.getText())) {
               passwordTextInput.setError(null); //Clear the error
           }
           return false;
       }
   });
   return view;
}            

Теперь мы можем перейти к другому фрагменту. Обновите OnClickListener в onCreateView() , чтобы перейти к другому фрагменту при успешной проверке ошибки. Это можно сделать, добавив следующую строку для перехода к ProductGridFragment в блок else обработчика щелчков:

LoginFragment.java

...
((NavigationHost) getActivity()).navigateTo(new ProductGridFragment(), false); // Navigate to the next Fragment
...

Теперь ваш прослушиватель щелчков должен выглядеть следующим образом:

LoginFragment.java

...
MaterialButton nextButton = view.findViewById(R.id.next_button);

// Set an error if the password is less than 8 characters.
nextButton.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View view) {
       if (!isPasswordValid(passwordEditText.getText())) {
           passwordTextInput.setError(getString(R.string.shr_error_password));
       } else {
           passwordTextInput.setError(null); // Clear the error
           ((NavigationHost) getActivity()).navigateTo(new ProductGridFragment(), false); // Navigate to the next Fragment
       }
   }
});
...

Эта новая строка кода вызывает метод navigateTo() из MainActivity для перехода к новому фрагменту — ProductGridFragment . В настоящее время это пустая страница, с которой вы будете работать в MDC-102.

Теперь создайте приложение. Нажмите кнопку «Далее».

Вы справились! Этот экран станет отправной точкой нашей следующей лабораторной работы, над которой вы будете работать в MDC-102.

Используя базовую разметку XML и около 30 строк Java, библиотека Material Components для Android помогла вам создать красивую страницу входа, которая соответствует принципам Material Design, а также выглядит и ведет себя одинаково на всех устройствах.

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

Текстовое поле и кнопка — два основных компонента библиотеки MDC Android, но их гораздо больше! Вы можете изучить остальные компоненты MDC Android . Кроме того, ознакомьтесь с разделом MDC 102: Material Design Structure and Layout, чтобы узнать больше о верхней панели приложения, карточном представлении и сетке. Спасибо за знакомство с Material Components. Надеемся, вам понравилась эта практическая работа!

Мне удалось завершить эту лабораторную работу, затратив разумное количество времени и усилий.

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

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

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