- 投稿日:2019-02-26T23:58:11+09:00
Kotlin用ドキュメント生成ツール「dokka」のセットアップ&操作方法
「dokka」とは?
Kotlin用のドキュメント自動生成ツールです。
Kotlin公式が提供しています。環境
- OS:macOS Mojave 10.14
- Kotlin:1.3.21
- Gradle:5.2.1
- dokka:0.9.17
セットアップ
appフォルダ配下の「bundle.gradle」にdokkaのプラグインと設定を追加します。
/app/build.gradleapply plugin: 'kotlin-android' + apply plugin: 'org.jetbrains.dokka-android' … + dokka { + outputFormat = 'html' + outputDirectory = "$buildDir/javadoc" + }ルート直下の「bundle.gradle」にdokkaのバージョンとクラスパスを追加します。
/build.gradlebuildscript { ext.kotlin_version = '1.3.21' + ext.dokka_version = '0.9.17' repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.3.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.dokka:dokka-android-gradle-plugin:${dokka_version}" } }実装
ドキュメント化したいメソッドやプロパティにドキュメンテーションコメント(KDoc)を付けます。
例
MainActivity.ktpackage com.example import android.os.Bundle import android.support.v7.app.AppCompatActivity /** * メインアクティビティ */ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } /** * 2つの数字を足す * * @param x 数字1 * @param y 数字2 * @return 数字1 + 数字2 */ fun add(x: Int, y: Int): Int { return x + y } }ドキュメントの生成・閲覧
Android Studioのターミナルで
./gradlew dokka
を実行すると、outputDirectory
で指定したフォルダ(/app/build/javadoc/)内にドキュメントが生成されます。「…javadoc/app/index.html」をWebブラウザで開くとドキュメントを閲覧できます。
非常にシンプルなデザインです。
おわりに
ここでは簡単にしか紹介していません。
dokkaの使い方についてもっと詳細に知りたい場合は参考リンクなどをご参照ください。KDocについては以下のページが参考になります。
https://kotlinlang.org/docs/reference/kotlin-doc.html
https://qiita.com/opengl-8080/items/fe43adef48e6162e6166参考リンク
- 投稿日:2019-02-26T22:17:36+09:00
ARCore01-HelloAR
專案位置
gitlab:https://gitlab.com/kangyueLuo/helloar
起源
對於擴增世界應用的喜愛,在加上 Google ARCore 降低了使用擴增實境技術的門檻,及 Youtube 上許多教學影片,幫助我推開了 AR世界的門。
參考資料
- 投稿日:2019-02-26T20:38:30+09:00
Dynamic Feature Moduleに対応したアプリを、単一apk化する
Dynamic Feature Moduleに対応すると、
./gradle assembleDebugとかの実行時、apkが複数作られるようになります。
AndroidStudioでビルドするときは ▶ ボタンで動くけれど、コマンドラインからのインストールだとadb install-multiple app.apk feature.apkみたいにしないといけない。
何より、複数apkだとDeployGateとかでの社内配布で困る…(というか無理)
という場合、以下のようなコマンドでuniversalなapkを作成できます./gradle packageDebugUniversalApkapkファイルは、
/app/build/outputs/universal_apk/${variant.name}/app-${variant.baseName}-universal.apkとかあたりに作られます。
※${variant.name}
と${variant.baseName}
は、gradleでの実行時のvariantのパラメータです。flavor
がdev
でbuildType
がdebug
だとすると、それぞれdevDebug
,dev-debug
という感じになります。なお、本件対応時、CIにて
Program type already present: android.databinding.layouts.DataBindingInfoのようなエラーがでたのですが、
com.android.tools.build
を最新(本記事執筆時点では3.3.1
)まで上げると解消しました。
こちらの内容ですね。
- 投稿日:2019-02-26T17:53:23+09:00
Toastには通知元アプリを識別する情報を表示した方が良いという話
概要
AndroidでToastを表示した際、通知元のアプリを識別してもらうために、Toastに何かしらのアプリ情報を表示した方が良いと思う、という開発者向けの話です。
Toastについて
Androidで簡易なメッセージをユーザに表示するには、Toastを使うことが多いと思います。Toastはダイアログと異なり、Activityが無くても表示することができます。具体的には、ServiceやBroadcastReceiverなどのバックグラウンドで動作するクラスでも、Contextが参照できれば呼び出すことができます。つまり、次のように別のアプリの上やホーム画面にToastを表示することができてしまいます。
Toastの問題
ここで問題になるのは、このToastを表示している元のアプリを識別することが出来ないという点です。
自分の端末では一時期、「Account has not been authenticated」という内容(うろ覚え)のメッセージが不定期に表示されるという事象が発生していました。恐らく何れかのアプリが何らかのイベントをトリガーにしてToastを表示していたのだと思いますが、そのアプリが何か分からず、面倒で放置してたらいつの間にか出なくなってしまったので、結局どのアプリだったのかは分からず仕舞いでした。
こうした、どのアプリが出したメッセージなのかが分からない状況は、ユーザにとって決して好ましい状態ではありません。実際自分は、表示されたメッセージに対応することができなかったどころか、正体不明のメッセージが表示されて気味が悪いという印象を持ちました。ユーザに対して配慮したつもりが、まったくの逆効果となっています。
想定される状況
画面を表示していない状態でToastが表示される場合、次のようなタイミングでToastを表示するアプリの存在が考えられます(他にもあると思いますが)。
- 起動中のService(Android 8.0以降はForegroundService)からの実行
- JobScheduler実行時
- BroadcastReceiver受信時(参考:暗黙的なブロードキャストの例外)
- プッシュ通知受信時
- 非同期処理を動かしたまま画面を閉じ、かつプロセスが生きたままの状態
対応
この状況に対して、開発アプリにはToastにアプリを識別する情報を表示するのが望ましいのではないかと考えました。この場合、次の2パターンがあるかと思います。
1. Toastにアプリ名を表示する
Toastにアプリ名を表示します。メリットとしては、通常のToast表示でも対応可能なことです。デメリットとしては、長い名前だとメッセージの邪魔になります。
2. Toastにアイコンを表示する
アプリを識別できるアイコンを表示します。メリットはメッセージ表示領域の邪魔にならないこと、デメリットはToastは標準で画像を表示することはできないので、別途Viewを作成するか、ライブラリを使用する必要があることです。
次の画像はDynamic Toastsライブラリを使用した場合の表示です。
最後に
個人的には、アイコンを表示するのが適当かと考えています。
注意点としては、いくらアイコンを付与したところで、ユーザがそのアイコンから元アプリを識別できなければ意味がないので、ランチャーアイコンや通知アイコンなど、ユーザが普段目にするアイコンを使用するのが適切かと思います。こうした「ユーザがToastの表示元を識別できない」問題を考えると、今後Toastの表示に何らかの仕様変更が加えられるような気はします。
- 投稿日:2019-02-26T13:57:20+09:00
UnityPurchasing: InitializationFailureReason.PurchasingUnavailable
The official document says that when this error happens it means
IAP may be disabled in security settings: iOS devices only
The system purchasing library may be outdatedMy case did not fall into any of the above but simply because I have not logged into my Google account on the test device yet.
If you encounter the above error on Android, it may help to check if the Play Store has been set up properly before trying anything else.
- 投稿日:2019-02-26T13:38:06+09:00
pageViewにscaleType="fitXY"を適用する方法
通常のimageViewならXMLファイルにscaleType="fitXY"を入れるだけで縦横比を無視して画面いっぱいに表示することができるかと思います。
しかし、pageViewの場合は何やらうまくいかなかったので。
何も手を加えなければ画像の縦横比を保ってしまうので画像が切れてしまいます。今回は、画像がページ全体を覆っている場合を想定しています。
tinderのスワイプ画面なんかがまんまこれですね。
アンドロイドで複数の実機で試したところ、画像のサイドが切れてしまったので対策しました。①最初に試したこと(失敗)
Glideを使う前のimageViewの段階で、setScaleType(ImageView.ScaleType.FIT_XY)を用いるImageView imageView = new ImageView(getActivity()); RequestOptions options = new RequestOptions() .placeholder(R.drawable.hoge1) .error(R.drawable.hoge2)); String link = ServerConfig.hoge3; imageView.setScaleType(ImageView.ScaleType.FIT_XY); Glide.with(getContext()).load(link).apply(options).into(imageView);無反応でした。
②optionsにcenterCrop()を追加してみる(失敗: 原因不明)
ImageView imageView = new ImageView(getActivity()); RequestOptions options = new RequestOptions() .centerCrop() .placeholder(R.drawable.hoge1) .error(R.drawable.hoge2); String link = ServerConfig.hoge3; Glide.with(getContext()).load(link).apply(options).into(imageView);なぜか無反応
③ optionsにfitCenter()を追加してみる(成功)
ImageView imageView = new ImageView(getActivity()); RequestOptions options = new RequestOptions() .fitCenter() .placeholder(R.drawable.hoge1) .error(R.drawable.hoge2); String link = ServerConfig.hoge3; Glide.with(getContext()).load(link).apply(options).into(imageView);fitCenter()でできてcenterCrop()でダメなのはなぜなのか。
ちょっと調べましたが出てこないので結果だけ。
- 投稿日:2019-02-26T13:05:41+09:00
巨大なファイルを含んだリポジトリの履歴を改変して GitHub にインポートする方法
概要
GitHub.com には巨大なファイルを含んだコミットに対して push 制限があります。
具体的には push をする際、 次のようなメッセージが表示されます
- コミットに 50MB を超えるファイルが含まれている場合は warning が表示されます
- warning は表示されるものの、リモートへの転送自体は成功します。(ただし非推奨)
remote: warning: Large files detected. remote: warning: File big_file is 55.00 MB; this is larger than GitHub's recommended maximum file size of 50 MB
- コミットに 100MB を超えるファイルが含まれている場合は error が表示されます
- リモートへの転送に失敗します
remote: warning: Large files detected. remote: error: File giant_file is 123.00 MB; this exceeds GitHub's file size limit of 100 MB上記の問題は、以下のいずれかの対応をすることで解決できます。
1. 対象ファイルが必要な場合は、Git-LFS を利用する Versioning large files
2. 対象ファイルが不要な場合は、リポジトリから取り除く Removing files from a repository's historyこの記事では、後者の巨大なファイルをリポジトリから取り除く 2 通りの手順について紹介します。
具体例
プロジェクトで利用するために、外部で構成管理されているリポジトリを GitHub.com にインポートするケースについて考えてみます。
例えば Android オープンソースプロジェクト(以下 AOSP)は android.googlesource.com で公開されていて 1,000 を超える Git リポジトリから構成されています。これらのリポジトリの中には、「巨大なファイルが含まれている、または過去に含まれていた」リポジトリがあり、GitHub.com でそのままインポートすることができません。
- 実際、見つかった巨大なファイルの多くは、通常のソースコードではなくビルド済みバイナリであったりテストデータであったりします。
次のステップでは、実在する外部管理のリポジトリを GitHub.com にインポートする処理を行います。
Step0 インポート時に git-push が失敗することの確認
巨大なファイルが含まれていることがあらかじめ判明している AOSP の platform/system/core.git を題材として、GitHub.com へのインポートに失敗することを確認します。
まずは以下の手順でインポートを試みます。
$ git clone --mirror https://android.googlesource.com/platform/system/core $ git remote add github $YOUR_GITHUB_URL $ git push --mirror github remote: error: GH001: Large files detected. You may want to try Git Large File Storage - https://git-lfs.github.com. remote: error: Trace: be6e0a48b32da493f6a767241ba85e9b remote: error: See http://git.io/iEPt8g for more information. remote: error: File libunwindstack/tests/files/offline/jit_debug_x86_32/libartd.so is 219.06 MB; this exceeds GitHub's file size limit of 100.00 MB ! [remote rejected] android-9.0.0_r16 -> android-9.0.0_r16 (pre-receive hook declined)ここで上記のエラーが出ました。エラー内容から 200MB を超える共有ライブラリ
libartd.so
がコミットされていたため push に失敗したことがわかります。次のステップでは、巨大なファイルの正体を特定します。
Step1 対象ファイルの特定する
200MB を超える共有ライブラリがコミットが含まれていたため、 GitHub.com への push に失敗しました。ところが実際は、この共有ライブラリは指定のパスには既に
libartd.so
は存在しません。$ test -e libunwindstack/tests/files/offline/jit_debug_x86_32/libartd.so次に、既に削除済みファイルであることを確認します
$ git log --diff-filter=D --summary -- libunwindstack/tests/files/offline/jit_debug_x86_32/libartd.so delete mode 100644 libunwindstack/tests/files/offline/jit_debug_x86_32/libartd.soつまり、例え削除済みであっても、巨大ファイルを含んだコミットがあったことには変わりないため、push に失敗したことがわかりました。このように push に失敗する前に、リポジトリの中に含まれた巨大なファイルをあらかじめ確認するためには、Atlassian の Reduce repository size で紹介されている
git_find_big.sh
を活用すると良いことが知られています。このスクリプトでは Git 内部の パックファイル を走査して、巨大なファイルのサイズ順に表示してくれます。今回扱う platform/system/core.git リポジトリを対象にgit_find_big.sh
を実行すると次の結果が得られます。All sizes are in kB's. The pack column is the size of the object, compressed, inside the pack file. size pack SHA location 224312 60015 70352dbb7a51e338096bb1305ea63641a39200cd libunwindstack/tests/files/offline/jit_debug_x86_32/libartd.so 28051 7497 e72e673ad74ff69060291ddab1a3989a7c6a8c07 libunwindstack/tests/files/offline/jit_debug_x86_32/libarttestd.so 10380 3225 92ed9915d4f9ea96a4accfcad82c657ff72f17fd libunwindstack/tests/files/offline/jit_debug_x86/libartd.so 8127 3048 ef1a6a1857bc2613a9ae162dd56f2cb67858eb3a libbacktrace/testdata/arm64/libskia.so 5968 2422 05278936be5fda0f62b5bd37b6951bcdbb5e8cd2 libunwindstack/tests/files/offline/jit_debug_arm/libart.so 5952 2401 09ba49532205981ff638c6935436cbb09d678aba libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libart.so 5400 2175 bed8e3595aa8cab7f381cfcb7aa05d10f010e687 libbacktrace/testdata/arm/libart.so 5294 2610 855905655e4c822794b6d66657c657b5307f17c9 libunwindstack/tests/files/offline/jit_debug_arm/libartd.so 5076 1392 092fc3aec90445662e23c2fdca8207e031602793 libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_test 4134 1369 871f6dc89e9fb88b8758f5ef2deb2224295f8411 libbacktrace/testdata/arm/libGLESv2_adreno.so上記の結果から、 50MB を超える巨大なファイルは
libartd.so
だけであることが明らかになりました。したがって、
1. 過去に管理されていた巨大なファイルが参照できなくても問題ないこと
2. 対象コミット以降のすべてのコミットハッシュ(SHA-1)が変わっても問題ないこと上記のポイントを理解したうえで、それでも問題がなければ、次のステップで、対象コミットを削除するためにコミット履歴を書き換えます。
Step2 コミット履歴の書き換え
Step2a コミット履歴の書き換え:
git-filter-branch
を用いた場合git-filter-branch コマンドを用いることで、コミット履歴を書き換えることができます。
すべてのブランチ・タグから対象の巨大なファイルを rm する場合は、次のコマンドで実現できます$ time git filter-branch --index-filter 'git rm --ignore-unmatch libunwindstack/tests/files/offline/jit_debug_x86_32/libartd.so' --tag-name-filter 'cat' -- --allオプションの説明:
--index-filter <command>
- 各コミットハッシュに対して
<command>
を実行します--tag-name-filter <command>
- コミット履歴の書き換えに際し、新しいコミットハッシュに対してタグを上書きするオプションです
<command>
の規則にしたがってタグ名を上書きします- 元のタグ名のままタグを上書きする場合
--tag-name-filter 'cat'
- すべて大文字でタグを上書きする場合
--tag-name-filter 'cat | tr [:lower:] [:upper:]'
- タグを上書きしても元のメッセージやタイムスタンプ、Tagger は変化しませんが、 タグにつけられた GPG 署名はストリップされます
-- --all
git-filter-branch
の対象とするコミットハッシュを取得する際のgit-rev-list
に渡すオプションです- 今回の目的はすべてのブランチ・タグから巨大なファイルを削除したいので
--all
としますファイル削除の確認手順
上記
git-filter-branch
を実行後、libartd.so
の削除が正しく行われたことを確認します。$ git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
git-filter-branch
によって退避されたrefs/original
のすべての参照を git-update-ref で削除します$ git reflog expire --expire=now --all
- アクセスできないすべての無効な reflog エントリを削除します
$ git gc --prune=now
- git-gc で GC を実行し、古いオブジェクトを削除して、パックファイルを更新します
その後
git_find_big.sh
を再実行しますAll sizes are in kB's. The pack column is the size of the object, compressed, inside the pack file. size pack SHA location 28051 7497 e72e673ad74ff69060291ddab1a3989a7c6a8c07 libunwindstack/tests/files/offline/jit_debug_x86_32/libarttestd.so 10380 3200 92ed9915d4f9ea96a4accfcad82c657ff72f17fd libunwindstack/tests/files/offline/jit_debug_x86/libartd.so 8127 3048 ef1a6a1857bc2613a9ae162dd56f2cb67858eb3a libbacktrace/testdata/arm64/libskia.so 5968 2417 05278936be5fda0f62b5bd37b6951bcdbb5e8cd2 libunwindstack/tests/files/offline/jit_debug_arm/libart.so 5952 2401 09ba49532205981ff638c6935436cbb09d678aba libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libart.so 5400 2170 bed8e3595aa8cab7f381cfcb7aa05d10f010e687 libbacktrace/testdata/arm/libart.so 5294 2607 855905655e4c822794b6d66657c657b5307f17c9 libunwindstack/tests/files/offline/jit_debug_arm/libartd.so 5076 1391 092fc3aec90445662e23c2fdca8207e031602793 libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_test 4134 1362 871f6dc89e9fb88b8758f5ef2deb2224295f8411 libbacktrace/testdata/arm/libGLESv2_adreno.so 3695 1604 7a30bfa440efc24d651b544163e5574d6b027a63 libunwindstack/tests/files/offline/offset_arm/libunwindstack_test上記のとおりようやく 50MB を超える
libartd.so
が存在しなくなっていることが確認できました。ところがここで一つ課題があります。
今回の作業は Azure D2V2 (Core=8, RAM=28GB, SSD=400GB) インスタンスで行いましたが、実行時間は
real 15m48.335s
と 15 分以上要しました。実行時間をより短くするために、次のステップでは、 BFG という機能を絞ったシンプルかつ高速なツールを紹介します。Step2b コミット履歴の書き換え:
BFG
を用いた場合BFG は Scala 製でシンプルかつ高速なツールです。
git-filter-branch
ほど複雑な処理はできませんが、今回の目的に最も適した--strip-blobs-bigger-than <MB>
という便利なオプションがあります。再度取得し直した platform/system/core.git にこのツールを適用してみます。$ java -jar bfg.jar --strip-blobs-bigger-than 50M platform/system/core.gitオプションの説明:
strip-blobs-bigger-than 50M
- 50MB より大きなすべての blob を削除します
実行結果
Scanning packfile for large blobs: 140503 Scanning packfile for large blobs completed in 1,009 ms. Found 1 blob ids for large blobs - biggest=229696508 smallest=229696508 Total size (unpacked)=229696508 Found 1897 objects to protect Found 687 tag-pointing refs : refs/tags/afw-test-harness-1.5, refs/tags/afw-test-harness-2.1, refs/tags/android-1.6_r1, ... Found 190 commit-pointing refs : HEAD, refs/heads/master, refs/remotes/origin/HEAD, ... Protected commits ----------------- These are your protected commits, and so their contents will NOT be altered: * commit f80c326d (protected by 'HEAD') Cleaning -------- Found 45661 commits Cleaning commits: 100% (45661/45661) Cleaning commits completed in 4,047 ms. Updating 89 Refs ---------------- Ref Before After --------------------------------------------------------------------------- refs/heads/master | f80c326d | 9a90afc4 refs/remotes/origin/master | f80c326d | 9a90afc4 refs/remotes/origin/master-cuttlefish-testing-release | fd088948 | 9c55624d refs/remotes/origin/nougat-iot-release | 044e0276 | 52cd619d refs/remotes/origin/o-mr1-iot-preview-7 | 2b49abe4 | 8ff6ed0b refs/remotes/origin/o-mr1-iot-preview-8 | 22dc27b9 | 219cb8c9 refs/remotes/origin/oreo-mr1-1.2-iot-release | c53a0e91 | 8e1c9575 refs/remotes/origin/oreo-mr1-iot-release | f307efcf | 9ee5bb5f refs/remotes/origin/pie-cts-dev | 2ef5c2ef | 1be06911 refs/remotes/origin/pie-cts-release | bfe37429 | 0710a778 refs/remotes/origin/pie-cuttlefish-testing | 17b9e8e0 | 7135e773 refs/remotes/origin/pie-dev | 820ef150 | c9998b31 refs/remotes/origin/pie-dr1-dev | 93d837f3 | bfe27d17 refs/remotes/origin/pie-dr1-release | 47e38865 | 92fcac25 refs/remotes/origin/pie-gsi | afc32531 | 788ecf39 ... Updating references: 100% (89/89) ...Ref update completed in 55 ms. Commit Tree-Dirt History ------------------------ Earliest Latest | | .....................................................DDmmmmm D = dirty commits (file tree fixed) m = modified commits (commit message or parents changed) . = clean commits (no changes to file tree) Before After ------------------------------------------- First modified commit | 150db124 | ecbcdafc Last dirty commit | e242a97d | cd89e6b1 Deleted files ------------- Filename Git id -------------------------------- libartd.so | 70352dbb (219.1 MB) BFG run is complete! When ready, run: git reflog expire --expire=now --all && git gc --prune=now --aggressive上記のような実行レポートのサマリーが得られます。
Deleted files
から何が削除されたかも明らかになりました。より詳細な実行レポートはリポジトリ脇のディレクトリ配下に次の形式で残されています。
cache-stats.txt
deleted-files.txt
object-id-map.old-new.txt
そして実行結果はわずか
real 0m9.403s
で、git-filter-branch
よりも 100 倍前後高速に結果が得られました。またgit_find_big.sh
の内容も Step2a と同一でした。Step3 インポート成功確認
最後に GitHub.com にインポートできることを確認します。
$ git push --mirror github今度は無事成功しました。
上記 Step2a, Step2b のどちらの手法を用いても巨大なファイルは削除され、
git-push
できるようになったことが確認されました。削除できない巨大なファイルが含まれている場合は
今回の例では、見つかった巨大なファイルが不要なため削除できるケースでしたが、削除できない場合はどうでしょうか。まずは Git LFS を利用するのが最も素直な解になります。それでもプロジェクトの都合で Git LFS が利用できない場合は巨大なファイルを分割したり、圧縮したりする回避策もあるかもしれません。
管理方法
$ md5sum $LARGEFILE > $LARGEFILE.md5 $ split $LARGEFILE -b 10M $LARGEFILE-
- 元ファイルのチェックサムと数 MB ごとに分割したファイルとして管理します
復元方法
$ cat $LARGEFILE-* > $LARGEFILE $ md5sum -c $LARGEFILE.md5
- githooks の
post-merge
やビルドシステムなどで fetch 後に復元処理と元ファイルとの完全性を確認しますただし what-is-my-disk-quota にもあるように GitHub ではリポジトリのサイズは 1GB 以下を推奨されているため、分割管理をしたとしてもサイズの総和の制約は回避できません。
まとめ
この記事では、不要かつ巨大なファイルが含まれたリポジトリを GitHub.com にインポートする手順についてまとめました。
git-filter-branch
でもBFG
でも不要なファイルを扱ったコミットを除去できることが確認できました。単純に巨大なファイルを削除するだけが目的ならBFG
を用いて、タグを一意の命名規則に沿って上書きしたり、ブランチ別に処理を分ける等、より複雑な操作をするときはgit-filter-branch
を用いると良いと思います。
- 投稿日:2019-02-26T02:26:37+09:00
【ImageView】ScaleTypeを変更したときの内部処理
ScaleTypeと表示画像の対応を整理するために作成したサンプルアプリで、
ScaleType
をFIT_START
→FIT_XY
→MATRIX
と切り替えたときにFIT_START
とMATRIX
のときの画像表示が同じになる現象が発生したので、備忘録を兼ねてその際に調査した内容をまとめました。ScaleTypeを変更したときの内部処理
ImageView.javaのソースはこちらにあります。
まず、
ScaleType
を切り替えるためにsetScaleType()
を呼び出します。
引数に指定されたScaleType
がmScaleType
に代入されます。ImageView.java#LL.827-838public void setScaleType(ScaleType scaleType) { if (scaleType == null) { throw new NullPointerException(); } if (mScaleType != scaleType) { mScaleType = scaleType; requestLayout(); invalidate(); } }
このmScaleType
はconfigureBounds()
内で分岐条件として使われます。
以下、今回の調査に必要な条件のみを抜粋します。①
mScaleType == MATRIX
のとき、以下のような処理が行われます。ImageView#LL.1253-1257if (mMatrix.isIdentity()) { mDrawMatrix = null; } else { mDrawMatrix = mMatrix; }
mMatrix
はMatrix
の参照を持つ変数です。mDrawMatrix
はmMatrix
の参照を代入するための変数です。この2つの変数によってMatrix
による画像変換を行うか否かを制御しています。
mMatrix
が単位行列(変換前後で画像の状態を変化させないMatrix
)の場合、mDrawMatrix
にnullを代入しています。②
mScaleType == FIT_CENTER、FIT_END、FIT_START
のとき、以下のような処理が行われます。ImageView#LL.1302-1306mTempSrc.set(0, 0, dwidth, dheight); mTempDst.set(0, 0, vwidth, vheight); mDrawMatrix = mMatrix; mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));ネイティブコードを呼び出していたので詳細は確認できなかったのですが、最後の
setRectToRect()
によってmScaleType
に対応したMatrix
を計算しているようです。
この処理によってmDrawMatrix
の参照先の内容が更新されることがEvaluate Expressionでトレースすることによって分かりました。③
mScaleType == FIT_XY
のとき、以下のような処理が行われます。
vwidth
、vheight
はそれぞれImageViewの幅と高さです。ImageView.java#LL.1244-1245mDrawable.setBounds(0, 0, vwidth, vheight); mDrawMatrix = null;
そして、mDrawMatrix
はonDraw()
内で分岐条件として使われています。
mDrawMatrix
がnullでないときのみMatrix
による画像変換が行われます。ImageView.java#LL.1350-1383@Override protected void onDraw(Canvas canvas) { ... if (mDrawMatrix == null && mPaddingTop == 0 && mPaddingLeft == 0) { mDrawable.draw(canvas); } else { ... if (mDrawMatrix != null) { canvas.concat(mDrawMatrix); } mDrawable.draw(canvas); canvas.restoreToCount(saveCount); } }
以上を踏まえ、改めて状況を整理します。
ScaleType
をFIT_START
→FIT_XY
→MATRIX
と切り替えたとき、
FIT_START
- ②の処理が行われるので、
mMatrix
の参照先のMatrix
の内容はFIT_START
に対応した値になるmDrawMatrix
はnullでないので、Matrix
による画像変換が行われるFIT_XY
- ③の処理が行われるので、
mMatrix
の参照先のMatrix
の内容はFIT_START
に対応した値のままになる
mDrawMatrix
はnullなので、Matrix
による画像変換は行われないMATRIX
- ①の処理が行われるので、
mMatrix
の参照先のMatrix
の内容はFIT_START
に対応した値のままになるmDrawMatrix
はnullでないので、Matrix
による画像変換が行われるとなるため、
FIT_START
とMATRIX
のときの画像表示が同じになっていました。最後に、この現象を回避するために
ScaleType
を変更する前にsetImageMatrix()
を呼び出します。ImageView.java#LL.871-884public void setImageMatrix(Matrix matrix) { // collapse null and identity to just null if (matrix != null && matrix.isIdentity()) { matrix = null; } // don't invalidate unless we're actually changing our matrix if (matrix == null && !mMatrix.isIdentity() || matrix != null && !mMatrix.equals(matrix)) { mMatrix.set(matrix); configureBounds(); invalidate(); } }
setImageMatrix()
の引数に新しい単位行列を代入することでmMatrix
が初期化されます。
- 投稿日:2019-02-26T00:00:40+09:00
Notificationのタイトルに色を付ける方法
こったプッシュ通知(notification)だと色付きのものをよく見るがそれをする方法
意外と調べても出てこなかったので掲載addColorMethod.ktprivate fun addColor(title: String): SpannableString { return SpannableString(title).apply { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { return@apply } setSpan(ForegroundColorSpan(ContextCompat.getColor(context, R.color.notification_job_title_text)), 0, title.length, 0) } }NotificationCompatBuilderでの利用法.ktNotificationCompat.Builder(yourContext, "yourId") .setContentTitle(addColor("your notification title")) .set .. // 中略 .build()現場からは以上です