自作ツールの UI を自慢したい — PrismDesk のデバイスプレビュー
PrismDesk の Android アプリの設定画面にある「端末にどう映るか」のプレビュー。Canvas 1 枚で、実機の角丸とカメラの切り欠きまで描いて回している。その作りと、周りの実装の話。
目次
はじめに
8月ごろから本格的な開発を始めた PrismDesk というツールについて、Android版の設定UIが気に入っているので自慢したい。
PrismDesk というのは Windows の画面を Android 端末で拡張したり複製したりすることができるツールで、この記事を書いている時点ではクローズテストを行っている。
この記事では、設定UIの中でも特に自信のある デバイスプレビュー を中心に、実装やその意図について書いていく。
デバイスプレビューとは
PrismDesk の Android アプリでは、画面の向き・拡張か複製か・解像度・リフレッシュレート・fps を、繋ぐ前に設定できる。ただ、いずれも数字や選択肢でしかなく、変えた結果どう映るのかは繋いでみるまで分からない。「向きを縦に固定したのに、横長の解像度を選んでいた」という食い違いが起こると、嫌な気持ちになる。
そこで、いまの設定を この端末に当てはめるとどう見えるか を、設定画面に 1 枚の絵で出すことにした。これがデバイスプレビューというもの。


描いているのは次の通り。
- 端末の外形。角の丸みとカメラの切り欠きは OS から実際の値を取るので、機種ごとに形が変わる
- 選んだ解像度の画面。端末の画面と同じ縮尺で収める
- 右上に fps とリフレッシュレート。fps の方が大きいと赤くなる
- 左上に拡張/複製の記号
切り欠きは DisplayCutout.cutoutPath が取れればその形をそのまま描き、取れなければ boundingRects から推測する (ほぼ正方形ならパンチホールとみなして円で描く)。切り欠きの無い端末では、上下のベゼルを太くしてそこにカメラの穴を描いている。どれも Compose の Canvas 1 枚で描いていて、子の Composable や独自の View は使っていない。
なお、手元にある Android 16 のタブレットは切り欠きを取得できなかったので、案外形は外れるのかもしれない。
端末は回っても、中身は水平のまま
この部分が個人的には気に入っている。画面の向きの設定を変えると、端末の絵が 0.5 秒で回る。そして中に映っている画面は回らずに水平のまま、端末の画面に収まり直す。

