
Факты о Чаке Норрисе — это интернет-феномен с шутливыми «фактами» о мастере боевых искусств и актёре Чаке Норрисе. «Факты» — это шутки о выносливости Норриса, его мужественности и статусе альфа-самца.
В этом уроке мы создадим собственное Android-приложение с фактами о Чаке Норрисе с помощью Kotlin. В качестве IDE мы будем использовать Android Studio. На этом примере вы сможете узнать, как выполнять запросы к сети на Kotlin и как использовать библиотеку OkHttp 3.
Факты будут получены из базы данных, состоящей из фактов о Чаке Норрисе, которая предлагает простой API для получения случайных фактов.
Добавление зависимости для OkHttp
Чтобы выполнять сетевые вызовы, мы будем использовать библиотеку OkHttp. Итак, нам нужно добавить зависимость OkHttp 3.10 в наш файл build.gradle:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 27
defaultConfig {
applicationId "com.ssaurel.chucknorrisfacts"
minSdkVersion 15
targetSdkVersion 27
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
}Настройка Android Manifest
Также для выполнения сетевых вызовов, нам нужно добавить разрешение INTERNET в манифест нашего приложения:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ssaurel.chucknorrisfacts">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>Создание пользовательского интерфейса
Следующим шагом является создание пользовательского интерфейса нашего приложения. Мы будем использовать ConstraintLayout в качестве корневого компонента layout.
Вверху нашего пользовательского интерфейса будет находиться ImageView с лицом Чака Норриса:

Затем мы добавляем TextView, в котором мы будем отображать факт о Чаке Норрисе. Для TextView определяем зависимость, которая располагает его чуть ниже ImageView. После этого добавляем кнопку, которая позволит пользователям загружать новый факт, запрашивая его из базы данных. Наконец, добавляем ProgressBar, который будет центрирован на экране.
В итоге получаем следующий layout для нашего пользовательского интерфейса:
<?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"
android:background="#FFFFFF"
tools:context=".MainActivity">
<ImageView
android:id="@+id/imageView"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="30dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/chucknorris" />
<TextView
android:id="@+id/factTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="30dp"
android:text="Chuck Norris Fact"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView"
android:textSize="20sp"
android:paddingLeft="30dp"
android:paddingRight="30dp"
android:gravity="center_horizontal"/>
<Button
android:id="@+id/nextBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="30dp"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:text="Next"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone"/>
</android.support.constraint.ConstraintLayout>Тестирование API
Перед написанием кода в MainActivity мы протестируем ответ, возвращаемый API базы данных. Мы будем обращаться по следующему адресу: https://api.icndb.com/jokes/random.
Этот веб-сервис случайным образом возвращает новый факт о Чаке Норрисе при каждом вызове. Введя URL-адрес в веб-браузер, вы получите следующий результат:

Итак, нам нужно будет спарсить JSON-ответ, чтобы добраться до свойства joke, в котором и содержится необходимый нам факт.
Написание кода на Kotlin для MainActivity
Теперь пришло время написать код для MainActivity. Мы определяем переменную, в которой храним URL конечной точки API, который мы собираемся вызвать. Затем мы создаем экземпляр объекта OkHttpClient.
В методе onCreate MainActivity нам просто нужно установить OnClickListener на кнопку, позволяющую пользователям загружать новые факты о Чаке Норрисе.
Обращение к API выполняется в специальном методе loadRandomFact. Мы отображаем ProgressBar непосредственно перед обращением к сети. Затем мы создаём объект Request с URL-адресом конечной точки в параметре.
После этого мы вызываем метод newCall на OkHttpClient, передавая в него Request в качестве параметра. Чтобы обработать ответ, мы вызываем метод enqueue с экземпляром Callback в параметре.
В методе onResponse мы получаем ответ и затем создаём JSONObject. Последний шаг — получить свойство joke объекта value. После этого мы можем отобразить факт о Чаке Норриме в TextView, инкапсулировав всё в блок runOnUiThread, чтобы быть уверенным, что обновление пользовательского интерфейса будет выполнено в потоке пользовательского интерфейса.
В итоге получаем следующий код для MainActivity нашего Android-приложения:
package com.ssaurel.chucknorrisfacts
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.text.Html
import android.view.View
import kotlinx.android.synthetic.main.activity_main.*
import okhttp3.*
import org.json.JSONObject
import java.io.IOException
class MainActivity : AppCompatActivity() {
val URL = "https://api.icndb.com/jokes/random"
var okHttpClient: OkHttpClient = OkHttpClient()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
nextBtn.setOnClickListener {
loadRandomFact()
}
}
private fun loadRandomFact() {
runOnUiThread {
progressBar.visibility = View.VISIBLE
}
val request: Request = Request.Builder().url(URL).build()
okHttpClient.newCall(request).enqueue(object: Callback {
override fun onFailure(call: Call?, e: IOException?) {
}
override fun onResponse(call: Call?, response: Response?) {
val json = response?.body()?.string()
val txt = (JSONObject(json).getJSONObject("value").get("joke")).toString()
runOnUiThread {
progressBar.visibility = View.GONE
factTv.text = Html.fromHtml(txt)
}
}
})
}
}Запускаем приложение
Лучшая часть урока. Когда запустите приложение, вы получите следующий результат:

Комментарии (6)

dasenkiv
17.05.2019 15:28Я, конечно, понимаю, что на хабре стали появляться статьи с очень низким порогом вхождения в предметную область и не IT-шные статьи, но ИМХО это даже для песочницы слабовато.

rdnve
17.05.2019 15:28Всякий раз как вижу посты с фактами про Чака Норриса, вспоминаю статейку на хабре: «18 фактов о Джоне Ските». :-)
DarkMonkqq
А в чём смысл данного примера? Если смотреть на общий концепт, то это небольшое приложение написано достаточно плохо, новичкам даже не стоит смотреть на это(
1. Куча странных отступов в xml (и падинг, и left, и start), всё тоже самое можно сделать с помощью guideline по-человечески
2. Если текста про Чака будет много, то он наедет на нижнюю кнопку
3. Вся логика в активити, урл не разбит по эндпоинтам.
4. Вместо того, чтобы полученный ответ от сервера через GSON обернуть в объект, он парсится в ручную в лучших традициях пятилетней давности.
В итоге это не туториал, а какой-то набор худших практик по написанию приложений под андроид.
UncleJey
Тебе уже стукнуло 15 и ты не написал ни одной статьи на хабре?

Самое время