Android kotlin руководство

Данная статья является перевом статьи от Mladen Rakonjac

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

  1. Android Studio 3
  2. Язык программирования Kotlin
  3. Варианты сборки
  4. ConstraintLayout
  5. Библиотека привязки данных Data Binding
  6. Архитектура MVVM + паттерн repository (с mapper’ами) + Android Manager Wrappers
  7. RxJava2 и как это помогает нам в архитектуре
  8. Dagger 2.11, что такое внедрение зависимости, почему вы должны использовать это.
  9. Retrofit (Rx Java2)
  10. Room (Rx Java2)

Каким будет наше приложение?

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

Я попытаюсь объяснить как можно больше строк кода. Вы всегда можете посмотреть код, который я опубликовал на GitHub.

Android Studio

Чтобы установить Android Studio 3, перейдите на эту страницу

Android Studio 3 поддерживает Kotlin. Откройте Create Android Project. Там вы увидите новый флажок с меткой Include Kotlin support. Он выбран по умолчанию. Дважды нажмите кнопку Далее и выберите Empty Activity, затем нажмите Finish.

Поздравляю! Вы сделали первое приложение для Android на Котлине :)

Kotlin

Вы можете видеть MainActivity.kt:

package me.fleka.modernandroidapp

import android.support.v7.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

Расширение .kt означает, что файл является файлом Kotlin.

MainActivity: AppCompatActivity() означает, что мы расширяем AppCompatActivity.

Кроме того, все методы должны иметь ключевое слово fun и в Котлине вам не нужно использовать ;, но вы можете, если хотите. Вы должны использовать ключевое слово override, а не аннотацию, как в Java.

Так что же означает ? в savedInstanceState: Bundle?? Это означает, что savedInstanceState может быть типа Bundle или типа null. Kotlin null безопасный язык. Если у вас есть:

var a : String

вы получите ошибку компиляции, потому что a должна быть инициализированна и это не может быть null. Это означает, что вы должны написать:

var a : String = "Init value"

Кроме того, вы получите ошибку компиляции, если вы это сделаете:

a = null

Чтобы сделать a nullable, вы должны написать:

var a : String?

Почему эта важная особенность языка Котлина? Это помогает нам избежать NPE. Разработчики Android уже устали от NPE. Даже создатель null, сэр Тони Хоар, извинился за изобретение. Предположим, что мы имеем nullable nameTextView. Если переменная равна null, то в следующем коде мы получим NPE:

nameTextView.setEnabled(true)

Но Котлин, на самом деле, хорош, он не позволят нам делать даже такое. Он заставляет нас использовать оператор ? или оператор !!. Если мы используем оператор ?:

nameTextView?.setEnabled(true)

Строка будет исполнена только если nameTextView не null. В ином случае, если вы используете оператор !!:

nameTextView!!.setEnabled(true)

Мы получим NPE если nameTextView null. Это для авантюристов :).
Это было небольшое введение в Kotlin. Когда мы продолжим, я остановлюсь, чтобы описать другой специфический код на Котлине.

2. Build Variants

В разработке часто вы имеете различные окружения. Наиболее стандартным является тестовое и производственное окружение. Эти среды могут отличаться в URL-адресах сервера, иконке, имени, целевом API и т.д. На fleka в каждом проекте у вас есть:

  • finalProduction, который отправляется в Google Play Store.
  • demoProduction, то есть версия с URL-адресом production сервера с новыми функциями, которые всё ещё не находятся в Google Play Store. Наши клиенты могут установить эту версию рядом с Google Play, чтобы они могли протестировать ее и дать нам обратную связь.
  • demoTesting, то же самое, что и demoProduction с тестовым URL-адресом сервера.
  • mock, полезен для меня как для разработчика и дизайнера. Иногда у нас есть готовый дизайн, и наш API ещё не готов. Ожидание API, чтобы быть начать разработку — не решение. Этот вариант сборки снабжён поддельными данными, поэтому команда дизайнеров может проверить его и дать нам обратную связь. Очень полезно это не откладывать. Когда API уже готов, мы перемещаем нашу разработку в окружение demoTesting.

В этом приложении мы будем использовать всех их. У них будут отличаться applicationId и имена. В gradle 3.0.0 есть новый API flavorDimension, который позволяет смешивать разновидности продукта, так, например, вы можете смешать разновидности demo и minApi23. В нашем приложении мы будем использовать только «default» flavorDimension. Перейдите в build.gradle для приложения и вставьте этот код внутри android {}

flavorDimensions "default"
    
productFlavors {

    finalProduction {
        dimension "default"
        applicationId "me.fleka.modernandroidapp"
        resValue "string", "app_name", "Modern App"
    }

    demoProduction {
        dimension "default"
        applicationId "me.fleka.modernandroidapp.demoproduction"
        resValue "string", "app_name", "Modern App Demo P"
    }

    demoTesting {
        dimension "default"
        applicationId "me.fleka.modernandroidapp.demotesting"
        resValue "string", "app_name", "Modern App Demo T"
    }


    mock {
        dimension "default"
        applicationId "me.fleka.modernandroidapp.mock"
        resValue "string", "app_name", "Modern App Mock"
    }
}

Перейдите в strings.xml и удалите строку app_name, чтобы у нас не было конфликтов. Затем нажмите Sync Now. Если вы перейдете в Build Variants, расположенным слева от экрана, вы увидите 4 варианта сборки, каждый из которых имеет два типа сборки: Debug и Release. Перейдите к варианту сборки demoProduction и запустите его. Затем переключитесь на другой и запустите его. Вы должны увидеть два приложения с разными именами.

3. ConstraintLayout

Если вы откроете activity_main.xml, вы увидите, что этот layout — ConstrainLayout. Если вы когда-либо писали приложение под iOS, вы знаете об AutoLayout. ConstraintLayout действительно похож на него. Они даже используют один и тот же алгоритм Cassowary.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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="me.fleka.modernandroidapp.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

Constraint помогает нам описать связи между View. Для каждого View у вас должно быть 4 Constraint, один для каждой стороны. В данном случае наш View ограничен родителем с каждой стороны.

Если вы передвинете TextView «Hello World» немного вверх во вкладке Design, во вкладке Text появится новая линия:

app:layout_constraintVertical_bias="0.28"

Вкладки Design и Text синхронизируются. Наши изменения во вкладке Design влияют на xml во вкладке Text и наоборот. Vertical_bias описывает вертикальную тенденцию view его Constraint. Если вы хотите центровать вертикально, используйте:

app:layout_constraintVertical_bias="0.28"

Давайте сделаем чтобы наш Activity показал только один репозиторий. В нём будут имя репозитория, количество звезд, владелец, и он будет показывать, есть ли у репозитория issues, или нет.

Чтобы получить такой layout, xml должен выглядеть так:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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="me.fleka.modernandroidapp.MainActivity">

    <TextView
        android:id="@+id/repository_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:layout_marginStart="16dp"
        android:textSize="20sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.083"
        tools:text="Modern Android app" />

    <TextView
        android:id="@+id/repository_has_issues"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:layout_marginStart="16dp"
        android:layout_marginTop="8dp"
        android:text="@string/has_issues"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="@+id/repository_name"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintStart_toEndOf="@+id/repository_name"
        app:layout_constraintTop_toTopOf="@+id/repository_name"
        app:layout_constraintVertical_bias="1.0" />

    <TextView
        android:id="@+id/repository_owner"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="16dp"
        android:layout_marginStart="16dp"
        android:layout_marginTop="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/repository_name"
        app:layout_constraintVertical_bias="0.0"
        tools:text="Mladen Rakonjac" />

    <TextView
        android:id="@+id/number_of_starts"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="16dp"
        android:layout_marginStart="16dp"
        android:layout_marginTop="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="1"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/repository_owner"
        app:layout_constraintVertical_bias="0.0"
        tools:text="0 stars" />

