JVMデスクトップフレームワークの現状


このシリーズの前のポストは、同じ伝統的なオブジェクト指向プログラミングアプローチを採用したフレームワークに捧げられました.コンポーネントをクラスとしてモデル化しました.今週のポストは、デスクトップのためのジェットの作成、完全に異なるアプローチを提供するブロック上の新しい子供に捧げられています.

足を濡らす


本来、Jetpack ComposeはAndroidランタイムのフレームワークです.デスクトップ用の構成はJVMへのポートです.
伝統的なGUI開発はOOP原則に従います:一つのグラフィカルな構成要素は州をカプセル化して、それを変えるためにふるまいを提供します.これは、AWT、スイング、JavaFX、さらにはSWTの仕事です.
作曲の背後にあるアイデアは、OOPから機能的プログラミング原理に移行することです.コンポーネントは、関数によってモデル化され、状態はパラメーターとして渡されます.状態が変化すると、関数が呼び出されます.
テキストフィールドに値を表示するスニペットを作成します.
fun main() = Window {
  TextField("Hello world!")   // 1
}
  • TextField はコンストラクタへの呼び出しではなく関数の呼び出しです
  • ソースコードは確かに誤解を払拭します.
    @Composable
    fun TextField(
        value: String,
        ...
    )
    

    巻上げ


    コンポーネントから状態を削除するには、状態巻き上げとして知られています.
    ほとんどのアプリケーションは状態を表示するのを止めません.GUIアプリケーションの古典的な例は、テキストフィールドの値を反映するラベルを持つことです.

    以下のようにします:
    fun main() = Window {                                        // 1
      val state = remember { mutableStateOf("Hello world!") }    // 2
      Row {                                                      // 3
        TextField(
          state.value,                                           // 4
          { state.value = it }                                   // 5
        )
        Text(state.value)
      }
    }
    
    これはいくつかの説明に値する.
  • トップレベルコンテナ.構成ループをドライブします.我々は見るthe compose loop 次のセクションで.
  • 可変コンテナ内の状態オブジェクトをラップする
  • レイアウト.セットされていないレイアウトでは、コンポーネントが互いに上に描かれている
  • 初期値
  • 値が変更される度に実行される関数
  • 構成フレームワークの概念の周りに設計されてState . それは、そのようなクラスのインスタンスにいくつかの機能を提供します.

    それ自体では、テキストフィールドの値をミラーリングするだけで、状態は面白くない.単純な計算機使用ケースを想像してください、しかし、代わりに2つのフィールドの整数値の合計にそれを制限してください.

    合計を保持する状態オブジェクトが必要です.
    構成は、派生状態の概念を提供します:
    fun main() = Window {
      val first = remember { mutableStateOf(0) }                           // 1
      val second = remember { mutableStateOf(0) }                          // 2
      val sum = derivedStateOf { first.value + second.value }              // 3
      Row {
        TextField(first.value.toString(),  { first.value = it.toInt() })
        TextField(second.value.toString(), { second.value = it.toInt() })
        Text(sum.value.toString())
      }
    }
    
  • ファーストバリュー
  • 二値フィールド
  • いつでもfirst or second 値の変更sum 再計算
  • 独自のコンポーネント


    独自のコンポーネントを作成する機能を実装し、それを注釈として簡単です@Composable .

    Composable can be applied to a function or lambda to indicate that the function/lambda can be used as part of a composition to describe a transformation from application data into a tree or hierarchy.

    -- Composable JavaDocs


    上の「電卓」のスニペットはこのように書き換えることができます.
    @Composable
    fun IntField(state: MutableState<Int>) = TextField(     // 1
      state.value.toString(),
      { state.value = it.toInt() }
    )
    
    fun main() = Window {
      val first = remember { mutableStateOf(0) }
      val second = remember { mutableStateOf(0) }
      val sum = derivedStateOf { first.value + second.value }
      Row {
        IntField(first)
        IntField(second)
        Text(sum.value.toString())
      }
    }
    
  • 見て、MA、新しいカスタムコンポーネント!
  • コンポーネントの注釈@Composable 重要な結果:バイトコード内の関数のシグネチャを変更します.この点では、coroutinesに似ています.
    これはIntField 機能
    public static final void IntField(
        androidx.compose.runtime.MutableState<java.lang.Integer>,
        androidx.compose.runtime.Composer<?>, int
    );
    
    追加に注意Composer パラメータこれは、構成の魔法があるところです.
    コンパイラ自身がコルーチンを扱う間、同じ結果を達成するために専用のコンパイラプラグインが必要です.

    合成ループ


    これまでのところ、我々は、どのように構成を開発するためのポストに焦点を当てている.私たちは作曲の仕方を避けた.それでも、他のフレームワークと連携して開発するのは、他のフレームワークとは異なっていると思います.
    コンポーネントを構成するすべての関数だけを覚えておいてください.このような関数はステートレスであり、パラメータを渡すことによって外部からの状態を注入する.状態が変化すると、それを検出し、アプリケーションのリペイントをトリガします.関数は、先頭を含むWindow() 一つ
    作曲はそれを達成するComposer パラメータ@Composable 関数.最後にWindow 関数はこの機構をセットアップする.
    興味深いことに、デスクトップ用の構成は、GUIクラスに依存します.スイングJFrame ! これは次のクラス図に要約されます.

    記憶状態


    今は時間を書く時間ですremember 関数.私たちは、構成が各々の状態変化のために機能を呼び出すということを知っています.状態はそれらの関数の中で変数に格納されます.このように、構成が起動するときに、状態は失われて、初期値にリセットされる.
    上記のスニペットのいずれかを実行しないでくださいremember() 機能:それぞれの変更後に状態が失われるので、彼らは何もしない.
    再構成全体の状態を追跡するには、内部にラップする必要がありますremember ブロック.これは、状態の値をキャッシュし、関数が呼び出された後に再び設定するように構成します.

    第一remember() 機能のみが実行されますcalculation 最初の構成中.さらなる再構成はキャッシュされた値をもたらします.
    オーバーロードされた関数は1つ以上のパラメータを渡すことができます.パラメータが以前の構成から変更された場合、composeはcalculation 関数を返します.それ以外の場合は、上記と同様に動作します-値をキャッシュします.

    その他


  • アルファ
    まず第一に、デスクトップのための合成がアルファである点に注意してください.変更が必要です.警告されました.

  • Gradleプラグイン:
    そのAndroidのルーツのために、バイトコードの機能署名の変更の魔法をする合成プラグインはGradleで利用可能です.これであなたの平和を作る、私はかなりMavenプラグインが正式に公開される予定はないと確信しています.あなたが1を書く限り.

  • 配布
    プラグインはpackage OS固有のインストーラを作成するタスク.これはあなたのアプリケーションを配布するのに最適です.
    タスクはjpackage フードの下にJDK 14以上を使用してください.また、インストール済みのアプリケーションを実行するためにJREを必要とすることに注意してください.

  • ラベル:
    フィールドにラベルを付けるにはText 前のフレームワークのUIのコンポーネント.代わりに、フィールド自体に設定します.
    TextField(
        value = "Hello world!",
        onValueChange = {},
        label = { Text("Say hello!") },
    )
    
    値なしで、プレースホルダとして表示テキストラベルを作成します.値やフォーカスを受け取ると、それは上に移動します.


  • 機能の欠落
    再びAndroidのため、フレームワークの成熟のため、いくつかの重要な機能が不足している.少なくとも次のように気付きました.
  • タブ貼りなしTAB 次のフィールドへジャンプします
  • 表コンポーネントJTable . 他方では、構成はAを提供しますSwingPanel これは、任意のスイングコンポーネントを埋め込むことができます.
  • 複雑なレイアウトは可能ですが、実装は複雑です
  • これが最終結果です.

    結論


    デスクトップ用のJetpackは面白いイニシアチブのようです.フレームワークは初期段階にある.しかし、機能的アプローチは、他のすべてのJavaデスクトップフレームワークと比較してオリジナルです.
    この投稿の完全なソースコードはGithubにあります.

    アヤアベーク / Renamerの構成



    さらに進む
  • Build better apps faster with Jetpack Compose (Android)
  • Compose for Desktop
  • GitHub repo with samples
  • Under the hood of Jetpack Compose — part 2 of 2
  • 当初公開A Java Geek 2月7日