Diary

Diary

日々学んだことをアウトプットする場として初めてみました

Jetpack Compose で横画面に対応する!

Jetpack Compose で横画面を検知しレイアウトを変更する』ことをやってみたいと思います。

環境

- build:gradle:7.0.4
- compose_version = '1.0.1'

レイアウト

次のように、横画面か縦画面かを判断しレイアウトを変更できるようにことがこの記事のゴールです。

縦画面

f:id:kokoichi206:20220205002300p:plain

横画面

f:id:kokoichi206:20220205002334p:plain

現在の画面の向きを取得する

Composable 関数の中で現在の画面の向きは、以下のメソッドで取得することができます。

LocalConfiguration.current.orientation

縦画面かどうかを判断する

従って、縦画面かどうかを変数として表すには次のようにConfiguration.ORIENTATION_PORTRAIT(縦画面)と比較してあげます。

isVertical =
    LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT

// 縦画面か横画面かに応じて次のいずれかが入る
// 縦
// Configuration.ORIENTATION_PORTRAIT
// 横
// Configuration.ORIENTATION_LANDSCAPE

縦画面や横画面の変更を検知する

画面の向きの変更に伴い、基本的には再描画が走るため、特に特別な処理は必要ないように思います。

val isVertical =
    LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT

サンプルプログラム

冒頭の写真の物を作った際のコードを貼っておきます。

サンプルコード

@Composable
fun ShowName() {
    val isVertical =
        LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT

    if (isVertical) {
        Column(
            modifier = Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center,
        ) {
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .weight(1f)
                    .background(Color.Blue),
                contentAlignment = Alignment.Center
            ) {
                Text(
                    "First Name!", fontSize = 24.sp,
                    textAlign = TextAlign.Center
                )
            }
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .weight(1f)
                    .background(Color.Red),
                contentAlignment = Alignment.Center
            ) {
                Text(
                    "Last Name!", fontSize = 24.sp,
                    textAlign = TextAlign.Center
                )
            }
        }
    } else {
        Row(
            modifier = Modifier.fillMaxSize(),
            verticalAlignment = Alignment.CenterVertically,
            horizontalArrangement = Arrangement.Center,
        ) {
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .weight(1f)
                    .background(Color.Blue),
                contentAlignment = Alignment.Center
            ) {
                Text(
                    "First Name!", fontSize = 24.sp,
                    textAlign = TextAlign.Center
                )
            }
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .weight(1f)
                    .background(Color.Red),
                contentAlignment = Alignment.Center
            ) {
                Text(
                    "Last Name!", fontSize = 24.sp,
                    textAlign = TextAlign.Center
                )
            }
        }
    }
}

Links

Re-gaining orientation #1

おわりに

今回は Jetpack Compose で『縦画面と横画面を検知しレイアウトを変更する』ということをやってみました。

Jetpack Compose で3点ボタンのドロップダウンメニュー

成果物

今回は、Jetpack Compose で以下のようなものを作りたいと思います。

  • 縦に3つ点の並んだアイコンを表示
    • MoreVert と言うらしいです
      • More read, vertical ??
  • クリック時にそこからメニューを表示させる

写真

クリック時の挙動

環境

- build:gradle:7.0.4
- compose_version = '1.0.1'

実装

色々と頑張って作ったのですが、きちんとドキュメントに書いてありました。。。そのため説明は割愛します。。。

また、どんなアイコンが利用可能かは fonts.google.com をみると良いです。

// メニューの開閉をコントロールするため
var expanded by remember { mutableStateOf(false) }
Box(
    modifier = Modifier
        .wrapContentSize(Alignment.TopStart)
) {
    IconButton(
        onClick = {
            expanded = true
        }
    ) {
        Icon(
            Icons.Filled.MoreVert,
            contentDescription = Constants.DESCRIPTION_MORE_VERT_ICON,
            tint = MaterialTheme.colors.surface,
        )
    }
    DropdownMenu(
        modifier = Modifier
            .background(MaterialTheme.colors.background)
            // タップされた時の背景を円にする
            .clip(RoundedCornerShape(16.dp))
            .background(MaterialTheme.colors.onSurface),
        expanded = expanded,
        // メニューの外がタップされた時に閉じる
        onDismissRequest = { expanded = false }
    ) {
        DropdownMenuItem(
            onClick = {
                // Do something
            }
        ) {
            Text(
                text = "1",
                fontSize = 24.sp,
                color = Color.White,
            )
        }
        DropdownMenuItem(
            onClick = {}
        ) {
            Text(
                text = "2",
                fontSize = 24.sp,
                color = Color.White,
            )
        }
        DropdownMenuItem(
            onClick = {}
        ) {
            Text(
                text = "3",
                fontSize = 24.sp,
                color = Color.White,
            )
        }
    }
}

おわりに

Jetpack Compose で実装できる範囲が広がってきてとても楽しいです。

今後もできることが増えたら発信していけたらと思います!

android でメモリーリークを調べてみた

モリーリークとは

Java のランタイムの文脈においてメモリーリークとは、ガベージコレクション (GC) と強く関連しています。具体的には、必要無くなったオブジェクトの参照を持ち続け、GC が削除をいつまでも行わないことを指します。

すぐにバグを引き起こすわけではありませんが、リソースが限られている Android においては OOM (out of memory) crash を引き起こす可能性があります。

また、GC を挙動を知る必要もあるため、リークを検知するのも防ぐのも簡単ではありません。

[目次]

環境

- gradle:7.0.4
- kotlin-gradle-plugin:1.5.21
- targetSdk 31
- leakcanary-android:2.8.1'

