Я никогда не был любителем HelloWorld и туториалов. D них как правило решаются проблемы, которые данным инструментом решаются хорошо, а вот острые углы и недостатки деликатно обходятся. По-настоящему пощупать язык или библиотеку можно только на реальном приложении, написании «бизнес-кода», а не «сервиса фабрики сервисов фабрик моделей сервисов». Пример такого простого приложения — на видео. Ну что, поехали?
Предпосылки к задаче
Я люблю читать книги с телефона и люблю твитить из этих книг цитаты. Но поскольку эти книги как правило содержат цитаты длиной более чем 140 символов, ситуация несколько раздражала. Решить проблему можно было бы двумя путями:
- отправлять цитаты несколькими твитами
- твитить текст картинами.
Именно второй путь мне показался менее ужасным. В результате получилось приложение, используемое в цепочке действий: расшарить текст из любого приложения -> попасть в нашу программу -> расшарить уже картинку в опять же любое приложение
Что можно узнать из получившегося проекта
- посмотреть на плюсы Kotlin
- посмотреть на типовый скрипт сборки в Gradle
- утащить функцию вычисления размера шрифта для вписывания текста в прямоугольник, приема текста, расшаренного из чужого приложения, функцию расшаривания картинки в чужие приложения и прочие мелочи.
Бенефиты Kotlin
Последние месяцы, слава* Xamarin’у, я писал под мобилки на .NET (C#). После него возвращаться в Android (где царит Java 7 с ограничениями, если вы хотите писать под Android 4.1+) не то что бы неприятно, но контраст очень заметен. Сахарку не хватает. Kotlin же дико радует возможностью писать код лаконично, машина за вас делает нехилую часть рутины.
Нужно объявить просто POJO-класс с readonly полям? Легко.
public class Size(val width:Int,val height:Int);
Самостоятельно объявлять тип переменных? Но зачем, если компилятор может вывести их за вас!
val textPaint = TextPaint(Paint.ANTI_ALIAS_FLAG); var paint = Paint();
Причем var — это переменная, а val — это final объект, так что случайно вам её иным значением не заменить.
Так же код позволяют сократить операторы вроде with:
fun alertError(text:String){ val builder = AlertDialog.Builder(this); with(builder) { setTitle(R.string.error_title) setMessage(text) setPositiveButton(R.string.ok, { dialogInterface, button -> }) } builder.create().show(); }
Или возможность втыкать переменные внутри строки без функции format или аналогичной по смыслу:
val cachePath = File(getExternalCacheDir(), "temp"); cachePath.mkdirs(); val fileName = "$cachePath/long_text_image.png";
Callback’и для кликов тоже назначаются достаточно более коротким кодом:
buttonSend.setOnClickListener { val text = editText.getText().toString(); //и т.п.... }
И как вы наверное уже заметили их примеров выше, оператор «new» тоже упразднён.
Ещё мне понравилась возможность объявлять функции без классов как альтернатива статическим методам, которых в Kotlin, как я понял, нет.
package com.newbilius.longtextsharer import [...] public fun getMaxFontSizeOfMultilineText(text: String, maxSize: Size, maxTextSize: Int): Float { fun getHeightOfMultiLineText(text: String, textSize: Int, maxWidth: Int): Int { //[...] } var textSize = maxTextSize; while (getHeightOfMultiLineText(text, textSize, maxSize.width) > maxSize.height) textSize--; return textSize.toFloat(); }
А ещё под Kotlin+Android у Kotlin есть такая классная штука — Kotlin Android Extensions. Она позволяет забыть findViewById() как страшный сон и делать следующий финт ушами:
import kotlinx.android.synthetic.activity.имя_xml_файла_activity.* //просто используем в коде ID-щники, объявленный в XML'е override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_send) editText.setText(getIntent().getStringExtra(Intent.EXTRA_TEXT)); //[...] }
На порядок удобнее, чем аннотации RoboGuice.
Пожалуй единственное, что мне показалось странным — формат досрочного выхода из CallBack’а:
buttonSend.setOnClickListener { val text = editText.getText().toString(); if (text.length()==0) { alertError(R.string.error_empty_text); return@setOnClickListener; //вот тут вот просто так берём и выходим } //[...] }
Используем Gradle
Сегодня сборка Android-приложений с помощью Gradle считается стандартом. Правда немного напрягает то, что разработка Groovy лежащего в основе Gradle остановлена (?) в начале года. Ну да ладно, побудем оптимистами. Минимальный скрипт сборки приложения (в debug- и release-сборке) выглядит вот так.
apply plugin: 'com.android.application' apply plugin: 'kotlin-android' import groovy.swing.SwingBuilder buildscript { ext.kotlin_version = '0.12.1218' repositories { jcenter() mavenCentral() } dependencies { classpath "com.android.tools.build:gradle:1.1.1" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version" } } repositories { jcenter() mavenCentral() } gradle.taskGraph.whenReady { taskGraph -> if(taskGraph.hasTask(':longtextsharer:assembleRelease')) { def pass = ''; pass = System.console().readPassword("\nPlease enter key passphrase: ") pass = new String(pass) if(pass.size() <= 0) { throw new InvalidUserDataException("You must enter a password to proceed.") } android.signingConfigs.release.storePassword = pass android.signingConfigs.release.keyPassword = pass } } android { compileSdkVersion 22 buildToolsVersion "22.0.1" signingConfigs { release { storeFile file("sign/SET_YOU_KEY.jks") storePassword "" keyAlias "SET_YOU_KEY" keyPassword "" } } defaultConfig { applicationId "com.newbilius.longtextsharer" minSdkVersion 16 targetSdkVersion 22 versionCode 1 versionName "1.0" } buildTypes { debug { debuggable true } release { debuggable false minifyEnabled true signingConfig signingConfigs.release proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } sourceSets { main.java.srcDirs += 'src/main/kotlin' } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:22.2.1' compile 'com.android.support:design:22.2.1' compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" }
К слову, крайне рекомендую использовать proguard для сборки релизной версии — без неё у меня Kotlin-приложение весило раз в 5 больше, чем аналогичное Java-приложение. После обработки proguard’ом же разница в размере была в 100 килобайт или около того. В proguard-правила для сборки пришлось добавить всего одно правило:
-dontwarn org.w3c.dom.events.*
Впрочем при сборке того же скрипта не из консоли, а из например IntelliJ IDEA вы наткнётесь на проблему — консоли для ввода пароля для сертификата у вас в этом случае нет. Для данной проблемы вроде как есть решение — но мне оно не подошло, при попытке его использовать я получил ошибку:
Error:(29, 0) Gradle: Failed to create component for ‘dialog’ reason: java.awt.HeadlessException > java.awt.HeadlessException (no error message)
Возможно, вам повезёт больше и вы поможете найти мне ошибку? Версии библиотек, Java и самой IDE самые свежие.
Локальные решения и выводы
Перечислять особенности же функций вычисления размера шрифта для вписывания текста в прямоугольник или расшаривания приложения я не буду — всё есть в исходниках описанного приложения на GitHub’е.
Ссылка на приложение на GitHub.
В общем, как по мне, на Kotlin код получается более лаконичным и простым, так что для своих небольших проектов я его использовать (как и делиться исходниками) продолжу и дальше. Если вам показалось, что я не использовал в этом приложении ещё какие-либо классные (и уместные) возможности Kotlin или Gradle — жду pull-request’ов и комментариев!
* слава очень ограниченная, статья с перечислением килотонн подводных камней уже в пути
ссылка на оригинал статьи http://habrahabr.ru/post/263657/
Добавить комментарий