ファイルツリー構造を表示 tree をインストール
はじめに
ファイルツリーの作成方法を紹介します。
ドキュメントを作成する時には重宝しています。
treeをインストール
Homebrewでtreeをインストールします。
$ brew install tree
出力
あとは、treeコマンドを入力すれば出力することができます。
# コマンド(パスがなければ現在のパスで出力されます) $ tree app/src/main/kotlin/com/sample/hoge/ # 出力 app/src/main/kotlin/com/sample/hoge/ ├── App.kt ├── MainActivity.kt ├── data │ └── ApiClient.kt ├── di │ └── AppModule.kt └── ui └── main ├── MainFragment.kt └── MainViewModel.kt
意外に便利。
Ktlintのフォーマットをビルド時に実行する
はじめに
今回はKtlintのフォーマットをビルド時に実行するように設定していきます。
- Ktlint + CI + Dangerを連携させてPRにコメントさせる
- Ktlint + GitHookを使ってコミット時にフォーマットを実行する
- AndroidStudioのGit管理機能でコミット時にフォーマットを実行する
これらの方法は紹介されていましたが、SwfitLintのようにRunScriptで実行されるのは魅力だったのでAndroidでも同じようにできないかと試行錯誤してみました。
当ブログでもそのうちそれぞれのやり方を比較する記事を書こうかと思います。
導入
まずは、公式ドキュメントにしたがって導入していきます。
今回は、ktlint用の ktlint.gradle を分けて作成しているので、ファイルツリーも一応載せておきます。
├── app │ ├── build.gradle ├── gradle │ ├── ktlint │ │ └── ktlint.gradle ├── .editorcondig
ktlint.gradle
configurations { ktlint } // ktlintの設定 task ktlint(type: JavaExec, group: "verification") { description = "Check Kotlin code style." classpath = configurations.ktlint main = "com.pinterest.ktlint.Main" args "src/**/*.kt", "--android", "--color", "--reporter=plain", "--reporter=checkstyle,output=${buildDir}/reports/ktlint-results.xml" // レポート出力 ignoreExitValue true // CIで異常終了させない } check.dependsOn ktlint // ktlintのフォーマット設定(自動修正) task ktlintFormat(type: JavaExec, group: "formatting") { description = "Fix Kotlin code style deviations." classpath = configurations.ktlint main = "com.pinterest.ktlint.Main" args "-F", "src/**/*.kt" }
build.gradle
apply from: '../gradle/ktlint/ktlint.gradle' dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) // Lint ktlint "com.pinterest:ktlint:0.35.0" ... }
Ktlintの設定
ktlintは最初に.editorconfigのスタイル設定を読み込みに行くため、そこに独自のスタイルを明記しておけば、そのスタイルに従ってlintがかけられます。
// 例 [*.{kt,kts}] indent_size=2 continuation_indent_size=4 insert_final_newline=unset max_line_length=off
ビルド時に自動フォーマット
ktlint.gradle
先ほど作成した ktlint.gradle
に以下のタスクを追加してください。
// ktlintのフォーマットをビルド時に実行 task ktlintFormatRun(type: JavaExec) { "./gradlew ktlintFormat".execute() }
※ もっと良い方法があったら教えてください。
まとめ
若干ビルドが遅くなるのが難点です。
Kotlinのコーディング規約
はじめに
今回は、Kotlinのコーディング規約作成しなきゃと追われている人たちに贈ります。
Kotlin公式コーディングスタイル
コーディング規約はJetBrainsがご丁寧に書いてくれています。
プロジェクトごとに規約考えるのではなく、素直にしたがっておくのがいいと思います。
Coding Conventions - Kotlin Programming Language
Android公式コーディングスタイル
- TODO:追加していく
それ以外
- TODO:追加していく
AndroidStudioフォーマットをマクロで登録する
はじめに
今回は、AndroidStudioでフォーマットのマクロを組む方法を紹介します。
これを設定しておくと、インデントが揃っていなかったり不要なスペースが入っていたり不要なインポートがされているということはおおよそ防ぐことができます。
チームないでコード規約を登録しておくと毎回意識しなくて済みますね!
Ktlintでもできるので、それはそのうち書くことにします。
最終的にはこのように一瞬でフォーマットされるマクロを作成することを目指します。
マクロのレコーディングを開始
まずはマクロを登録するためにマクロのレコーディングを開始しましょう。
このように、右下に「Macro Recording Started...」と表示されていれば開始されています。
コードをフォーマット
今度は実際にマクロを登録するために手順を踏んでいきます。順番が入れ替わってしまうと正しくフォーマットされないので気をつけてください。
Step1
「Optimize Imports」を実行してください。 これを実行することで不要なimportを削除してくれます。
Step2
「Reformat Code」を実行してください。 これを実行することで不要なインデントや空白を削除してくれます。
Step3
最後に「Save All」を実行して保存しておきましょう。
マクロのレコーディングを終了
マクロの設定は以上になるので、マクロを終了しましょう。
マクロの登録
マクロを終了させると、どんな名前で保存するか聞かれるので、とりあえず「Format and save all」としておきます。
マクロのキーを割り当てる
作成した「Format and save all」マクロにショートカットキーを割り当てましょう。
AndroidStudioのPreferencesを開いて検索バーに「Format and save all」と打ち込むとヒットすると思います。
表示されたマクロをタブルクリックすると、どのキーに割り当てるか尋ねられるので、「⌘S」を設定しましょう。
何でもいいので、自分の好きなキーに割り当ててください。
すでにキーが割り当てられていた場合に上書きしていいかアラートがでるので、入れ替わってよければ「Remove」を選択してください。
以上で設定完了です! 「⌘S」を押すだけでコードがフォーマットされます!
Android アプリ名を環境ごとに変更する
はじめに
今回はAndroidアプリで環境ごとにアプリ名を変更する方法をご紹介します。
どういったときに必要なのか
ここでいう環境というのは主にFlavorのことをさしています。
app/build.gradleで設定するあれです(説明雑)。
本番環境・開発環境・検証用などで分ける場合に、実機にアプリ名を見ただけて環境がわかると間違えて開発用でテストしてしまったり、なんてことも防げます。
実際に環境を選択してビルドしている開発者はほぼ間違えることがないかもしれませんが、それ以外の人がアプリ名を見ただけで判断できるとより安全です。
方法
今回は以下のようなアプリ名を設定してみます。
環境 | アプリ名 |
---|---|
本番環境 | HogeApp |
開発環境 | HogeApp_dev |
検証環境 | HogeApp_stg |
app/build.gradleのproductFlavorsの環境ごとに、resValueを使ってアプリ名を設定しましょう。
resValueで指定したものは string.xml に出力されます。(もともと string.xml に記載されている app_name は削除しておきましょう)
productFlavors { // 本番環境 production { resValue "string", "app_name", "HogeApp" } // 検証環境 staging { resValue "string", "app_name", "HogeApp_stg" } // 開発環境 development { resValue "string", "app_name", "HogeApp_dev" } }
以上!これだけでOKです!
最後に
Floverごとにフォルダを用意して string.xml を差し替える方法もありますが、非効率なのでこちらの方法をお勧めします!!
あと、FacebookSDKのように環境変数をBuildConfig ではなく string.xml で用意する必要がある場合はこれでやったほうが楽です。
Android File Transfer を利用してMacからAndroidのファイルを参照
はじめに
Android端末とMacでのファイルのやりとりについて紹介します。
いままで、MacからAndroidAndroidとMacのファイルの
Android端末とPC間でのファイル移動は、クラウドストレージや共有フォルダの利用、USBケーブルでの直接接続などいろいろな方法があると思います。
USBケーブルで直接接続してファイル移動する際、接続するPCがWindowsの場合はケーブルをつなぐだけで済んでしまう事が多いです。
接続するPCがMacの場合には、USBケーブルで接続しただけではAndroid端末内のフォルダが表示されないのですが、Macに「Android File Transfer」をインストールするとファイル移動できるようになります。
最近自分もMacとAndroidをUSB接続してファイル移動する機会がありましたので、今回はその備忘録です。
インストール
こちらからアプリをダウンロードしてください。
brew cask でもインストール可能です。
$ brew cask install android-file-transfer
補足
AndroidStudioでファイルを参照する場合はこちらをご確認ください!
Android RecyclerView で空の状態のViewを表示するAdapterDataObserverを作成
はじめに
今回は、RecyclerViewが空の状態に別のViewを表示する方法をご紹介します。
ListViewのEmptyViewみたいなものを想像していただければいいと思います。
ゴールとしては、RecyclerViewに setEmptyView(View)
を呼び出すだけで設定できるまでを目指します。
Step1: レイアウトを作っていく
今回作成したのはシンプルに、「RecyclerView」と「RecyclerViewが空の場合に表示するView」の2つを用意しました。
ここでポイントになるのが、この2つは両方とも match_parent にしている部分です。VISIBLEの状態で正しく表示されないと正しく動作しないので、tools:visibilityなどをつかってしっかりとレイアウトを作成してください。
<androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" tools:itemCount="6" tools:listitem="@layout/item_message" tools:visibility="gone" /> <LinearLayout android:id="@+id/emptyView" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" tools:ignore="UseCompoundDrawables" tools:visibility="visible"> <ImageView android:layout_width="114dp" android:layout_height="100dp" android:src="@drawable/ic_message_placeholder" android:tint="#aaaaaa" tools:ignore="ContentDescription" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="メッセージはありません" android:textColor="#aaaaaa" android:textSize="14sp" /> </LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
Step2: RecyclerView AdapterDataObserver をカスタム
RecyclerViewにはアイテムの変更を監視してくれている、「AdapterDataObserver」というものがあります。
今回はこれを使用して、アイテムが0個になった時を検知していきましょう。
実際に判定しているのは checkIfEmpty()
ですが、「初期化時」「アイテム挿入時」「アイテム削除時」にそれぞれ判定をいれています。
「初期化時」・・・EmptyViewが設定された時
「アイテム挿入時」・・・RecyclerViewにアイテムが追加された時
「アイテム削除時」・・・RecyclerViewからアイテムが削除された時
/** * RecyclerViewの空状態を監視 * * @property recyclerView RecyclerView * @property emptyView 空の場合に表示するView */ class RecyclerViewEmptyObserver( private val recyclerView: RecyclerView, private val emptyView: View ) : RecyclerView.AdapterDataObserver() { init { checkIfEmpty() } override fun onChanged() { checkIfEmpty() } override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { checkIfEmpty() } override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) { checkIfEmpty() } /** * 空かどうかを判定してViewの表示状態を切り替える */ private fun checkIfEmpty() { val adapter = recyclerView.adapter if (adapter != null) { val emptyViewVisible = adapter.itemCount == 0 emptyView.visibleOrGone(emptyViewVisible) recyclerView.goneOrVisible(emptyViewVisible) } } }
visibleOrGone
と goneOrVisible
についてはViewのExentionです。
こちらを参考にしていただければわかると思います。
Step2: RecyclerView Extension を作成
RecyclerViewのExtensionに setEmptyView(view: View)
を追加しておきましょう。
こうすることで、ActivityないしFragment側からこれを呼び出すだけで設定が完了します。
注意点としては、これを設定する前に RecyclerView の Adapter は先に設定しておきましょう。
/** * 空の状態のViewを設定 * * @param view View */ fun RecyclerView.setEmptyView(view: View) { val observer = RecyclerViewEmptyObserver(this, view) adapter?.registerAdapterDataObserver(observer) }
結果
以上これだけで、設定が完了です!!
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) recyclerView.adapter = adapter recyclerView.setEmptyView(emptyView) }
補足
通信などの非同期処理を実行して、アイテムを追加する場合はこの setEmptyView(view: View)
のタイミングを取得完了時に設定してあげればOKです。
Android Viewの表示切り替えをスッキリさせる
はじめに
Android ではご存知の通り、Viewの表示切り替えは ViewのVisibility を切り替えてあげれば変更することができます。
Visibility | 状態 |
---|---|
View.VISIBLE | 表示(デフォルト) |
View.INVISIBLE | 不可視化(見えなくなるが、サイズは維持される) |
View.GONE | 非表示(見えなくなる、かつサイズも維持されない) |
使いやすく
使いやすくするために以下のような拡張関数を用意してみましょう。
/** * VisibleとGoneを切り替える * * @param isVisible Visibleであるかどうか */ fun View.visibleOrGone(isVisible: Boolean) { visibility = if (isVisible) View.VISIBLE else View.GONE } /** * VisibleとInvisibleを切り替える * * @param isVisible Visibleであるかどうか */ fun View.visibleOrInvisible(isVisible: Boolean) { visibility = if (isVisible) View.VISIBLE else View.INVISIBLE } /** * GoneとVisibleを切り替える * * @param isGone Goneであるかどうか */ fun View.goneOrVisible(isGone: Boolean) { visibility = if (isGone) View.GONE else View.VISIBLE } /** * InvisibleとVisibleを切り替える * * @param isInvisible Invisibleであるかどうか */ fun View.invisibleOrVisible(isInvisible: Boolean) { visibility = if (isInvisible) View.INVISIBLE else View.VISIBLE } /** * VISIBLEに切り替える */ fun View.toVisible() { visibility = View.VISIBLE } /** * INVISIBLEに切り替える */ fun View.toInvisible() { visibility = View.INVISIBLE } /** * GONEに切り替える */ fun View.toGone() { visibility = View.GONE }
これらを用意することで以下のように省略できるようになりました!
val view = findByViewId(R.id.view) if (isViewVisible) { view.visibility = View.VISIBLE } else { view.visibility = View.GONE } ↓ val view = findByViewId(R.id.view) view.visibleOrGone(isViewVisible)