Android向けKTXを学ぶ

こんにちは、Habr! GoogleがGoogle I / O 2017でKotlinをAndroidの公式開発言語として発表してからほぼ9か月が経過しました。 誰かがそれをメインツールとしてずっと長く使ってきました。2014年半ばから書かれている可能性があるためです。 Googleのドキュメントに、Kotlinでの実装の例が登場し始めました。 この間、開発者はこの言語のすべての利点を「触って」評価することができました。 そして、私を含む多くの人が、次のステップは何だと思いましたか? Kotlinのサポートライブラリ? または何か新しいものですか? 会いましょう:Android KTX! そして、分析に捧げられた記事の翻訳をあなたの注意に提示します。







2月5日、GoogleはAndroid KTXライブラリのリリースを発表しました。これは、Android用アプリケーションを開発するためのKotlin拡張機能のセットです。 このステップは、KotlinのAndroid開発への統合の論理的継続のように見え、コードの削減、より楽しく、プロジェクトコードの理解の容易さなどのすべての利点を追加します。



現在、ライブラリはプレビューでのみ利用可能であり、新しいアイデアを受け入れています。 最も可能性が高いのは、安定バージョンに移行したときに、大幅に多くの機能と機能を備えていることです。 私はドキュメントを読みながらこの記事を書きましたが、いくつかの点を詳しく見てみるといいと思いました。 ほとんどの場合、あなた自身が必要なときにだけドキュメントの学習を開始します。 しかし、私はこの記事があなたが今何を使用できるかについてのアイデアを提供してくれることを願っています。 まだKotlinを使用していない場合は、不足しているものを理解できます。



KTXのドキュメントはcore-ktxにあり、ライブラリ自体はandroid / android-ktxにあります。



アニメーター機能



アニメーションに関連する拡張機能は次のとおりです。 現在のリリースで利用可能なものを簡単に調べてみましょう。



アニメーションリスナー



開始するには、アニメーションリスナーをアニメーターに設定します。



animator.addListener { handleAnimation(it) }
      
      





この設計により、アニメーションイベントのコールバックを受け取ることができます。 リスナーからの特定のコールバックに拡張関数を使用することもできます。受信する関数のみを実装する必要があります。



 animator.addListener( onEnd = {}, onStart = {}, onCancel = {}, onRepeat = {} )
      
      





これは、不要で使用しないコールバックの実装がないため、コードが大幅に削減されます。



アニメーションイベントの個別のリスナー



リスナーを個々のイベントに設定する機会があります。たとえば、addListener()関数と同じ方法で、pauseイベントにリスナーを追加できます。



 animator.addPauseListener { handleAnimation(it) } //  animator.addPauseListener( onPause = {}, onResume = {} )
      
      





単一行の構文を使用して、アニメーションイベントでリスナーを「ハング」させることもできます。



 animator.doOnPause { handleAnimation(it) } animator.doOnCancel { handleAnimation(it) } animator.doOnEnd { handleAnimation(it) } animator.doOnRepeat { handleAnimation(it) } animator.doOnStart { handleAnimation(it) } animator.doOnResume { handleAnimation(it) }
      
      





現在Javaを使用している場合、Javaを実装するために必要なコードの量が少なく、読みやすいことがわかります。



内容



ここでは、Contentパッケージに追加された拡張機能を見ていきます。 システムサービスを取得する必要がある場合、拡張機能でこれを行うことができます。



 val alarmManager = systemService<AlarmManager>()
      
      