android で検知する

android で簡単にメモリーリークを調べるには、LeakCanaryというライブラリを使用します。

依存関係を追加する

dependencies {
    ...
    // to detect memory leak
    debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1'
}

使い方

上で依存関係を導入したら、あとはアプリを debug モードでビルドするだけです。

ビルドが完了すると、作成中ののアプリとは別アプリとして、以下のアイコンのアプリがインストールされています。

f:id:kokoichi206:20220204000703p:plain

このアプリを開いてみると以下のような画面が表示されます。

f:id:kokoichi206:20220204002615p:plain

このままだと面白くないので、意図的にメモリーリークを発生させてみます。

モリーリーク発生時

モリーリークが検知されると、アプリ内に以下のような通知が出ます。

f:id:kokoichi206:20220204002823p:plain

より詳細ページを見てみます。

f:id:kokoichi206:20220204003004p:plain

f:id:kokoichi206:20220204003231p:plain

どこで発生したかや、どのくらいの量のメモリーリークが発生しているか、その詳細が記載されています。

使用できない!(インストールできない)

debug モードでビルドするだけなのですが、自分の場合は Logcat に以下のようなメッセージがでて使用できませんでした。

D/LeakCanary: LeakCanary is currently disabled: test class org.junit.Test was found in classpath.
D/LeakCanary: Setting up flushing for Thread[LeakCanary-Heap-Dump,5,main]

そこで、こちらのドキュメントに従い次の1文をres/values/strings.xmlに追加することで解決しました。

<resources>
    ...
    <!-- to detect memory leak -->
    <string name="leak_canary_test_class_name">assertk.Assert</string>
</resources>

この時は Logcat のメッセージが以下のようになっていて、検知する準備が整っていることがわかります。

D/LeakCanary: LeakCanary is running and ready to detect memory leaks.
D/LeakCanary: Setting up flushing for Thread[LeakCanary-Heap-Dump,5,main]

おわりに

ドキュメントにはリソースファイルをどこに追加するかが書かれてなかったので少し戸惑いましたが、今作ってるアプリにメモリーリークがなくてよかったです。

定期的に確認していきたいと思います(もしくは自動で確認するフローを作りたいです)

android studio のメソッドが真っ赤になった!

android studio のメソッドが真っ赤になった!

テストメソッドのほとんどのファイルで、多くのメソッドが真っ赤になる現象が発生しました。

import 文を見て、赤文字の対象となっていたものには以下のようなものがありました。

androidx.test.xxx
org.junit.Assert.*

試してみたこと

以下のようなことをしてみましたが、一向に解決しませんでした。

  • FIle > Invalidate Caches/Restart
  • Android Studio 再起動
  • .idea フォルダの削除
  • PC の再起動

解決方法

Build Variants を Release -> Debug にすると解決しました

テストクラスでのみエラーが出ていた時点で、もっと早くに気づくべきでした。

Jetpack Compose でタイマー処理を実装する

タイマーを Android で作りたいことがあり、その際 Jetpack Compose で定期ループみたいなもの実装しました。

Kotlin で実装を行うには Timer や Runnable を用いることが多いそうですが、Jetpack compose では以下のようにLaunchedEffectdelay を用いて簡単に実装できます

// 監視対象とする変数
var deadLine by remember {
    mutableStateOf(20000)
}
var cTime by remember {
    mutableStateOf(10000)
}

LaunchedEffect(key1 = deadLine, key2 = cTime) {
    if (cTime > 0) {
        // delay で指定秒数(ミリ秒)だけ間隔を開ける
        delay(100)
        // 時間の更新処理
        cTime -= 100
        // (必要なら)update 処理
    }
}

key1, key2 に指定したキーを監視し、値が変更されるとLaunchedEffectの中身が実行されます。(keyは1つでも構いません。)

LaunchedEffectの中身で監視対象であるcTimeの更新をおこなっているので、ブロック終了後に再度LaunchedEffectが呼ばれます

これが上記コードがタイマー処理として働く理由となります。

おわりに

今回自分も初めて使ってみましたが、思ったより簡単にかけました。 今後も機会があれば使っていきたいと思います。

androidTest が表示されなくなった!

android でリリースに向けて色々といじってた際、左上のタブでAndroidを選択している状態で androidTest が表示されなくなりました。

Android Studio の再起動や、『File > Invalidate Cache / Restart』をやってみても解決しませんでした。

最終的にはこちらの Stack Overflow に従い、app/build. gradle に下記内容を加えることで解決しました(その後に Invalidate Cache / Restart )

android {
    ...
    sourceSets {
        main { java.srcDirs = ['src/main/java'] }
        test { java.srcDirs = ['src/test/java'] }
        androidTest { java.srcDirs = ['src/androidTest/java'] }
    }
    ...
}

原因はわかってないですが、とりあえず解決したのでよしとします。

GitHub 上でコミットを確認する2つの方法

GitHub でコミットの確認をしようと思った際、「独立した Commits 確認ボタン」がなかったので探すのに少し時間がかかりました。

URL に打ち込む

特定のコミットを確認する

https://github.com/<name>/<repository-name>/commit/<commit-hash>

https://github.com/<name>/<repository-name>までは、いわゆるリポジトリまでの URL です。

過去コミット一覧を確認する

https://github.com/<name>/<repository-name>/commits/<branch>

<branch>の部分にはブランチ名 (main など) が入ります。

Code 欄から飛ぶ

GitHub の Code 欄をひらき、下図赤枠の部分を確認します。

f:id:kokoichi206:20220201082305p:plain