Факты о Чаке Норрисе — это интернет-феномен с шутливыми «фактами» о мастере боевых искусств и актёре Чаке Норрисе. «Факты» — это шутки о выносливости Норриса, его мужественности и статусе альфа-самца.
В этом уроке мы создадим собственное 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 и ты не написал ни одной статьи на хабре?
Самое время