</android.support.constraint.ConstraintLayout>

Пусть tools:text вас не смущает. Он просто помогает нам видеть хороший предварительный просмотр макета (layout’а).

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

и мне кажется, что я могу добиться желаемого результата очень быстро.
Это было небольшое введение в ConstraintLayout. Вы можете найти Google code lab здесь, и документацию о ConstraintLayout на GitHub.

4. Библиотека привязки данных Data Binding

Когда я услышал о библиотеке привязки данных, первое вопрос, который я задал себе: «ButterKnife работает очень хорошо для меня. Кроме того, я использую плагин, который помогает мне получать View из xml. Зачем мне это менять?». Как только я узнал больше о привязке данных, у меня было такое же чувство, какое у меня было, когда я впервые использовал ButterKnife.

Как ButterKnife помогает нам?

ButterKnife помогает нам избавиться от скучного findViewById. Итак, если у вас 5 View, без Butterknife у вас есть 5 + 5 строк, чтобы привязать ваши View. С ButterKnife у вас есть 5 строк. Вот и всё.

Что плохо в ButterKnife?

ButterKnife по-прежнему не решает проблему поддержки кода. Когда я использовал ButterKnife, я часто получал исключение во время выполнения, потому что я удалял View в xml, и не удалял код привязки в классе Activity / Fragment. Кроме того, если вы хотите добавить View в xml, вам нужно снова сделать привязку. Это очень скучно. Вы теряете время на поддерживание связей.

Что насчёт библиотеки привязки данных?

Есть много преимуществ! С помощью библиотеки привязки данных вы можете привязать свои View всего одной строкой кода! Позвольте мне показать вам, как это работает. Давайте добавим библиотеку Data Binding в наш проект:

// at the top of file 
apply plugin: 'kotlin-kapt'


android {
    //other things that we already used
    dataBinding.enabled = true
}
dependencies {
    //other dependencies that we used
    kapt "com.android.databinding:compiler:3.0.0-beta1"
}

Обратите внимание, что версия компилятора Data Binding должна совпадать с версией gradle в файле build.gradle проекта:

classpath 'com.android.tools.build:gradle:3.0.0-beta1'

Нажмите Sync Now. Перейдите в activity_main.xml и оберните ConstraintLayout тегом layout:

<?xml version="1.0" encoding="utf-8"?>
<layout 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.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="me.fleka.modernandroidapp.MainActivity">

        <TextView
            android:id="@+id/repository_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="16dp"
            android:layout_marginStart="16dp"
            android:textSize="20sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.083"
            tools:text="Modern Android app" />

        <TextView
            android:id="@+id/repository_has_issues"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="16dp"
            android:layout_marginStart="16dp"
            android:layout_marginTop="8dp"
            android:text="@string/has_issues"
            android:textStyle="bold"
            app:layout_constraintBottom_toBottomOf="@+id/repository_name"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="1.0"
            app:layout_constraintStart_toEndOf="@+id/repository_name"
            app:layout_constraintTop_toTopOf="@+id/repository_name"
            app:layout_constraintVertical_bias="1.0" />

        <TextView
            android:id="@+id/repository_owner"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:layout_marginEnd="16dp"
            android:layout_marginStart="16dp"
            android:layout_marginTop="8dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/repository_name"
            app:layout_constraintVertical_bias="0.0"
            tools:text="Mladen Rakonjac" />

        <TextView
            android:id="@+id/number_of_starts"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:layout_marginEnd="16dp"
            android:layout_marginStart="16dp"
            android:layout_marginTop="8dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="1"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/repository_owner"
            app:layout_constraintVertical_bias="0.0"
            tools:text="0 stars" />

    </android.support.constraint.ConstraintLayout>

</layout>

Обратите внимание, что вам нужно переместить все xmlns в тег layout. Затем нажмите иконку Build или используйте сочетание клавиш Ctrl + F9 (Cmd + F9 на Mac). Нам нужно собрать проект, чтобы библиотека Data Binding могла сгенерировать класс ActivityMainBinding, который мы будем использовать в нашем классе MainActivity.

Если вы не выполните сборку проекта, вы не увидите класс ActivityMainBinding, потому что он генерируется во время компиляции. Мы все еще не закончили связывание, мы просто сказали, что у нас есть ненулевая переменная типа ActivityMainBinding. Кроме того, как вы можете заметить, я не указал ? в конце типа ActivityMainBinding, и я не инициализировал его. Как это возможно? Модификатор lateinit позволяет нам иметь ненулевые переменные, ожидающие инициализации. Подобно ButterKnife, инициализация привязки должна выполняться в методе onCreate, когда ваш Activity будет готов. Кроме того, вы не должны объявлять привязку в методе onCreate, потому что вы, вероятно, используете его вне области видимости метода onCreate. Наша привязка не должна быть нулевой, поэтому мы используем lateinit. Используя модификатор lateinit, нам не нужно проверять привязку переменной каждый раз, когда мы обращаемся к ней.

Давайте инициализируем нашу переменную binding. Вы должны заменить:

setContentView(R.layout.activity_main)

на:

binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

Вот и всё! Вы успешно привязали свои View. Теперь вы можете получить к ним доступ и применить изменения. Например, давайте изменим имя репозитория на «Modern Android Habrahabr Article»:

binding.repositoryName.text = "Modern Android Habrahabr Article"

Как вы можете видеть, мы можем получить доступ ко всем View (у которых есть id, конечно) из activity_main.xml через переменную binding. Вот почему Data Binding лучше, чем ButterKnife.

Getter’ы и Setter’ы в Котлине

Возможно, вы уже заметили, что у нас нет метода .setText (), как в Java. Я хотел бы остановиться здесь, чтобы объяснить, как геттеры и сеттеры работают в Kotlin по сравнению с Java.

Во-первых, вы должны знать, почему мы используем сеттеры и геттеры. Мы используем их, чтобы скрыть переменные класса и разрешить доступ только с помощью методов, чтобы мы могли скрыть элементы класса от клиентов класса и запретить тем же клиентам напрямую изменять наш класс. Предположим, что у нас есть класс Square в Java:

public class Square {
  private int a;
  
  Square(){
    a = 1;
  }

  public void setA(int a){
    this.a = Math.abs(a);
  }
  
  public int getA(){
    return this.a;
  }
  
}

Используя метод setA (), мы запрещаем клиентам класса устанавливать отрицательное значение стороне квадрата, оно не должно быть отрицательным. Используя этот подход, мы должны сделать a приватным, поэтому его нельзя установить напрямую. Это также означает, что клиент нашего класса не может получить a напрямую, поэтому мы должны предоставить getter. Этот getter возвращает a. Если у вас есть 10 переменных с аналогичными требованиями, вам необходимо предоставить 10 геттеров. Написание таких строк — это скучная вещь, в которой мы обычно не используем наш разум.

Kotlin облегчает жизнь нашего разработчика. Если вы вызываете

var side: Int = square.a

это не означает, что вы получаете доступ к a непосредственно. Это то же самое, что

int side = square.getA();

в Java. Причина заключается в том, что Kotlin автоматически генерирует геттеры и сеттеры по умолчанию. В Котлине, вы должны указать специальный сеттер или геттер, только если он у вас есть. В противном случае, Kotlin автогенерирует его для вас:

var a = 1
   set(value) { field = Math.abs(value) }

