ChatGPTアプリで表を出力させる - Markdown Text for Android JetpackCompose

ChatGPT は Markdown 出力することができますよね。

それを Jetpack Compose で表示させましょう。

ChatGPTアプリで表を出力させる - Markdown Text for Android JetpackCompose

すごく分かりやすいですね。



以下のライブラリを使いました。

👉 jeziellago/compose-markdown: Markdown Text for Android Jetpack Compose 📋. hatena-bookmark

利用記述はシンプルに作成されています。


@Composable  
fun ComplexExampleContent() {  
  MarkdownText(
    modifier = Modifier.padding(8.dp),
    markdown = markdown,
    textAlign = TextAlign.Center,
    fontSize = 12.sp,
    color = LocalContentColor.current,
    maxLines = 3,
    fontResource = R.font.montserrat_medium,
    style = MaterialTheme.typography.overline,
  )  
}

続いて、Markdown で Mermaid や Planet UML で図を出力もできそうです。

ちなみに、このライブラリは、さらに以下のライブラリたちに順に依存しています。

👉 Markwon/app-sample at master · noties/Markwon hatena-bookmark
👉 CommonMark hatena-bookmark



Android JetpackCompose での OptionMenu - Alternative for OptionMenu in JetpackCompose

Android JetpackCompose で TopAppBar に入れるべく「メニューボタン」をどうするか。

こんな感じになりました。

今現在は、この Box + DropDownMenu でいこうかと思っています。

以下記事から。

A DropdownMenu behaves similarly to a Popup, and will use the position of the parent layout to position itself on screen. Commonly a DropdownMenu will be placed in a Box with a sibling that will be used as the 'anchor'.

DropdownMenu は Popup と同様の動作をし、親レイアウトの位置を利用して画面上に配置されます。DropdownMenu
は「アンカー」として使用される兄弟を持つ Box に配置されます。

👉 android - Jetpack compose popup menu - Stack Overflow hatena-bookmark


@Composable
fun MenuButton() {

  val context = LocalContext.current
  var expanded by remember { mutableStateOf(false) }

  Box {

    IconButton(onClick = { expanded = true }) {
      Icon(Icons.Filled.MoreVert, null)
    }

    DropdownMenu(
      expanded = expanded,
      onDismissRequest = { expanded = false },
    ) {
      DropdownMenuItem(
        onClick = {
          expanded = false
          context.startActivity(
            Intent(Intent.ACTION_VIEW, Uri.parse("https://platform.openai.com/account/usage"))
          )
        }
      ) {
        Text(text = "Usage")
      }
      Divider()
      DropdownMenuItem(onClick = {
        expanded = false
        context.startActivity(
          Intent(Intent.ACTION_VIEW, Uri.parse("https://platform.openai.com/docs/api-reference"))
        )
      }) {
        Text(text = "API reference")
      }
    }

  }

}

こんなところでしょうか。

TopAppBar にセットするなら、以下のように action へ。


TopAppBar(
  title = { Text("Simple TopAppBar") },
  navigationIcon = {
    IconButton(onClick = { /* doSomething() */ }) {
      Icon(Icons.Filled.Menu, contentDescription = null)
    }
  },
  actions = {
    MenuButton()
  }
)

汎用性を上げるなら引数を考慮して、labelaction のペアのリストを渡してループ、などするとよいです。

👉 DropdownMenu | androidx.compose.material  |  Android Developers hatena-bookmark



Kotlin で Jetpack Compose, Retrofit を使って API リクエスト、JSON パース して、グリッド表示するコードをファイル別に表示して

Kotlin で Jetpack Compose と Retrofit を使って API リクエスト、JSON パースして、グリッド表示するためには、以下のようにファイルを分けることができます。

 

1. API リクエストを行うための Retrofit インターフェースを定義するファイル


interface ApiService {

    @GET("/photos")
    suspend fun getPhotos(): List<Photo>
}

 

2. API レスポンスを表すデータクラスを定義するファイル


data class Photo(
    val id: Int,
    val title: String,
    val url: String
)

 

3. API レスポンスをパースして、表示するための画面を定義するファイル


@Composable
fun PhotoGrid() {
    val photos by remember { mutableStateOf<List<Photo>>(emptyList()) }

    LaunchedEffect(Unit) {
        val apiService = Retrofit.Builder()
            .baseUrl("https://jsonplaceholder.typicode.com")
            .addConverterFactory(Json {
                ignoreUnknownKeys = true
            }.asConverterFactory("application/json".toMediaType()))
            .build()
            .create(ApiService::class.java)

        photos = apiService.getPhotos()
    }

    LazyVerticalGrid(cells = GridCells.Adaptive(minSize = 128.dp)) {
        items(photos) { photo ->
            Card(
                modifier = Modifier.padding(4.dp),
                shape = RoundedCornerShape(8.dp),
                elevation = 4.dp
            ) {
                Column(
                    Modifier
                        .fillMaxWidth()
                        .height(128.dp)
                        .padding(8.dp)
                ) {
                    Text(
                        text = photo.title,
                        style = MaterialTheme.typography.subtitle1,
                        maxLines = 2,
                        overflow = TextOverflow.Ellipsis
                    )
                    Spacer(modifier = Modifier.weight(1f))
                    CoilImage(
                        data = photo.url,
                        contentScale = ContentScale.Crop,
                        modifier = Modifier.fillMaxSize()
                    )
                }
            }
        }
    }
}

 