縦持ちのタブレットに横長の画面を映すと実際にこう見える、というのをそのまま絵にしたかった。やっていることは、単純に回転を入れ子にしているだけ。
rotate(angle) { // 端末を回す
drawPath(outlinePath) // 外形
clipPath(displayPath) { // 画面の形で切り抜く
clipPath(cutoutPath, ClipOp.Difference) { // 切り欠きをくり抜く
rotate(-angle) { // 中身だけ回転を戻す
scale(contentScale) {
drawRect(/* 選んだ解像度の画面 */)
}
}
}
}
}端末を回したあとで中身だけ逆に回すので、中身は水平のまま、切り抜く形だけが端末と一緒に回る。余白 (実際に繋ぐと黒帯になる部分) も特に計算していない。中身の倍率を min(画面の幅 / 解像度の幅, 画面の高さ / 解像度の高さ) で決めて収めれば、余ったところが自然に余白になる。
回転・倍率・記号の位置はどれも tween(500, EaseOutExpo) で動かしていて、最初に勢いよく動いてすっと止まる。EaseOutExpo が好きなのでこの動作がしっくりくる。
角丸に文字がかからないように
小ネタ。左上の記号と右上の fps は画面の隅に置いているが、最近の端末は画面の角が大きく丸まっているので、ただ隅に寄せると角丸に欠けてしまう。かといって角の半径 r だけ離すと、今度は隅から離れすぎて間が抜ける。
そこで、角から 45° の方向に、角丸の弧まで の距離だけ離している。角丸の中心は角から (r, r) の位置にあり、45° 方向の弧の上の点は中心から各軸 r/√2 だけ角に寄った所にある。なので角から弧までは、各軸で r − r/√2 = r(2 − √2)/2 ≈ 0.29r になる。
val leftTopMargin = r_tr * (2f - sqrt(2f)) * 0.5fr には OS から取った右上の角の半径をそのまま使っている。
回したら壊れた
実機での確認項目を全部通したあとで、プレビューまわりの不具合がいくつか見つかったことがある。原因は単純で、確認のあいだ端末を 1 度も回していなかった。
1 つ目は、端末を回してもカメラの切り欠きが前の向きの位置に残る不具合。原因が 2 つ重なっていて、片方だけ直しても直らなかった。
- 切り欠きは
ViewCompat.getRootWindowInsetsから読んでいたが、これはその場で値を取るだけで、変化を購読していない。回転しても描き直すきっかけにならない - 切り欠きの形を組む
rememberに key が無く、最初の 1 回しか組まれていなかった
Compose 側の WindowInsets.displayCutout を購読のためだけに 1 つ読み、回転や窓の大きさを remember の key に入れて直した。
2 つ目は、縦に回しても LocalConfiguration の screenHeightDp が横向きのときの値のまま残り、プレビューが半分以下の大きさで描かれる不具合。こちらは根本の原因を特定できていない。LocalConfiguration を読むのをやめて、LocalWindowInfo.current.containerSize から窓の大きさを取るようにした。
grep で守る
どちらも実機で回したときにしか出ない不具合で、テストで再現するのは難しい。Compose の画面は JVM のテストでは組み立てられず、壊れ方は「回したときだけ前の向きの絵が出る」というもの。
そこで、テストはソースコードを読むだけにした。DevicePreview.kt と SettingsScreen.kt からコメントを取り除いたうえで、LocalConfiguration が出てきたら落ちる。containerSize を読んでいなくても落ちる (禁止だけにすると、何も読まなくても通ってしまう)。
雑に見えるが、守りたいのは「この API を使わない」という約束そのものなので、これが一番確実で安い。
設定画面の分け方もプレビューから決めた
設定画面は「分類の一覧 → 分類ごとの画面」の 2 段になっている。どの項目を同じ画面に置くかは、プレビューが読む値から決めた。向き・モード・解像度・リフレッシュレート・fps の 5 つは必ず同じ画面に置き、プレビューもその画面にだけ出す。絵と、絵を変える設定が別の画面にあると、変えた結果がその場で見えなくなるからだ。
コードの上でも「この分類はプレビューを出す」と手で印を付けるのではなく、5 つを全部含む分類がプレビューを持つ、という形で導いている。
プレビューの置き場所は、実機で 2 回やり直した。
- 分類の一覧の下に置いた → どの分類を選んでも絵が変わらないので、ただの飾りに見えた
- 右側の上半分に置いた → 横持ちのスマホは高さが 360dp ほどしかなく、設定の方が隠れた
いまは、横長の画面ではプレビューと設定を左右に半分ずつ、縦長の画面ではプレビューを上に置いて高さの 4 割までにしている。


ブラウザでも触れるようにした
PrismDesk の公式サイトのトップには、このプレビューをブラウザで触れるデモを置いている。プレビューが Canvas 1 枚で描かれていたおかげで、同じ式・同じ寸法のまま SVG と素の JavaScript に移すことができた (Compose の Path は SVG の d に、入れ子の回転もそのまま)。
アプリより少し高機能で、隣に PC のモニタを並べ、ウィンドウを PC から端末へドラッグできるようにしてある。端末の絵だけだと、拡張と複製の違いが伝わりにくかったからだ。

おわりに
デバイスプレビューは見た目こそ単純な絵だが、数字の設定を目で見える形にするために、幾何・アニメーション・状態の購読・画面の構成・テストまで手を入れることになった。だからこそこうして自慢したくなった。
PrismDesk は 2026 年 10 月 14 日に公開予定で、無料版でも端末 1 台をサブモニタとして使える。HDR や複数台の同時接続が使える Pro 版は US$22 の買い切り。作った側から見た中身の話は ツールページ に、製品の紹介は公式サイトにまとめてある。デバイスプレビューもサイトのデモで触れるので、ぜひ回してみてほしい。
PrismDesk — Android タブレットを Windows のワイヤレスなサブモニタに手元の Android タブレットを、Windows 11 のワイヤレスなサブモニタに。設定作業なし、切れても自動で復帰、HDR とペン入力に対応。性能は実測値で公開しています。無料版あり・アカウント不要。
不具合の報告や要望は GitHub で受け付けている。
GitHub - Asynchronous-0x4C/prismdesk-app: Documentation, issue tracker and releases for PrismDesk - a wireless second monitor for Windows, using an Android tablet.Documentation, issue tracker and releases for PrismDesk - a wireless second monitor for Windows, using an Android tablet. - Asynchronous-0x4C/prismdesk-app