field? Что это? Чтобы было ясно, давайте посмотрим на этот код:

var a = 1
   set(value) { a = Math.abs(value) }

Это означает, что вы вызываете метод set внутри метода set, потому что нет прямого доступа к свойству в мире Kotlin. Это создаст бесконечную рекурсию. Когда вы вызываете a = что-то, он автоматически вызывает метод set.
Надеюсь, теперь понятно, почему вы должны использовать ключевое слово field и как работают сеттеры и геттеры.

Вернемся к нашему коду. Я хотел бы показать вам ещё одну замечательную особенность языка Kotlin, apply:

class MainActivity : AppCompatActivity() {

    lateinit var binding: ActivityMainBinding

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

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.apply {
            repositoryName.text = "Medium Android Repository Article"
            repositoryOwner.text = "Fleka"
            numberOfStarts.text = "1000 stars"
            
        }
    }
}

apply позволяет вам вызывать несколько методов на одном экземпляре.

Мы все еще не закончили привязку данных, есть ещё много дел. Давайте создадим класс модели пользовательского интерфейса для репозитория (этот класс модели пользовательского интерфейса для репозитория GitHub хранит данные, которые должны отображаться, не путайте их с паттерном Repository). Чтобы сделать класс Kotlin, вы должны перейти в New -> Kotlin File / Class:

class Repository(var repositoryName: String?,var repositoryOwner: String?,var numberOfStars: Int? ,var hasIssues: Boolean = false)

В Kotlin первичный конструктор является частью заголовка класса. Если вы не хотите предоставлять второй конструктор, это всё! Ваша работа по созданию класса завершена здесь. Нет параметров конструктора для назначений полей, нет геттеров и сеттеров. Целый класс в одной строке!

Вернитесь в класс MainActivity.kt и создайте экземпляр класса Repository:

var repository = Repository("Habrahabr Android Repository Article",
        "Fleka", 1000, true)

Как вы можете заметить, для построения объекта не нужно ключевого слова new.

Теперь перейдем к activity_main.xml и добавим тег data:

<data>
      <variable
        name="repository"
        type="me.fleka.modernandroidapp.uimodels.Repository"
        />
</data>

Мы можем получить доступ к переменной repository, которая является типом Repository в нашем макете. Например, мы можем сделать следующее в TextView с идентификатором repository_name:

android:text="@{repository.repositoryName}"

В TextView repository_name будет отображаться текст, полученный из свойства repositoryName переменной repository. Остается только связать переменную репозитория от xml до repository из MainActivity.kt.
Нажмите Build, чтобы сгенерировать библиотеку привязки данных для создания необходимых классов, вернитесь в MainActivity и добавить две строки:

binding.repository = repository
binding.executePendingBindings()

Если вы запустите приложение, вы увидите, что в TextView появится «Habrahabr Android Repository Article». Хорошая функция, да? :)

Но что произойдёт, если мы сделаем следующее:

Handler().postDelayed({repository.repositoryName="New Name"}, 2000)

Отобразится ли новый текст через 2 секунды? Нет, не отобразится. Вы должны заново установить значение repository. Что-то вроде этого будет работать:

Handler().postDelayed({repository.repositoryName="New Name"
    binding.repository = repository
    binding.executePendingBindings()}, 2000)

Но это скучно, если нужно будет делать это каждый раз, когда мы меняем какое-то свойство. Существует лучшее решение, называемое Property Observer.
Давайте сначала опишем, что такое паттерн Observer, нам понадобится это в разделе rxJava:

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

Это один из примеров паттерна Observer / Observable. В данном случае, Android Weekly — наблюдаемый (Observable), он выпускает информационные бюллетени каждую неделю. Читатели — это наблюдатели (Observers), они подписываются на него, ждут новых выпусков, и, как только они получают её, они читают её, и если некоторые из них решат, что им это не нравится, он / она может прекратить следить.

Property Observer, в нашем случае, представляет собой XML-макет, который будет прослушивать изменения в экземпляре Repository. Таким образом, Repository является наблюдаемым. Например, как только свойство name класса Repository изменяется в экземпляре класса, xml должен обновится без вызова:

binding.repository = repository
binding.executePendingBindings()

Как сделать это с помощью библиотеки привязки данных? Библиотека привязки данных предоставляет нам класс BaseObservable, который должен быть реализован в классе Repository:

class Repository(repositoryName : String, var repositoryOwner: String?, var numberOfStars: Int?
                 , var hasIssues: Boolean = false) : BaseObservable(){

    @get:Bindable
    var repositoryName : String = ""
    set(value) {
        field = value
        notifyPropertyChanged(BR.repositoryName)
    }

}

BR — это класс, который автоматически генерируется один раз, когда используется аннотация Bindable. Как вы можете видеть, как только новое значение установлено, мы узнаём об этом. Теперь вы можете запустить приложение, и вы увидите, что имя репозитория будет изменено через 2 секунды без повторного вызова функции executePendingBindings ().

Для этой части это всё. В следующей части я напишу о паттерне MVVM, паттерне Repository и об Android Wrapper Managers. Вы можете найти весь код здесь. Эта статья охватывает код до этого коммита.

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

Вы уже использовали Kotlin в разработке под Android?


56.58%
Нет, пока только читал
172


2.63%
Впервые попробовал в этом проекте и пока не решил, использовать ли его
8


3.62%
Да, но решил остаться на Java
11


12.83%
Да, использую вместе с Java
39


24.34%
Да, теперь пишу только на нём
74

Проголосовали 304 пользователя.

Воздержались 45 пользователей.

Improve Article

Save Article