4. 上記の画面を表示するための Activity もしくは Fragment を定義するファイル


class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            PhotoGrid()
        }
    }
}

以上のように、ファイルを分けることで、API リクエストやパース、表示を分離し、コードを見やすく、メンテナンスしやすくすることができます。

 

■ 感想

ただ、一行問いかけただけ。

ChatGPT で、ここまで書けるとは。



【Android】Version Catalog + JetpackCompose UI + BOM

Compose の BOM。

【Android】Version Catalog + JetpackCompose UI + BOM
👉 Google's Maven Repository hatena-bookmark

公開されているので使ってみます。

少し公式ドキュメントで調べておきます。

BOM の使用は義務ですか?

いいえ。個々の依存関係バージョンを手動で追加することもできます。しかし、すべての最新の安定版を同時に使用することが容易になるので、BOM を使用することをおすすめします。

Compose Compiler ライブラリが BOM に含まれていないのはなぜですか?

Compose Kotlin コンパイラ拡張機能(androidx.compose.compiler)は、Compose ライブラリのバージョンでなく、Kotlin コンパイラ プラグインのバージョンにリンクされており、Compose の他の部分とは異なる頻度でリリースされます。したがって、お使いの Kotlin バージョンと互換性のあるバージョンを使用してください。

👉 Using the Bill of Materials  |  Jetpack Compose  |  Android Developers hatena-bookmark

公式サンプルコードを少し変更したものを準備しておきます。


# libs.versions.toml

[libraries]
compose-ui-bom = { module = "androidx.compose:compose-bom", version = "2023.01.00" }


// build.gradle

dependencies {
  implementation platform(libs.compose.ui.bom)
}

👉 BOM to library version mapping  |  Jetpack Compose  |  Android Developers hatena-bookmark

 

■ 実装

実装としては、以下でいけました。


# libs.versions.toml

[libraries]

compose-ui-bom = { module = "androidx.compose:compose-bom", version = "2023.01.00"}
compose-material = { module = "androidx.compose.material:material" }
compose-material-icons = { module = "androidx.compose.material:material-icons-extended" }
compose-ui = { module = "androidx.compose.ui:ui" }
compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }


// build.gradle

dependencies {

  // compose-ui
  implementation platform(libs.compose.ui.bom)
  implementation libs.compose.material
  implementation libs.compose.material.icons
  implementation libs.compose.ui
  implementation libs.compose.ui.tooling.preview

}

 

■ まとめ

BOM を使っていなかったのですが、エラーが出始めたので、使うことにしました。

「整合性の適正化」と「グループ化」の機能を持っている「BOM」は、Kotlin, Firebase などでも公開されています。

便利に利用するべきでしょう。

しかし、AndroidStudio のコレ。

JetpackCompose UI で BOM

「null」 の表示。

どうにかなりませんかね。

👉 今はまだ必要な Android ライブラリ の Bill of Materials(BOM) のURLs hatena-bookmark


【Git】.gitignore の状態を確認するコマンドたち

git status --ignored

まずは、状態の確認をしましょう。

以下は、基本的な Android プロジェクトでの例。


 

■ 位置を確認する


❯ find . -name .gitignore                          
./app/.gitignore
./.gitignore
./.idea/.gitignore

👉 AndroidStudio 新規プロジェクトテンプレートから考える .gitignore hatena-bookmark

 

■ git status


❯ git status          
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean

 

■ git status --ignored


❯ git status --ignored
On branch main
Your branch is up to date with 'origin/main'.

Ignored files:
  (use "git add -f <file>..." to include in what will be committed)
        .gradle/
        .idea/workspace.xml
        app/build/
        local.properties

nothing to commit, working tree clean

→ ignore 対象が確認できます。

 

■ git clean -ndX


❯ git clean -ndX
Would remove .gradle/
Would remove .idea/workspace.xml
Would remove app/build/
Would remove build/
Would remove local.properties

 

■ git check-ignore -v


❯ git check-ignore -v .gradle/           
.gitignore:2:.gradle    .gradle/

❯ git check-ignore -v .idea/workspace.xml
.idea/.gitignore:3:/workspace.xml       .idea/workspace.xml

❯ git check-ignore -v app/build/         
app/.gitignore:1:/build app/build/

❯ git check-ignore -v local.properties
.gitignore:15:local.properties  local.properties

❯ git check-ignore -v **/*
app/.gitignore:1:/build app/build
app/.gitignore:1:/build app/build/generated
app/.gitignore:1:/build app/build/generated/ap_generated_sources
app/.gitignore:1:/build app/build/generated/ap_generated_sources/debug
app/.gitignore:1:/build app/build/generated/ap_generated_sources/debug/out
app/.gitignore:1:/build app/build/generated/res
...

→ そのファイルを ignore するパターン記述を特定することができます。

 

■ git ls-files --other --ignored --exclude-standard


❯ git ls-files --other --ignored --exclude-standard
.gradle/7.5.1/checksums/checksums.lock
.gradle/7.5.1/dependencies-accessors/dependencies-accessors.lock
.gradle/7.5.1/dependencies-accessors/gc.properties
.gradle/7.5.1/executionHistory/executionHistory.bin
.gradle/7.5.1/executionHistory/executionHistory.lock
...

→ 実際に ignore されたファイル一覧が確認できます。Adroid プロジェクトでは出力行が多くなるので省略しています。

 

■ 参考

👉 Git のステータス: リポジトリの検査 | Atlassian Git Tutorial hatena-bookmark