拡張機能を使用する場合、スタイル付き属性も機能します。



 context.withStyledAttributes(set = someAttributeSet, attrs = attributes, defStyleAttr = ..., defStyleRes = ...) { // -  } context.withStyledAttributes(set = someAttributeSet, attrs = attributes) { // -  }
      
      





SharedPreferencesの操作の記述は、拡張機能の編集機能を使用してできる限り簡単になりました。



 sharedPreferences.edit { putBoolean(key, value) }
      
      





contentValuesOf関数を使用してContentValuesのインスタンスを作成し、Pairインスタンスを引数として渡すこともできます。

 val contentValues = contentValuesOf(somePairs...)
      
      





時間操作



KTXは、時間の操作に関連する方法を使用することも提案しています。 ここにあるものを見てみましょう。

これで、単純な呼び出しでDayOfWeek、Month、YearをInt値として取得できます。



 DayOfWeek.FRIDAY.asInt() Month.APRIL.asInt() Year.now().asInt()
      
      





Durationクラスには、利用可能ないくつかの拡張関数もあります。



 //   val (seconds, nanoseconds) = Duration.ofSeconds(1) //  val resultValue = Duration.ofSeconds(1) * 2 //  val resultValue = Duration.ofSeconds(2) / 2 //  val resultValue = -Duration.ofSeconds(2)
      
      





プロパティInstant、LocalData、LocalDateTime、LocalTimeは、次の拡張関数によって取得できます。



 //   val (seconds, nanoseconds) = Instant.now() //   val (year, month, day) = LocalDate.now() //   val (localDate, localTime) = LocalDateTime.now() //   val (hour, minute, second, nanosecond) = LocalTime.now()
      
      





上記のメソッドと同様に、MonthDay、OffsetDateTime、およびOffsetTimeクラスのプロパティへのアクセスは、次のメソッドの呼び出しを通じて取得できます。



 //   val (month, day) = MonthDay.now() //   val (localDataTime, ZoneOffset) = OffsetDateTime.now() //   val (localTime, ZoneOffset) = OffsetTime.now()
      
      





Periodクラスを使用する場合、KTXライブラリには、このクラスのプロパティと操作にアクセスするためのいくつかの拡張関数が含まれています。



 //   val (years, month, days) = Period.ofDays(2) //  val resultValue = Period.ofDays(2) * 2 //  val resultValue = -Period.ofDays(2)
      
      





必要な値を取得するために使用できるいくつかの他の拡張関数があります。



 //   val (year, month) = YearMonth.now() //   val (localDateTime, ZoneId) = ZonedDateTime.now()
      
      





次の拡張関数は本当に良い追加機能であり、Int-valueを関数呼び出しによって提供される必要なクラスに簡単に変換できます。



 someInt.asDayOfWeek() //   DayOfWeek someInt.asMonth() //   Month someInt.asYear() //   Year someInt.days() //   Period someInt.hours() //   Duration someInt.millis() //   Duration someInt.minutes() //   Duration someInt.months() //   Period someInt.nanos() //   Duration someInt.seconds() //   Duration someInt.years() //   Period
      
      





これはLong値に対しても機能します:



 someLong.asEpochMillis() //   Instant someLong.asEpochSeconds() //   Instant someLong.hours() //   Duration someLong.millis() //   Duration someLong.minutes() //   Duration someLong.nanos() //   Duration someLong.seconds() //   Duration
      
      





OS



Android OSパッケージとのやり取りを目的とした拡張機能は次のとおりです。

これらには、Handlerクラスを操作するためのいくつかの拡張関数が含まれています。



 handler.postAtTime(uptimeMillis = 200L) { // -  } handler.postDelayed(delayInMillis = 200L) { // -  }
      
      





Bundleクラスのインスタンスの作成は、今ではずっと見栄えがよくなりました。



 val bundle = bundleOf("some_key" to 12, "another_key" to 15) val bundle = persistableBundleOf("some_key" to 12, "another_key" to 15)
      
      





また、Systraceツールのトレースイベントを記録すると、トレースメッセージの記録がより簡単で美しくなります。



 trace("section_name") { }
      
      





Utils



Utilパッケージには、ファイル、配列、およびその他の基本的なデータ型を操作するための拡張機能が含まれています。

AtomicFilesを使用する場合、次の関数を使用できます。



 val fileBytes = atomicFile.readBytes() val text = atomicFile.readText(charset = Charset.defaultCharset()) atomicFile.tryWrite { //    } atomicFile.writeBytes(byteArrayOf()) atomicFile.writeText("some string", charset = Charset.defaultCharset())
      
      





LongSparseArraySparseArraySparseBooleanArraySparseIntArraySparseLongArray型については、利用可能になりました:



 array.contains(someKey) array.containsKey(someKey) array.containsValue(someValue) array.forEach { key, value -> doSomething(key, value) } array.getOrDefault(key = keyValue, defaultValue = defaultValue) array.getOrElse(key = keyValue, defaultValue = defaultValue) array.isEmpty() array.isNotEmpty() val keyIterator = array.keyIterator() val valueIterator = array.valueIterator() array.plus(anotherArray) array.putAll(anotherArray) array.remove(key = keyValue, value = value) array.set(key = keyValue, value = value) array.size
      
      





Pairクラスの操作が少し簡単になりました。



 val pair = android.util.Pair("dsfn", "sdihfg") //   val (key, value) = pair //  Android Pair  Kotlin Pair val kotlinPair = pair.toKotlinPair()
      
      





KotlinペアをAndroidペアに変換することもできます。



 val pair = Pair("dsfn", "sdihfg") val androidPair = pair.toAndroidPair()
      
      





Halfクラスを使用する場合、KTXのおかげで、他のタイプのデータを簡単に変換できます。



 short.toHalf() string.toHalf() float.toHalf() double.toHalf()
      
      





拡張機能を使用して、 ClosedRangeクラスのインスタンスをRangeに変換できるようになりました



 val range = closedRange.toRange()
      
      





Rangeクラスのインスタンスの上で、次のことができるようになりました。



 val range = closedRange.toClosedRange() //     val resultValue = closedRange and someOtherRange //   ,    val resultValue = closedRange += someOtherCloseRange //       val resultValue = closedRange += someValue
      
      





SizeクラスとSizeFクラスの両方で拡張機能を使用できます。



 val size = Size(5, 5) //   val (width, height) = size
      
      





データベースカーソル



このセクションには、 Cursorクラスで使用可能な拡張関数が含まれています。 機能の各グループは、次の順序で配置されます。



 cursor.getBlob(columnName = "some_column") cursor.getBlobOrNull(columnName = "some_column") cursor.getBlobOrNull(index = 0) cursor.getDouble(columnName = "some_column") cursor.getDoubleOrNull(columnName = "some_column") cursor.getDoubleOrNull(index = 0) cursor.getFloat(columnName = "some_column") cursor.getFloatOrNull(columnName = "some_column") cursor.getFloatOrNull(index = 0) cursor.getInt(columnName = "some_column") cursor.getIntOrNull(columnName = "some_column") cursor.getIntOrNull(index = 0) cursor.getLong(columnName = "some_column") cursor.getLongOrNull(columnName = "some_column") cursor.getLongOrNull(index = 0) cursor.getShort(columnName = "some_column") cursor.getShortOrNull(columnName = "some_column") cursor.getShortOrNull(index = 0) cursor.getString(columnName = "some_column") cursor.getStringOrNull(columnName = "some_column") cursor.getStringOrNull(index = 0)
      
      





Sqlite



現時点では、SQLiteには1つの関数しかありませんが、非常に便利です。 指定されたSQLステートメントを使用してトランザクションを作成できます。



 sqLiteDatabase.transaction { "some SQL statement" }
      
      





資源



リソースに関しては、これまでのところ、 TypedArrayクラスでの作業を簡素化する拡張機能のみが追加されています。



 val boolean = typedArray.getBooleanOrThrow(0) val int = typedArray.getColorOrThrow(0) val colorStateList = typedArray.getColorStateListOrThrow(0) val float = typedArray.getDimensionOrThrow(0) val int = typedArray.getDimensionPixelOffsetOrThrow(0) val int = typedArray.getDimensionPixelSizeOrThrow(0) val drawable = typedArray.getDrawableOrThrow(0) val float = typedArray.getFloatOrThrow(0) val typeface = typedArray.getFontOrThrow(0) val int = typedArray.getIntOrThrow(0) val int = typedArray.getIntegerOrThrow(0) val string = typedArray.getStringOrThrow(0) val charSequenceArray = typedArray.getTextArrayOrThrow(0) val charSequence = typedArray.getTextOrThrow(0)
      
      





:指定されたインデックスが存在しない場合、すべてのthrowはIllegalArgumentExceptionをスローします。



テキスト



私たち(開発者)が取り組んでいるほとんどのアプリケーションは、これらの同じアプリケーションの異なる場所でテキストを使用しています。 幸いなことに、KTXには、特にSpannableStringBuilderクラスのために、それを操作するためのいくつかの関数があります。

たとえば、ビルダーの初期化直後に、これらの関数を使用して、元の行の最後に太字のテキストを追加できます。



 val builder = SpannableStringBuilder(urlString) .bold { append("hi there") } //  bold / italic / underlined ,  . val builder = SpannableStringBuilder(urlString) .bold { italic { underline { append("hi there") } } }
      
      





背景色を設定したり、テキストにパディングを追加したりできるビルド関数もあります。



 .backgroundColor(color = R.color.black) { //   builder } .inSpans(spans = someSpans) { //   builder }
      
      





最後の関数はbuildSpannedStringです。これにより、上記の拡張関数を使用して文字列を作成できます。



 textView.text = buildSpannedString { bold { append("hitherejoe") } }
      
      





ネット



.netパッケージには、文字列をURIに簡単に変換できる1つの関数があります。 必要なもの!



 val uri = urlString.toUri()
      
      





グラフィックス



KTXのグラフィックパッケージは非常に大規模でしたが、アプリケーションのすべての視覚的な微妙さを簡単に実装することができます。

まず、Bitmapを(だけでなく)次のタイプに変換する関数に注目したいと思います。



 val adaptiveIcon = bitmap.toAdaptiveIcon() val drawable = bitmap.toDrawable(resources) val icon = bitmap.toIcon() val drawable = someInt.toDrawable() val icon = someByteArray.toIcon() val icon = someUri.toIcon() val colorDrawable = someColor.toDrawable() val bitmap = drawable.toBitmap(width = someWidth, height = someHeight, config = bitMapConfig)
      
      





次に、ビットマップを操作するための主要な操作を検討します。



 val bitmap = someBitmap.applyCanvas(block = { }) val colorInt = someBitmap.get(x, y) val bitmap = someBitmap.scale(width, height, filter = true) someBitmap.set(x, y, color)
      
      





また、 Canvasでの作業がはるかに簡単になりました。



 canvas.withRotation(degrees, pivotX, pivotY) { //  } canvas.withSave { //  } canvas.withScale(x, y, pivotX, pivotY) { //  } canvas.withSkew(x, y) { //  } canvas.withTranslation(x, y) { //  }
      
      





また、 Colorにはいくつかのイノベーションがあります。



 //   val (r, g, b, a) = color //   val color += someColor
      
      





plus()関数は本当にクールで、2つの色を混ぜて混ぜた色を得ることができます!

さらに、マトリックスの操作が簡単になりました。 これで、2つの行列を乗算して、結果として1つのMatrixオブジェクトを取得できます。



 //  val resultMatrix = matrix * someOtherMatrix val values = matrix.values()
      
      





また、適切なアクションを実行するためにパラメーターブロックを使用して、record関数を介してPictureを操作することもできます。



 val resultField = picture.record(width = someWidth, height = someHeight) { // -   Canvas }
      
      





drawableの境界を変更したい場合は、updateBounds関数を呼び出して、パラメーターとしてディメンションを渡すことができます。



 drawable.updateBounds(left = 16, top = 16, right = 16, bottom = 16)
      
      





シェーダーで変換を行う必要がありますか? 問題ありません!



 shader.transform { //  }
      
      





PorterDuffクラスを操作するためのいくつかの拡張関数があります。



 val porterDuffColorFilter = mode.toColorFilter(someColor) val porterDuffXfermode = mode.toXfermode()
      
      





Regionクラスを使用して、次の関数を使用できるようになりました。



 //   someRegion  Rect val region = someRegion and someRect //   someRegion  Region val region = someRegion and someRegion //   someRegion  Rect val region = someRegion - someRect //   someRegion   Region val region = someRegion - someRegion //   someRegion  Rect val region = someRegion or someRect //   someRegion   Region val region = someRegion or someRegion //   someRegion  Rect val region = someRegion + someRect //   someRegion  Region val region = someRegion + someRegion //     someRegion  Rect val region = someRegion xor someRect //     someRegion   Region val region = someRegion xor someRegion val boolean = someRegion.contains(somePoint) someRegion.forEach { doSomethingWithEachRect(it) } val iterator = someRegion.iterator() //   someRegion   Region val region = -someRegion
      
      





PointFクラスも追加され、いくつかの機能が簡素化されています。



 val (x, y) = somePointF val pointF = somePointF - someOtherPointF val pointF = somePointF - someFloat val pointF = somePointF + somePointF val pointF = somePointF + someFloat val point = somePointF.toPoint() val pointF = -somePointF
      
      





同じことがPointクラスにも使用できます。



 val (x, y) = somePoint val point = somePoint - somePoint val point = somePoint - someFloat val point = somePoint +somePoint val point = somePoint + someFloat val point = somePoint.toPointF() val point = -somePoint
      
      





また、 Rectクラスの場合:



 val rect = someRect and anotherRect val (left, top, right, bottom) = someRect someRect.contains(somePoint) val region = someRect - anotherRect val rect = someRect - someInt val rect = someRect - somePoint val rect = someRect or someRect val rect = someRect + someRect val rect = someRect + someInt val rect = someRect + somePoint val rectF = someRect.toRectF() val region = someRect.toRegion() val region = someRect xor someRect
      
      





驚くことはありませんが、 RectFでも利用可能です。



 val rectF = someRectF and anotherRectF val (left, top, right, bottom) = somerectF someRectF.contains(somePoint) val region = someRectF - anotherRectF val rectF = someRectF - someInt val rectF = someRectF - somePoint val rectF = someRectF or someRect val rectF = someRectF + someRect val rectF = someRectF + someInt val rectF = someRectF + somePoint val rect = someRectF.toRect() val region = someRectF.toRegion() val reactF = someRectF.transform(someMatrix) val region = someRectF xor someRect
      
      





Pathクラスを使用する場合、次のオプションを使用できます。



 val path = somePath and anotherPath val path = somePath.flatten(error = 0.5f) val path = somePath - anotherPath val path = somePath or anotherPath val path = somePath + anotherPath val path = somePath xor anotherPath
      
      





グラフィックを使用する場合、データ型IntおよびLongを使用する可能性が非常に高くなります。 Type Intは、KTXで次の機能を提供します。



 val alpha = int.alpha val blue = int.blue val green = int.green val red = int.red val luminance = int.luminance val (alphaComp, redComp, greenComp, blueComp) = someInt val color = someInt.toColor() val color = someInt.toColorLong()
      
      





一方、Long型には、さらにいくつかの関数が含まれています。



 val alpha = long.alpha val blue = long.blue val green = long.green val red = long.red val luminance = long.luminance val (alphaComp, redComp, greenComp, blueComp) = someLong val color = someLong.toColor() val color = someLong.toColorInt() long.isSrgb long.isWideGamut long.colorSpace
      
      





遷移



したがって、 Transitionクラスに到達すると、ここでアニメーションリスナーと同様の拡張機能を使用できることがわかります。



 transition.addListener { doSomethingWithTransition(it) } transition.addListener( onEnd = {}, onStart = {}, onCancel = {}, onResume = {}, onPause = {} )
      
      





ただし、個々のコールバックのメソッド構文にはわずかな違いがあります。



 transition.doOnCancel { } transition.doOnEnd { } transition.doOnPause { } transition.doOnResume { } transition.doOnStart { }
      
      





視聴回数



同様の機能がViewクラスにも追加されました。 コールバックの設定は非常に明確です。



 view.doOnLayout { } view.doOnNextLayout { } view.doOnPreDraw { }
      
      





postDelayedメソッド関数として利用可能になりました:



 view.postDelayed(delayInMillis = 200) { // -  }
      
      





postOnAnimationDelayedメソッドでも同じことが言えます。



 view.postOnAnimationDelayed(delayInMillis = 200) { // -  }
      
      





Viewのパディングの更新は、はるかに簡単で理解しやすいものになりました。そのため、いくつかの機能が提供されました。



 view.setPadding(16) view.updatePadding(left = 16, right = 16, top = 16, bottom = 16) view.updatePaddingRelative( start = 16, end = 16, top = 16, bottom = 16)
      
      





ビューをビットマップに変換する必要がある場合は、1行のコードで実行できます!



 val bitmap = view.toBitmap(config = bitmapConfig)
      
      





ViewGroup



いくつかの非常にクールな拡張機能がViewGroupに追加されました。 気に入っていただけると思います! たとえば、ViewGroupに特定のビューが含まれているかどうかを確認します。



 val doesContain = viewGroup.contains(view)
      
      





子ViewGroupのサイクル(それが子である場合):



 viewGroup.forEach { doSomethingWithChild(it) } viewGroup.forEachIndexed { index, view -> doSomethingWithChild(index, view) }
      
      





Kotlinスタイルの特定のポジションの子へのアクセス:



 val view = viewGroup[0]
      
      





MutableIteratorのインスタンスの取得



 val viewGroupIterator = viewGroup.iterator()
      
      





そして、ViewGroupを使用した他のいくつかの操作:



 viewGroup.isEmpty() viewGroup.isNotEmpty() viewGroup.size //  view   viewgroup viewGroup -= view //  view   viewgroup viewGroup += view
      
      





マージン



Viewのパディングと同様に、次の関数を使用してLayoutParamsのマージンを追加できます。



 params.setMargins(16) params.updateMargins(left = 16, right = 16, top = 16, bottom = 16) params.updateMarginsRelative( start = 16, end = 16, top = 16, bottom = 16)
      
      





おわりに



ご覧のとおり、KTXはAndroidアプリケーションの開発でKotlinを使用するための強力なツールを提供します。 私のプロジェクトでそれらを使用できることを非常に嬉しく思っており、近い将来に追加されるものを楽しみにしています。



All Articles