Like Article

  • Read
  • Discuss
  • Improve Article

    Save Article

    Like Article

    Kotlin is a statically typed, cross-platform, general-purpose programming language for JVM developed by JetBrains. This is a language with type inference and fully interoperable with Java. Kotlin is concise and expressive programming as it reduces the boilerplate code. Since Google I/O 2019, Android development has been Kotlin-first. Kotlin is seamlessly integrated with the Android Studio and many companies are moving the entire code base from Java to Kotlin. Asynchronous tasks are seamlessly implemented in Kotlin using coroutines. So here’s the complete guide to learn Kotlin, specifically for Android Application Development.

    Learn Kotlin For Android App Development

    So in this article, we have covered the following things:

    1. Basics Of Kotlin Programming Language
    2. Conditional Statements(Control Flow) in Kotlin
    3. Functional Programming in Kotlin
    4. Collections in Kotlin Programming Language
    5. Object-Oriented Programming Concepts of Kotlin
    6. Kotlin Exception Handling
    7. Kotlin Null Safety
    8. Kotlin Scope Functions
    9. Kotlin Interoperability with Java
    10. Kotlin Coroutines
    11. Miscellaneous
    12. Complete Kotlin Tutorial

    Step-by-Step Guide to Learn Kotlin for Android App Development

    Basics Of Kotlin Programming Language

    • Introduction to Kotlin – Introductory discussion about the Kotlin Programming language. Contains advantages and application of the Kotlin programming language.
    • Kotlin Environment setup with Intellij IDEA – Setup IDE and get ready to start the programming journey with Kotlin.
    • Hello World program in Kotlin – Get to know the first “Hello World” program in Kotlin. It’s simple.
    fun main(args: Array<String>) {
        println("Hello, World!")
    }
    • Kotlin Data Types – Fundamental data type in Kotlin is a Primitive data type and all others are reference types like array and string.
    • Kotlin Variables – The mutable and immutable data types in Kotlin.

    Conditional Statements(Control Flow) in Kotlin

    • Kotlin if-else expression – Basic if-else control flow statement in Kotlin. Get to know if, if-else, if-else-if ladder, nested-if.
    • Kotlin when expression – when it replaces the switch operator of other languages like Java. We do not require a break statement at the end of each case.
    • Kotlin while loop – while loop in Kotlin.
    • Kotlin do-while loop – do-while loop in Kotlin.
    • Kotlin for loop – In Kotlin, for loop is equivalent to foreach loop.

    Functional Programming in Kotlin

    • Kotlin functions – Basics of functions and their declaration in Kotlin.
    • Kotlin | Default and Named argument – Make the parameters of the function optional. i.e pass an argument or not while calling a function.
    • Lambdas Expressions and Anonymous Functions – Syntax of Kotlin lambdas is similar to Java Lambdas. A function without a name is called an anonymous function.
    • Kotlin Inline functions – Interesting inline keyword which ultimately requests the compiler to not allocate memory and simply copy the inlined code of that function at the calling place.
    • Kotlin infix function notation – A function marked with infix keyword can also be called using infix notation, which means calling without using parenthesis and dots.
    • Kotlin Higher-Order Functions – Kotlin functions can be stored in variables and data structures, passed as arguments to, and returned from other higher-order functions.

    Collections in Kotlin Programming Language

    • Kotlin Collections – A collection usually contains a number of objects of the same type and these objects in the collection are called elements or items.
    • Kotlin list : Arraylist – Dynamic array states that we can increase or decrease the size of an array as pre-requisites.
    • Kotlin list : listOf() – Kotlin has two types of lists, immutable lists (cannot be modified) and mutable lists (can be modified).
    • Kotlin Set : setOf() – Kotlin Set interface is a generic unordered collection of elements, and it does not contain duplicate elements.
    • Kotlin mutableSetOf() – setOf() is immutable, meaning it supports only read-only functionalities and mutableSetOf() is mutable, meaning it supports both read and write.
    • Kotlin hashSetOf() It implements the set interface. hashSetOf() is a function that returns a mutable hashSet, which can be both read and written.
    • Kotlin Map : mapOf() – Map holds the data in the form of pairs which consist of a key and a value.
    • Kotlin Hashmap – Kotlin Hash Table-based implementation of the MutableMap interface. It stores the data in the form of key and value pairs.

    Object-Oriented Programming Concepts of Kotlin

    • Kotlin Class and Objects – Class and Objects are the basic concepts of an object-oriented programming language. These support the OOPs concepts of inheritance, abstraction, etc.
    • Kotlin Nested class and Inner class – A class is declared within another class, then it is called a nested class.
    • Kotlin Setters and Getters – setter is used to set the value of any variable and getter is used to get the value. Getter and Setter are auto-generated in the code.
    • Kotlin | Class Properties and Custom Accessors – So, accessor methods – a getter and a setter are provided to let the clients of the given class access the data.
    • Kotlin constructor –  The primary constructor initializes the class, while the secondary constructor is used to initialize the class and introduce some extra logic.
    • Kotlin Visibility Modifiers – Visibility modifiers are used to restrict the accessibility of classes, objects, interfaces, constructors, functions, properties, and their setters to a certain level.
    • Kotlin Inheritance – Inheritance enables code re-usability. It allows inheriting the features from an existing class(base class) to a new class(derived class).
    • Kotlin Interfaces – Interfaces are custom types provided by Kotlin that cannot be instantiated directly.
    • Kotlin Data Classes – We often create classes to hold some data in them. In such classes, some standard functions are often derivable from the data.
    • Kotlin Sealed Classes – As the word sealed suggests, sealed classes conform to restricted or bounded class hierarchies.
    • Kotlin Abstract class – An abstract class can not instantiate. It means we can not create objects for the abstract class.
    • Enum classes in Kotlin – enum has its own specialized type, indicating that something has a number of possible values. Unlike Java enums, Kotlin enums are classes.
    • Kotlin extension function – When a function is added to an existing class it is known as an Extension Function.
    • Kotlin generics – Allow defining classes, methods, and properties that are accessible using different data types while keeping a check of the compile-time type safety.

    Kotlin Exception Handling

    • Kotlin Exception Handling | try, catch, throw and finally – An exception is an unwanted or unexpected event that occurs during the execution of a program i.e at run time.
    • Kotlin Nested try block and multiple catch block – The requirement of nested try-catch block arises when an exception occurs in the inner try-catch block is not handled by the inner catch blocks.

    Kotlin Null Safety

    • Kotlin Null Safety – Kotlin’s type system is aimed to eliminate the jeopardy of null reference from the code because it is a billion-dollar mistake.
    • Kotlin | Type Checking and Smart Casting – It is a way of checking the type of the variable at runtime.
    • Kotlin | Explicit type casting – Kotlin also provides a facility of typecasting using safe cast operator as. If casting is not possible, it returns null instead of throwing a ClassCastException exception.

    Kotlin Scope Functions

    • Kotlin – Scope Function – We can access the object of these functions without its name.
    • Kotlin | apply vs with – Difference between most confusing Scope functions, get to know with examples.

    Kotlin Interoperability with Java

    • Java Interoperability – Calling Kotlin from Java – When Kotlin was developed, it worked solely on JVM, hence it provides a complete set of features that makes calling Kotlin from Java quite easy.
    • Java Interoperability – Calling Java from Kotlin – The getter and setter of all the types defined within the Java class are represented as properties in the Kotlin.

    Kotlin Coroutines

    • Suspend Function In Kotlin Coroutines – Suspend function is a function that can be started, paused, and resumed.
    • runBlocking in Kotlin Coroutines with Example – runBlocking is a coroutine function. By not providing any context, it will get run on the main thread.
    • Jobs, Waiting, Cancellation in Kotlin Coroutines – How to wait for the coroutine, and how to cancel the coroutine. Whenever a new coroutine is launched, it will return a job.
    • Launch vs Async in Kotlin Coroutines – Creating coroutines doesn’t allocate new threads. Instead, they use predefined thread pools and smart scheduling for the purpose of which task to execute next and which tasks later.
    • withContext in Kotlin Coroutines – withContext is nothing but another way of writing the async where one does not have to write await().

    Miscellaneous

    • Kotlin annotations – Annotations are a feature of Kotlin that allows the programmer to embed supplemental information into the source file.
    • Kotlin Reflection – Along with Java reflection API, Kotlin also provides its own set of reflection API, in a simple functional style.
    • Delegation in Kotlin – Delegation controls the allocation of power/authority from one instance to another for any object.
    • Delegated Properties in Kotlin – Delegation is defined as the granting of any authority or power to another person (Boss assigning tasks to its employees) to carry out different work.

    For a complete Kotlin Tutorial, you may refer to this article: Kotlin Programming Language 

    Last Updated :
    07 Jul, 2021

    Like Article

    Save Article

    Создание вашего первого кроссплатформенного мобильного приложения

    Здесь вы узнаете, как создать и запустить ваше первое Kotlin Multiplatform Mobile приложение.

    Вы также можете посмотреть видео этого урока, созданное Екатериной Петровой.

    1. Настройте среду для кроссплатформенной мобильной разработки, установив необходимые
      инструменты для подходящей операционной системы.

      > Вам понадобится Mac с macOS для выполнения определенных шагов в этом руководстве, которые включают написание
      > специфичного для iOS кода и запуск iOS-приложений.
      >
      > Работать с iOS в других операционных системах, таких как Microsoft Windows, нельзя. Это связано с требованием
      > Apple.

    2. В Android Studio выберите File | New | New Project.

    3. Выберите Kotlin Multiplatform App в списке шаблонов проектов и нажмите Next.

      Шаблон мобильного мультиплатформенного проекта

    4. Укажите имя для вашего первого приложения и нажмите Next.

      Мобильный мультиплатформенный проект - основные настройки

    5. В открывшемся окне выполните следующие действия:

      • Не изменяйте имена по умолчанию для приложения и общих папок;
      • Установите флажок, чтобы сгенерировать образцы тестов для вашего проекта;
      • Выберите Regular framework в списке вариантов распространения iOS-фреймворка.

      Нажмите Finish, чтобы создать новый проект.

      Мобильный мультиплатформенный проект - дополнительные настройки

      > Если вы хотите использовать мультиплатформенный модуль Kotlin в качестве зависимости CocoaPods, выберите опцию
      > CocoaPods dependency manager. Чтобы узнать больше о зависимостях CocoaPods, см. раздел Интеграция CocoaPods.

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

    Чтобы просмотреть полную структуру вашего мобильного мультиплатформенного проекта, переключите просмотр с Android на
    Project. Вы можете разобраться со структурой проекта и
    тем, как вы можете её использовать.

    Выбор вида проекта

    Запуск вашего приложения

    Вы можете запустить свое мультиплатформенное приложение на Android или iOS.

    Запуск вашего приложения на Android

    • В списке конфигураций запуска выберите androidApp и нажмите Run.

      Запуск мультиплатформенного приложения на Android

      Первое мультиплатформенное приложение на Android

    Запуск эмулятора другого Android-устройства

    Узнайте, как настроить эмулятор Android и запустить ваше приложение на другом моделируемом устройстве.

    Запуск на реальном Android-устройстве

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

    Запуск вашего приложения на iOS

    • В списке конфигураций запуска выберите iosApp и нажмите Run.

      Запуск мультиплатформенного приложения на iOS

      Первое мультиплатформенное приложение на iOS

    Запуск эмулятора другого iPhone

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

    1. В списке конфигураций запуска нажмите Edit Configurations.

      Редактирование конфигурации запуска

    2. Нажмите кнопку + над списком конфигураций и выберите iOS Application.

      Новая конфигурация запуска для iOS-приложения

    3. Укажите название вашей конфигурации.

    4. Выберите симулируемое устройство в списке Execution target, а затем нажмите OK.

      Новая конфигурация запуска с симулятором iOS

    5. Нажмите Run, чтобы запустить ваше приложение на новом симулируемом устройстве.

    Запуск на реальном iPhone

    1. Подключите iPhone к Xcode.
    2. Создайте конфигурацию запуска, выбрав iPhone в списке Execution
      target
      .
    3. Нажмите Run, чтобы запустить ваше приложение на iPhone.

    Если ваша сборка завершится неудачей, следуйте инструкциям, описанным в этой issue.

    Запуск тестов

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

    Запуск тестов на iOS

    1. Откройте файл iosTest.kt в папке shared/src/iosTest/kotlin/com.example.kmmapplication.shared. Каталоги с именем
      Test содержат тесты. Этот файл содержит пример теста для iOS.

      Тестовый Kotlin-файл для iOS

    2. Нажмите на значок Run рядом с тестом.

    Тесты выполняются на симуляторе без UI. Поздравляем! Тест пройден – смотрите результаты теста в консоли.

    Результаты iOS-теста

    Запуск тестов на Android

    Для Android процесс запуска тестов очень похож на этот же процесс для iOS.

    1. Откройте файл androidTest.kt в папке shared/src/androidTest/kotlin/com.example.kmmapplication.shared.

    2. Нажмите на значок Run рядом с тестом.

    Обновление вашего приложения

    1. Откройте файл Greeting.kt в папке shared/src/commonmain/kotlin/com.example.kmmapplication.shared. В этом каталоге
      хранится общий код для обеих платформ – Android и iOS. Если вы внесете изменения в общий код, вы увидите изменения в
      обоих приложениях.

      Общий Kotlin-файл

    2. Обновите общий код: используйте функцию стандартной Kotlin-библиотеки, которая работает на всех платформах и
      возвращает текст: reversed().

      `kotlin
      class Greeting {

       fun greeting(): String {
           return "Guess what it is! > ${Platform().platform.reversed()}!"
       }
      

      }
      `

    3. Запустите обновленное приложение на Android.

      Обновленное мобильное мультиплатформенное приложение на Android

    4. Запустите обновленное приложение на iOS.

      Обновленное мобильное мультиплатформенное приложение на iOS

    5. Запустите тесты на Android и iOS.

      Как вы видите, тесты проваливаются. Обновите тесты для прохождения. Вы знаете, как это сделать, верно? ;)

      iOS-тест провалился

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

    Как только вы поиграете со своим первым кроссплатформенным мобильным приложением, вы сможете:

    • Понимать структуру проекта,
    • Завершите руководство о запуске вашего Android-приложение на iOS.

    Введение в Android

    Оглавление

    • Скачивание и установка Android Studio
    • Создание первого приложения
    • Установка эмулятора
    • Подключение реального устройства
    • Запуск приложения на эмуляторе и устройстве
    • Структура проекта
    • Приложение Dice Roller
      • Добавление кнопки
      • Обработка нажатия на кнопку
      • Добавление изображения костей
    • Сборка и управление зависимостями

    Скачивание и установка Android Studio

    Android Studio — специальная среда разработки под Android. Основана на IntelliJ IDEA, поэтому пользовательский интерфейс очень похож.

    Скачать Android Studio можно с официального сайта для разработчиков под Android: developer.android.com: https://developer.android.com/studio.

    Устанавливается стандартным образом для каждой из поддерживаемых операционных систем (Windows, Linux, macOS).
    Инструкцию можно найти здесь: https://developer.android.com/studio/install.

    Создание первого приложения

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

    Запустить Android Studio -> «Start a new Android Studio project».
    Choose your project -> Empty Activity -> Next.

    Configure your project ->
    Name: Dice Roller
    Package: com.example.android.diceroller
    Project location: путь до каталога с проектом в файловой системе
    Language: Kotlin
    Minimum API: минимальный уровень API (версия Android)
    -> Finish

    Проект нового приложения создан. Для его запуска необходимо либо подключить реальное Android-устройство, либо установить и запустить эмулятор.

    Установка эмулятора

    Для установки эмулятора необходимо выполнить следующие шаги в рамках Android Studio:

    Tools -> AVD Manager -> Create Virtual Device.
    Choose a device definition -> Pixel -> Next.
    Select a system image -> Pie -> Next.
    Verify Configuration -> AVD Name -> pixel2-api28 -> Finish.

    Эмулятор создан. Можно его запустить, нажав на кнопку «Run» в окне «AVD Manager».

    Подключение реального устройства

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

    1. Включить отладку по USB:
    2. Включить режим разработчика: Настройки -> О телефоне -> Номер сборки (нажать 5 раз). Шаги могут отличаться в зависимости от модели устройства и версии Android. В настройках появится пункт «Для разработчика».
    3. Перейти в раздел настроек «Для разработчиков» и включить «Отладка по USB».
    4. Подключить устройство к компьютеру по кабелю.
    5. Подтвердить разрешение на отладку по USB на устройстве.

    Запуск приложения на эмуляторе и устройстве

    Для запуска приложения необходимо сперва выбрать устройство (слева от кнопки «Run»), на котором будет выполнен запуск, а затем нажать кнопку «Run».

    Структура проекта

    Изучим структуру проекта приложения.
    Вкладка «Project» на панели слева отображает структуру проекта. Режим «Android» не отображает реальную структуру проекта, но он удобен для разработки. Режим «Project» отображает реальную структуру проекта в файловой системе.

    Рассмотрим структуру проекта в режиме «Android».
    На верхнем уровне проект отображает свои модули и список Gradle-скриптов. Проект может содержать более, чем один модуль, но по-умолчанию у него есть хотя бы один модуль app с исходным кодом приложения. Gradle-скрипты — это сборочные скрипты проекта, описывающие этапы сборки, зависимости и другую необходимую для сборки проекта информацию. О них позже.

    Модуль app содержит разделы:

    • manifests — содержит файл манифеста приложения AndroidManifest.xml,
    • java — содержит исходный код приложения на языке Java или Kotlin,
    • res — содержит ресурсы приложения, такие как изображения, иконки, переводимые строки, файлы разметки интерфейса и др.

    Манифест AndroidManifest.xml располагается в корневом каталоге проекта. Он содержит важную информацию о приложении, которая требуется системе Android для выполнения какого-либо кода приложения. Среди прочего файл манифеста выполняет следующее:

    • Задает имя пакета для приложения. Имя пакета служит его уникальным идентификатором в системе Android и в магазине Google Play.
    • Описывает различные компоненты приложения: активности (Activities), службы (Services), провайдеры контента (Content Providers) и др. На основании этих описаний система Android может определить, из каких компонентов состоит приложение и при каких условиях их можно использовать.
    • Объявляет какие разрешения приложение может запрашивать у пользователя, чтобы приложение могло получить доступ к защищенным API системы. Например, это могут быть разрешения на чтение контактов, текущего местоположения пользователя, или доступ к файловой системе.
    • Объявляет минимальный уровень API (версию) Android, который требуется приложению.

    Раздел (или каталог) java содержит исходный код приложения. При создании нового проекта с пустой активностью создается лишь один файл исходного кода — класс MainActivity, содержащий код единственного экрана приложения. Класс MainActivity по-умолчанию содержит лишь один переопределенный метод onCreate(), наследованный от класса AppCompatActivity.

    class MainActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
        }
    }

    Вызов setContentView() как ясно из названия метода, устанавливает контент для отображения на экране для данной активности. В качестве параметра в метод передается ссылка на ресурс с описанием разметки экрана: activity_main.xml.

    Ресурсы с разметкой экранов располагаются в каталоге layout. Это могут быть ресурсы с разметкой не только экранов, но каких-либо других UI-элементов.
    Кроме этого среди ресурсов могут содержаться изображения (drawable), иконки запуска (mipmap), определения цветов (values/colors.xml), переводимые строки (values/strings.xml), определения тем и стилей элементов приложения (values/styles.xml).

    Список Gradle-скриптов содержит конфигурационные файлы Gradle build.gradle. Один файл принадлежит всему проекту и описывает конфигурацию сборки проекта: репозитории зависимостей, задача очистки проекта. Второй файл принадлежит модулю app и описывает конфигурацию сборки конкретного модуля: описание параметров системы Android, зависимости от библиотек. Если проект содержит более, чем один модуль, то и Gradle-файлов он содержит больше — по одному на каждый модуль.

    Приложение Dice Roller

    В качестве простейшего примера создадим приложение «Dice Roller», которое будет иметь один единственный экран с изображением игрального кубика и кнопкой для имитации броска.

    Заготовка для приложения уже создана. Далее необходимо:

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

    Готовый пример располагается в каталоге рядом с данным конспектом.

    Добавление кнопки

    Игральный кубик содержат точки в диапазоне от 1 до 6. Таким образом кнопка для имитации броска должна генерировать случайное число от 1 до 6.

    Сперва рассмотрим добавление кнопки без добавления изображения, а для проверки работы кнопки необходимо подготовить текстовое поле для отображения числа от 1 до 6.

    1. Изменение текста и его размера на экране (activity_main.xml):

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        tools:context=".MainActivity">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:textSize="30sp"
            android:text="1" />
    
    </LinearLayout>

    2. Добавление кнопки (тега Button):

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="@string/roll" />

    Параметр layout_gravity=center_horizontal позволяет установить кнопку в центре по горизонтали.
    Параметр text содержит ссылку на строковый ресурс с именем roll. Ресурс содержит строку «Roll».

    3. Расположение элементов в центре экрана:

    Для расположения элементов в центре экрана необходимо добавить следующие параметры к тегу LinearLayout.
    Параметр layout_gravity="center_vertical" располагает компонент LinearLayout в центре по вертикали экрана.
    Параметр orientation определяет ориентацию расположения элементов внутри LinearLayout.

    android:layout_gravity="center_vertical"
    android:orientation="vertical"

    Обработка нажатия на кнопку

    Для того, чтобы нажатие на кнопку броска имело какой-нибудь эффект, необходимо добавить для кнопки обработчик события нажатия на нее. Для этого необходимо задать уникальный идентификатор для кнопки, чтобы можно было получить ее экземпляр, и установить обработчик события onClick.

    1. Установка идентификатора кнопки:

    android:id="@+id/roll_button"

    2. Получение экземпляра кнопки по ее идентификатору:

    val rollButton: Button = findViewById(R.id.roll_button)

    3. Установка обработчика события onClick:

    rollButton.setOnClickListener {
        val randomInt = Random().nextInt(6) + 1
        val resultText: TextView = findViewById(R.id.result_text)
        resultText.text = randomInt.toString()
    }

    Обработчик нажатия на кнопку описывает генерацию случайного числа от 1 до 6, получение экземпляра текстового поля по идентификатору result_text (предварительно идентификатор должен быть добавлен к компоненту TextView), и установку сгенерированного числа в качестве текста.

    Если запустить приложение, можно убедиться, что нажатие на кнопку «Roll» меняет значение числа на экране.

    Добавление изображения костей

    Для завершения приложение не хватает отображения картинки с игральным кубиком вместо обычного числа. Для добавления изображения необходимо: добавить заранее подготовленные файлы изображений в каталог ресурсов drawable, заменить компонент для отображения текста TextView на компонент ImageView, обновить обработчик нажатия на кнопку «Roll» для смены изображения в зависимости от сгенерированного числа.

    1. Добавление изображений в каталог drawable:

    Скачать файлы с изображениями игральной кости по ссылке https://github.com/udacity/andfun-kotlin-dice-roller/raw/master/DiceImages.zip, и добавить файлы в каталог res/drawable.

    2. Замена TextView на ImageView:

    <ImageView
        android:id="@+id/dice_image"
        android:layout_width="wrap_content"
    	android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:src="@drawable/empty_dice" />

    Компонент ImageView устанавливает изображение с пустым кубиком в параметре src. Для ImageView установлен идентификатор dice_image.

    3. Замена получения экземпляра TextView на ImageView:

    val diceImage: ImageView = findViewById(R.id.dice_image)

    4. Обновление обработчика нажатия на кнопку «Roll»:

    По нажатию на кнопку «Roll» необходимо отобразить картинку игрального кубика со сгенерированным числом точек. Для этого необходимо сперва определить правильную ссылку на изображение среди ресурсов с помощью конструкции when. После этого необходимо установить полученную ссылку на ресурс для объекта ImageView с помощью метода setImageResource().

    rollButton.setOnClickListener {
        val drawableRes = when (Random.nextInt(1, 6)) {
            1 -> R.drawable.dice_1
            2 -> R.drawable.dice_2
            3 -> R.drawable.dice_3
            4 -> R.drawable.dice_4
            5 -> R.drawable.dice_5
            else ->  R.drawable.dice_6
        }
        diceImage.setImageResource(drawableRes)
    }

    Сборка и управление зависимостями

    Сборкой Android-приложений занимается утилита Gradle. Кроме сборки Gradle отвечает за следующее:

    • Определяет какие устройства, могут запустить данное приложение.
    • Компилирует код в исполняемый файл приложения.
    • Управляет зависимостями приложения.
    • Подписывает приложения специальными ключами для публикации в Google Play.
    • Собирает и запускает автоматические тесты.

    Главная задача Gradle — сборка проекта. Gradle в процессе сборки компилирует исходный код (*java— и *kt-файлы), берет файлы ресурсов, скомпилированный код, манифест (AndroidManifest.xml), внешние использующиеся библиотеки, если они есть, и упаковывает все эти файлы в один APK-файл (Android Application Package) — исполняемый формат файлов для распространения Android-приложений.

    В каждом проекте располагается как минимум два конфигурационных файла Gradle. Каждый обладает именем build.gradle. Один из файлов располагается в корне проекта и является конфигурационным файлом всего проекта. Второй располагается в корне модуля app и является соответственно конфигурационным файлов модуля. Если проект содержит больше чем 1 модуль, то и Gradle-файлов модулей больше — по одному на каждый модуль.

    Gradle-файл проекта определяет репозитории и зависимости общие для всех модулей в проекте.
    Репозитории — хранилища, в которых будут искаться добавляемые зависимости.
    Зависимости — библиотеки или инструменты, которые необходимы для работы проекта.

    Также часть конфигурации проекта располагается в файле settings.gradle.

    Код ниже демонстрирует файл проекта settings.gradle, создающийся по-умолчанию. Здесь описаны репозитории google(), mavenCentral() и gradlePluginPortal(), использующиеся для доступа к плагинам и зависимостям во всех модулях проекта.

    pluginManagement {
        repositories {
            google()
            mavenCentral()
            gradlePluginPortal()
        }
    }
    dependencyResolutionManagement {
        repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
        repositories {
            google()
            mavenCentral()
        }
    }
    rootProject.name = "My Application"
    include ':app'

    Блок pluginManagement {} описывает список репозиториев для загрузки плагинов для использования в рамках проекта.
    Блок dependencyResolutionManagement {} описывает список репозиториев для загрузки библиотек зависимостей для использования в рамках проекта и его модулей.
    Также здесь описывается имя проекта rootProject.name и список модулей. В данном примере лишь один модуль :app, но если бы их было больше, они бы были перечислены через запятую.

    Пример файла build.gradle проекта описывает объявления плагинов для сборки и разработки.

    // Top-level build file where you can add configuration options common to all sub-projects/modules.
    plugins {
        id 'com.android.application' version '7.4.0' apply false
        id 'com.android.library' version '7.4.0' apply false
        id 'org.jetbrains.kotlin.android' version '1.8.20-Beta' apply false
    }

    Пример build.gradle для модуля app представлен ниже.

    plugins {
        id 'com.android.application'
        id 'org.jetbrains.kotlin.android'
    }
    
    android {
        namespace 'com.example.myapplication'
        compileSdk 33
    
        defaultConfig {
            applicationId "com.example.myapplication"
            minSdk 24
            targetSdk 33
            versionCode 1
            versionName "1.0"
    
            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        }
    
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            }
        }
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
        kotlinOptions {
            jvmTarget = '1.8'
        }
        buildFeatures {
            viewBinding true
        }
    }
    
    dependencies {
        implementation 'androidx.core:core-ktx:1.7.0'
        implementation 'androidx.appcompat:appcompat:1.4.1'
        implementation 'com.google.android.material:material:1.5.0'
        implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
        implementation 'androidx.navigation:navigation-fragment-ktx:2.4.1'
        implementation 'androidx.navigation:navigation-ui-ktx:2.4.1'
        implementation 'androidx.core:core-ktx:+'
        testImplementation 'junit:junit:4.13.2'
        androidTestImplementation 'androidx.test.ext:junit:1.1.3'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    }

    Блок plugins описывает плагины, которые должны быть включены в проект:

    • com.android.application — объявляет проект как Android-приложение.
    • kotlin-android — включает использование языка Kotlin для Android-проекта.

    В более старых версиях Gradle плагины добавлялись не целым блоком, а каждый отдельно с помощью директивы apply plugin. Например,

    apply plugin: 'com.android.application'
    

    Блок android описывает параметры сборки проекта под Android:

    • namespace — уникальный идентификатор пакета приложения для публикации в системе Android.
    • compileSdk — уровень Android API для компиляции. Должно совпадать с targetSdkVersion.
    • minSdk — минимальный уровень Android API требуемый для работы приложения.
    • targetSdk — целевой уровень Android API требуемый для работы приложения.
    • versionCode — кодовое число версии приложения. Используется для идентификации версии приложения в системе Android.
    • versionName — текстовое название версии приложения. Версия, которая показывается пользователю.
    • buildTypes — блок, описывающий параметры сборки и цифровой подписи приложения.
    • dependencies — блок, описывающий зависимости от библиотек, необходимых проекту. Зависимости задаются идентификаторами библиотек в репозиториях, включенных в Gradle-файле проекта.

    Russian (Pусский) translation by Anna k.Ivanova (you can also view the original English article)

    Введение

    Kotlin, язык программирования с открытым исходным кодом, разработанный JetBrains, становится все более популярным среди разработчиков Java. Он часто рекламируется как преемник Java. По сравнению с Java, он предлагает более богатый опыт разработки, потому что он более современный, выразительный и лаконичный.

    Если вы ищете альтернативный язык программирования для разработки Android, вы должны попробовать Kotlin. Его можно легко использовать вместо или в сочетании с Java в ваших проектах Android.

    В этом уроке я покажу вам, как использовать плагины Kotlin и Kotlin в ваших проектах Android Studio.

    Предпосылки

    Чтобы следовать за мной, вам нужно:

    • последняя версия Android Studio
    • базовое понимание синтаксиса Kotlin

    Если вы не знакомы с языком программирования Kotlin, я рекомендую прочитать раздел «Начало работы» секции Kotlin, чтобы познакомиться с языком.

    1. Установка плагинов Kotlin

    В меню быстрого запуска Android Studio выберите Настройка> Плагины.

    Configure PluginsConfigure PluginsConfigure Plugins

    На следующем экране нажмите Установить плагин JetBrains.

    Install JetBrains pluginInstall JetBrains pluginInstall JetBrains plugin

    Выберите Kotlin Extensions для Android из списка и нажмите Установить плагин справа.

    Install Kotlin Extensions For AndroidInstall Kotlin Extensions For AndroidInstall Kotlin Extensions For Android

    Поскольку плагин зависит от плагина Kotlin, вас попросят установить оба. Нажмите Да, чтобы начать установку.

    Plugin Dependencies DetectedPlugin Dependencies DetectedPlugin Dependencies Detected

    По завершении установки перезапустите Android Studio, чтобы активировать плагины.

    2. Создание Kotlin Activity

    В Android Studio щелкните правой кнопкой мыши имя вашего пакета и выберите Создать> Файл Kotlin.

    New Kotlin FileNew Kotlin FileNew Kotlin File

    В появившемся диалоговом окне введите имя нового Activity и выберите Класс в раскрывающемся списке. Я назвал свой класс MainActivity.

    New Kotlin file dialogNew Kotlin file dialogNew Kotlin file dialog

    Как только класс будет создан, вы увидите предупреждение с просьбой настроить модуль приложения для поддержки Kotlin.

    Configure Kotlin alertConfigure Kotlin alertConfigure Kotlin alert

    Нажмите ссылку в предупреждении и в появившемся диалоговом окне нажмите OK, чтобы выбрать значения по умолчанию.

    Configure Kotlin dialogConfigure Kotlin dialogConfigure Kotlin dialog

    Чтобы настроить проект для поддержки Kotlin, плагин Kotlin вносит несколько изменений в файл build.gradle. Примените эти изменения, нажав кнопку Синхронизировать сейчас, как показано ниже.

    Sync Now pop upSync Now pop upSync Now pop up

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

    3. Использование Kotlin

    Чтобы упростить пример, я покажу вам, как создать Activity с помощью одного TextView, который отображает String.

    Убедитесь, что ваш класс является подклассом Activity и переопределяет его метод onCreate. Конечно, вы должны сделать это в Kotlin. Если вы новичок в Kotlin, я предлагаю вам использовать функциональность генерации кода Android Studio, нажав Control + O, чтобы получить сигнатуры метода справа.

    Override Members dialogOverride Members dialogOverride Members dialog

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

    1
    package com.hathy.kotlinsample
    
    2
    
    
    3
    import android.app.Activity
    
    4
    import android.os.Bundle
    
    5
    
    
    6
    public class MainActivity: Activity() {
    
    7
        override fun onCreate(savedInstanceState: Bundle?) {
    
    8
            super.onCreate(savedInstanceState)
    
    9
        }
    
    10
    }
    

    Создайте экземпляр TextView как локальную переменную assign-once, используя ключевое слово val.

    1
    val myMessage = TextView(this)
    

    Вызовите ее метод setText, чтобы установить String, который хотите отобразить, и затем вызовите setContentView для отображения текстового вида.

    1
    myMessage.setText("Hello")
    
    2
    setContentView(myMessage)
    

    Как и для Java-Activity, вам нужно объявить свою Kotlin-Activity в AndroidManifest.xml вашего приложения, чтобы она была распознана системой Android. Если это единственный Activity в вашем проекте, ваш файл манифеста должен выглядеть так:

    1
    <activity android:name=".MainActivity">
    
    2
        <intent-filter>
    
    3
            <action android:name="android.intent.action.MAIN"/>
    
    4
            <category android:name="android.intent.category.LAUNCHER"/>
    
    5
        </intent-filter>
    
    6
    </activity>
    

    Теперь вы можете скомпилировать и запустить приложение на своем Android-устройстве или эмуляторе. Хотя компилятор Kotlin немного медленнее, чем у Java, вы вряд ли увидите существенные изменения во времени сборки вашего проекта.

    4. Использование Kotlin Android Extensions

    Плагин Kotlin Android Extensions позволяет обрабатывать виджеты, которые вы определяете в XML-макете Activity, как если бы они были свойствами этой Activity. Другими словами, если вы используете этот плагин, вам никогда не придется вызывать findViewById. Эти свойства называются синтетическими свойствами.

    Чтобы использовать эту функцию в своем приложении, вам нужно добавить org.jetbrains.kotlin: kotlin-android-extensions как зависимость скрипта сборки в модуле build.gradle вашего приложения, как показано ниже. Не забудьте нажать кнопку Синхронизировать сейчас, чтобы синхронизировать ваш проект.

    1
    buildscript {
    
    2
        dependencies {
    
    3
            classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
    
    4
        }
    
    5
    }
    

    Теперь создадим Activity, аналогичную той, которую мы создали на предыдущем шаге, но используем XML-макет для определения TextView. Создайте новый XML-файл макета с именем another_activity.xml. В XML-файле макета укажите TextView с id myMessage.

    1
    <?xml version="1.0" encoding="utf-8"?>
    
    2
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    
    3
        android:orientation="vertical" android:layout_width="match_parent"
    
    4
        android:layout_height="match_parent">
    
    5
    
    
    6
        <TextView
    
    7
            android:layout_width="wrap_content"
    
    8
            android:layout_height="wrap_content"
    
    9
            android:textAppearance="?android:attr/textAppearanceLarge"
    
    10
            android:text="Large Text"
    
    11
            android:id="@+id/myMessage" />
    
    12
    </LinearLayout>
    

    Создайте еще один класс Kotlin, AnotherActivity, который расширяет Activity и переопределяет его метод onCreate. Это должно выглядеть так:

    1
    package com.hathy.kotlinsample
    
    2
    
    
    3
    import android.app.Activity
    
    4
    import android.os.Bundle
    
    5
    
    
    6
    public class AnotherActivity: Activity() {
    
    7
    
    
    8
        override fun onCreate(savedInstanceState: Bundle?) {
    
    9
            super.onCreate(savedInstanceState)
    
    10
        }
    
    11
    }
    

    Вызовите setContentView в методе onCreate для использования XML-макета, который вы только что создали, в качестве макета этого Activity.

    1
    setContentView(R.layout.another_activity)
    

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

    1
    import kotlinx.android.synthetic.another_activity.myMessage
    

    Если в вашем макете было больше виджетов, вы можете импортировать все из них, используя следующий фрагмент кода:

    1
    import kotlinx.android.synthetic.another_activity.*
    

    Теперь вы можете получить доступ к своему TextView, используя свой id, как если бы это было свойство класса Activity. Например, чтобы изменить текст TextView, вы можете написать:

    1
    myMessage.setText("Hello")
    

    5. Преобразование классов Java в Kotlin

    Вы можете использовать плагин Kotlin для преобразования существующих классов Java в классы Kotlin. Чтобы попробовать эту функцию, создайте новый класс Java со следующей реализацией. Это простая Activity, которая выводит сумму двух целых чисел.

    1
    public class YetAnotherActivity extends Activity {
    
    2
    
    
    3
        private int a,b;
    
    4
    
    
    5
        @Override
    
    6
        protected void onCreate(Bundle savedInstanceState) {
    
    7
            super.onCreate(savedInstanceState);
    
    8
            a=10;
    
    9
            b=20;
    
    10
            Log.d("MESSAGE", "The sum is "+(a+b));
    
    11
        }
    
    12
    }
    

    Преобразуйте класс Java в класс Kotlin, нажав Control + Alt + Shift + J или в меню выберите Код > Преобразовать файл Java в файл Kotlin.

    Convert Java file to Kotlin fileConvert Java file to Kotlin fileConvert Java file to Kotlin file

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

    1
    public class YetAnotherActivity : Activity() {
    
    2
    
    
    3
        private var a: Int = 0
    
    4
        private var b: Int = 0
    
    5
    
    
    6
        override fun onCreate(savedInstanceState: Bundle?) {
    
    7
            super.onCreate(savedInstanceState)
    
    8
            a = 10
    
    9
            b = 20
    
    10
            Log.d("MESSAGE", "The sum is " + (a + b))
    
    11
        }
    
    12
    }
    

    Вы также заметите, что расширение файла изменилось с .java на .kt.

    Заключение

    В этом уроке вы узнали, как использовать Kotlin в своих проектах Android после установки плагина Kotlin и плагина Android Kotlin для Android. Поскольку классы Kotlin и Java в значительной степени совместимы, если вы все еще изучаете Kotlin, лучше всего вводить его в свои проекты Android постепенно.

    Чтобы узнать больше о Kotlin, я рекомендую просмотреть документацию Kotlin. Раздел Начало работы поможет вам быстро освоить этот новый язык.

    Понравилась статья? Поделить с друзьями:

    А вот и еще наши интересные статьи:

  • Панангин купить в минске цена инструкция
  • Темпрес таблетки инструкция по применению цена
  • Руководство по созданию интерфейсов
  • Новопассит при климаксе инструкция по применению
  • Телефон редми 9а инструкция по эксплуатации

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии