- 投稿日:2020-01-20T23:53:51+09:00
Androidアプリ開発でのSonarQube~Java~
はじめに
JavaでAndroidアプリの開発を行うときにSonarQubeを導入した時の手順を記載していきます。
ミニマムで導入した手順です。
Kotlinはもう少しシンプルにできると思います。環境
OS:Windows 10(64bit)
Java:11
Android Studio:3.5SonarQube
SonarQubeとは静的解析ツールの一つでJava, Kotlin, Swift, Goなど25以上の言語をサポートしているようです。
JenkinsやAzure DevOpsと連携することでCIツールの強化を図ることができるようです。詳細は公式ドキュメントを参照してください。
導入手順
基本的には公式のGet Started in Two Minutes Guideに沿って行きます。
1.SonarQube Serverのインストール
以下のリンクからDownloadします。
Download SonarQube適当な場所に展開します。
今回は以下の構成にします。
C:\SonarQube\SonarQubeServer2.SonarQubeServerを起動
以下にあるbatを実行するだけです。
C:\SonarQube\SonarQubeServer\bin\windows-x86-64C:\SonarQube\SonarQubeServer\bin\windows-x86-64>StartSonar.bathttp://localhost:9000/ にアクセスするとSonarQube Serverの画面を確認できます。
3.Gradleの設定
プロジェクト配下のbuild.gradleの編集を行います。
build.gradleの先頭に以下を追記します。plugins { id "org.sonarqube" version "2.7" }4.gradle.propertiesの設定
ここからはアプリ側の設定を行っていきます。
公式ではSonarScanner for Gradleを参考にしてます。
gradle.propertiesに以下を追記します。# gradle.properties systemProp.sonar.host.url=http://localhost:9000 #----- Token generated from an account with 'publish analysis' permission systemProp.sonar.login=<token>tokenはSonarQube Serverから取得します。
5.Tokenの取得
SonarQube Serverに戻ります。
(1) 右上のLog inからログインユーザー名:admin パスワード:admin(2) 右上のユーザーアイコンのMy Accountから管理者画面に移動
(3) Securityのタブを選択
(4) Tokensのところにプロジェクト名を入れてTokenを取得6.実行
以下のいずれかで静的解析を実行
(1) Android SrudioのGradle > app > Tasks > other > sonarqubeをダブルクリック
(2) プロジェクト配下で以下を実行
./gradlew sonarqube7.確認
http://localhost:9000 にアクセスしてappが表示されれば完了
参考文献
・公式
・SONNARQUBEでソースコードの品質を解析する
最初にSonarQubeを導入するにあたって参考にさせていただきました。Kotlinのプロジェクトはこちらの記事で解析まで行うことができました。
- 投稿日:2020-01-20T17:33:19+09:00
[Android] プログレスダイアログ(通信中のぐるぐる)
*2020/1/21追記
API24で実装しています。API26からは非推奨となっているようです。
実装方法
*非同期処理クラスの処理が終了したらBroadcastによりonReceiveが呼ばれる例を使っている。
public class MainActivity extends Activity{ public ProgressDialog progressDialog_ = null; @Override protected void onCreate(Bundle savedInstanceState) { //通信前にダイアログ表示 progressDialog_ = new ProgressDialog(this); progressDialog_.setMessage("実行中..."); progressDialog_.show(); //非同期処理クラスを呼び出して通信処理 } public class MyReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { // プログレスダイアログを閉じる if (progressDialog_ != null && progressDialog_.isShowing()) { progressDialog_.dismiss(); } } } }結果
感想
とても簡単なのでぜひ使ってみてほしい。
参考URL
- 投稿日:2020-01-20T16:00:04+09:00
ダークモードとは何か?各OSの設定方法からiOS/Androidアプリの対応方法まで
はじめに
勉強会をご覧の皆さま
元々のテーマは「10分でわかるダークモード対応」でしたが、あちこち調べているうちにページ数が30を超えてしまい、「10分ではわからないダークモード対応」になりました?♂️
20分には収めます
ダークモードとは?
OSシステムレベルで設定可能な、画面表示色の設定です。
Light Modeが従来通りの白基調な画面、Dark Modeは黒基調な画面です。
何が明るく/暗くなるの?
特定の何に適用すべきという法則は無いようですが、閲覧者に見せたいコンテンツ以外のウィンドウ枠、背景などに対して適用されることが多いようです。
もちろん、メインコンテンツ(テキスト色など)も背景色に応じて調整する必要があります。
ダークモードにすると何がいいの?
- 目へのダメージを抑える
- 眩しい画面を見ることによるドライアイ、頭痛、メラトニン抑制による不眠など
- バッテリーの持ちが長くなる
- OLEDスクリーンで輝度が100%の場合、DarkはLightと比較して約60%のエネルギーを節約。輝度が50%の場合、約15%節約(YouTubeアプリで計測)
- 背景は明るいより暗いほうが、メインのコンテンツを目立たせ、作業に集中しやすくする
ダークモードにする場合のデメリットや注意点
- 直射日光の下などの明るい場所で読みづらい
- ナイトモード(ブルーライトカットの)と同じように、日没〜日の出までダークモードとする設定が用意されている
- テキスト色と背景色の逆転にOCRが追いついていない
- ダークモードで撮ったスクショをGoogle翻訳にかけても(現時点では)翻訳してくれない?
ダークモード対応OS
- Windows 10 version 1903〜
- Mac OS Mojave(10.14)〜
- Android 10〜
- iOS 13〜
ダークモード設定方法
- Windows
- スタート -> 設定 -> 個人用設定 -> 色
- macOS
- -> システム環境設定 -> 一般 -> 外観モード
- Android
- 設定 -> ディスプレイ -> テーマ
- Zenfoneの場合は 設定 -> ディスプレイ -> システムカラースキーム
- クイック設定パネルに追加したい場合は 画面上端を下にスワイプ -> ?
- iOS
- 設定 -> 画面表示と明るさ -> 外観モード
- コントロールセンターに追加したい場合は 設定 -> コントロールセンター -> コントロールをカスタマイズ
iOS/Androidアプリでのダークモード対応方法(アプリエンジニア向け)
iOS
準備
- Xcode 11以上のビルド環境
- iOS 13以上の実機orシミュレーター
iOSダークモード対応は急務!!
- iOSアプリが2020年4月以降、iOS 13 SDKビルドでの審査提出が必須となる
- iOS 13 SDKビルドはデフォルトでダークモードがONになる
UIUserInterfaceStyle
を使えば一時しのぎ出来るが、公式は移行を強く推している
- 将来効かなくなる可能性が高い
まずダークモードの見え方を確認しよう!
StoryboardやXibのInterface Builder上でLight/Darkを切り替える
デバッグ実行中にLight/Darkを切り替える
Darkにして、どうなったか。弊社案件の場合
- テキストが自動で白くなったが背景も白い。文字が読めない!
- 背景が自動で黒くなったがテキストも黒(
- 濃緑基調のグラフィックが黒い背景では見えづらい
- QRコードが真っ黒黒助
何故そうなるのか
- 場所によってiOS System Colorsを使ったり使わなかったりするため
- 大人数のプロジェクトでありがちな統一感の甘さ
- とはいえiOSのStoryboardを完璧な統一感で作った人は見たことありませんが…?
対応作業
カラーセットを使う場合
- ハードコードしている色をAssets CatalogのColor Setに移行する
- Color SetのAppearancesにてDarkを追加する
- Dark Appearanceにフォーカスを合わせ色設定する
- 色は従来のsRGB方式以外に、iOS System Colorsも選択可能
darkTextColor
やsystemBackgroundColor
など- 使うとOS側が色の濃さを自動調整してくれる。便利!
- 詳しくは参考記事の実践 iOS13ダークモード対応に、わかりやすく書かれています
カラーセットを使わない場合
- アプリ起動中にLight/Darkを切り替えられても即反映させたいので、次のいずれかの方法で実装
- dynamicColor方式
extension UIColor { private class func dynamicColor(light: UIColor, dark: UIColor) -> UIColor { if #available(iOS 13, *) { return UIColor { (traitCollection) -> UIColor in if traitCollection.userInterfaceStyle == .dark { return dark } else { return light } } } return light } public static var textColor: UIColor { return dynamicColor( light: UIColor(displayP3Red: 0, green: 9, blue: 11, alpha: 1), dark: UIColor(displayP3Red: 255, green: 246, blue: 244, alpha: 1) ) } }
- dynamicProvider方式
extension UIColor { @available(iOS 13.0, *) convenience init(light: UIColor, dark: UIColor) { self.init { if $0.userInterfaceStyle == .dark { return dark } else { return light } } } @available(iOS 13.0, *) public static let textColor = UIColor( light: UIColor(displayP3Red: 0, green: 9, blue: 11, alpha: 1), dark: UIColor(displayP3Red: 255, green: 246, blue: 244, alpha: 1) ) }こちらはSDKバージョンの制限を受けますが、initとして処理を書けて、定数をimmutableにできます。
Android
準備
- Android Studio 3.3以上のビルド環境
- Android 10(Q)以上の実機orシミュレーター
まずダークモードの見え方を確認しよう!
Design View上でLight/Darkを切り替える
開発者向けオプションを使う
- システム -> 詳細設定 -> 開発者向けオプション -> フォースダークのオーバーライド
- アプリの実装を変えずダークモードをシミュレートできる
Darkにして、どうなったか。弊社案件の場合
- Design View上では何も変わらなかった…
- Android版は
?android:attr/textColorPrimary
などのシステムカラーを使っていなかったから- Androidチームはデザイン指示書に厳密に従ったので、システムカラーを使わなかった
- でも、開発者向けオプションだといい感じのダークモードになった
- iOSで起きてた問題は一切起きなかった(細かい部分を見ると直したい色はあったが)
- Android10の強制ダークモードは割としっかりダークしてくれるの例と同じような結果だった
対応作業
styles.xml
やcolors.xml
を別々に用意する
- Light:
res/values/*
- Dark:
res/values-night/*
- 共通のものはLight側にまとめよう
- Darkの方には、夜間モードかフォースダークを適用する(後述)
- 2つの重ね掛けはできないらしい
- もしくは、iOSのSystem Colorsに相当するテーマの属性やマテリアルデザイン コンポーネントを利用することもできる。厳密にカラー指定しない場合はこちらがオススメ
- 色のハードコードは厳禁らしい
夜間モード機能を使う
- アプリを新規作成すると自動で
Theme.AppCompat.Light
系のテーマが適用されているが、Dark側のスタイルにはTheme.AppCompat.DayNight
を適用するres/values-night/themes.xml<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar" />
フォースダーク機能を使う
- 各ビューを分析して自動でダークカラーに変えてくれる機能
- 先述の開発者向けオプションを使った確認では、こちらがシミュレートされる
res/values-night/themes.xml<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <item name="android:forceDarkAllowed">true</item> </style>
- 自動で変えたくないビューには、AppTheme以外のstyleを用意して適用すればよい
Androidのダークモードサポートは色々手厚い!
- より詳しく知りたい場合、こちらを参照
WebViewの場合は?
- Web側CSSで対応するらしい(StackOverflowより)
まとめ
- ダークモードは目とバッテリーに優しく、集中できる!
- iOSは2020/04まで(03末?)にダークモードを対応するか、一時しのぎでLightに固定しなければならない
- まずは動かしてみないと修正規模がわからない
- Androidはフォースダークで対応するのが手っ取り早そう
最後に大事なこと
- ダークモード対応はエンジニアファーストで提案しよう!
- 顧客やPMは、iOS SDKの事情でダークモード対応が必要なんて知る由もない
- 工数削減のために、自動調整してくれるシステムカラーは最大限活用しよう!
- 活用すれば、20画面あっても半日で終わるから怖くない(ヤバいAppは知りません)
- デザイナーにも必要な情報を公開しよう!
- 一言「ダークモード版の素材と指示書お願いします」で受けてくれるデザイナーは奇跡の存在です(個人の所感)
やっぱり言わせてください
参考
- Use Dark Mode on your iPhone, iPad, or iPod touch
- How to use Dark Mode on your Mac
- The Benefits of Dark Mode: Why should you turn off the lights?
- ダークテーマ | Android デベロッパー | Android Developers
- Dark Mode - How to support Dark Mode for iOS 13 using Xcode
- 実践 iOS13ダークモード対応
- Android10の強制ダークモードは割としっかりダークしてくれる
- 投稿日:2020-01-20T15:49:57+09:00
カスタムビューでよく使うヘルパー関数を晒してみる
カスタムビューの実装など、
onDraw
の内部の実装を自前で行う場合には、自分で描画位置の計算をしないといけないので計算量が多くなります。そういった場合に自前でちょっとしたヘルパー関数を用意しておくととても便利なので、自分が使う際によく使う関数をメモしておきます。
(Float
のバージョンのみを記載していますが、状況に応じて適宜Int
のバージョンなども作ると便利です。)constrain()
value
がmin 〜 max
の間に収まるようにする関数です。こいつを一つ挟んでおくことでバリデーションになるので非常に重宝しています。fun constrain(value: Float, min: Float, max: Float): Float { return max(min(value, max), min) }map()
srcStart 〜 srcStop
の範囲にある値をdstStart 〜 dstStop
の範囲に変換します。グラフを描画したいけれど、グラフの座標系と画面上の座標系が違う...といった場合にサクッと変換を書けるので非常に便利です。(後述するlerp
とnorm
はmap
の変化系です。)fun map(value: Float, srcStart: Float, srcStop: Float, dstStart: Float, dstStop: Float): Float { if(srcStop - srcStart == 0f) { return 0 } return dstStart + (value - srcStart) * (dstStop - dstStart) / (srcStop - srcStart) }lerp()
lerp
は0.0f 〜 1.0f
の範囲の値をstart 〜 stop
の範囲に変換します。 例えば、2点間を3:1に分割した点の座標を求めたい、といった場合にlerp(0.75f, 0f, 100f)
とすれば、すぐに座標を計算できます。fun lerp(amt: Float, start: Float, stop: Float): Float { return start + (stop - start) * amt }norm()
norm
はlerp
の反対で、start 〜 stop
の範囲にある値を0.0f 〜 1.0f
の範囲に変換します。fun norm(value: Float, start: Float, stop: Float): Float { return value / (stop - start) }おわりに
計算関数の部分だけを他の描画ロジックから分離することでコードの見通しが良くなり、テストも容易になるためおすすめです。
自分のプロジェクトではこんなヘルパー関数を使っているよ、などコメントで教えていただけると喜びます
- 投稿日:2020-01-20T15:49:57+09:00
カスタムビューの実装でよく使うヘルパー関数を晒す
カスタムビューの実装など、
onDraw
を継承して描画を自前で行う場合には、自分で描画位置の計算をしないといけないので計算量が多くなりがちです。そういった場合に自前でちょっとしたヘルパー関数を用意しておくと便利なので、自分が使っている関数を紹介します。
(Float
のバージョンのみを記載していますが、状況に応じて適宜Int
のバージョンなども作ると便利です。)constrain()
value
がmin 〜 max
の間に収まるようにする関数です。こいつを一つ挟んでおくことでバリデーションになるので非常に重宝しています。fun constrain(value: Float, min: Float, max: Float): Float { return max(min(value, max), min) }map()
srcStart 〜 srcStop
の範囲にある値をdstStart 〜 dstStop
の範囲に変換します。グラフを描画したいけれど、グラフの座標系と画面上の座標系が違う...といった場合にサクッと変換を書けるので非常に便利です。(後述するlerp
とnorm
はmap
の変化系です。)fun map(value: Float, srcStart: Float, srcStop: Float, dstStart: Float, dstStop: Float): Float { if(srcStop - srcStart == 0f) { return 0 } return dstStart + (value - srcStart) * (dstStop - dstStart) / (srcStop - srcStart) }lerp()
lerp
は0.0f 〜 1.0f
の範囲の値をstart 〜 stop
の範囲に変換します。 例えば、2点間を3:1に分割した点の座標を求めたいといった場合にlerp(0.75f, 0, 100f)
のように書けば、すぐに座標を計算できます。fun lerp(amt: Float, start: Float, stop: Float): Float { return start + (stop - start) * amt }norm()
norm
はlerp
の反対で、start 〜 stop
の範囲にある値を0.0f 〜 1.0f
の範囲に変換します。fun norm(value: Float, start: Float, stop: Float): Float { return value / (stop - start) }おわりに
ヘルパー関数の部分だけを他の描画ロジックから分離することでコードの見通しが良くなり、テストも容易になるのでおすすめです。
自分のプロジェクトではこんなヘルパー関数を使っているよ、などコメントで教えていただけると喜びます
- 投稿日:2020-01-20T10:45:42+09:00
LiveData Builderを使ってコードをスッキリさせる
はじめに
LiveData Builderを勉強したのでメモ代わりに投稿します。
LiveData Builderとは?
androidx.lifecycle:lifecycle-livedata-ktx
に含まれる拡張機能で、liveData
関数内でsuspend関数を呼び出し、LiveDataに値を直接反映させることができます。Android Developers内に解説があります。
また、こちらに公式のコードラボがあります。
リポジトリ
GitHubからリポジトリ情報を取得してリスト表示するサンプルをこちらに上げてあります。
導入
LiveData Builderの導入にはlivedata-ktxの導入が必要です。
build.gradleimplementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-rc03"LiveData Builderを使わない場合
通常、LiveDataを扱う場合は
MutableLiveData
を用いて以下のように記述すると思います。ViewModel// MutableLiveDataで取得したいオブジェクトをラップ val data = MutableLiveData<Repos>() // 関数を定義 fun getRepos() { viewModelScope.launch { try { // APIからデータ取得 ※getReposはsuspend関数 val repos = api.getRepos() // postValueでdata内の値を更新 data.postValue(repos) } catch (e: Exception) { ... } } }MainActivity// viewModelを定義 private val viewModel: ViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { ... // 関数呼び出し viewModel.getRepos() // dataの値を監視 viewModel.data.observe(this, Observer { // 値が更新された時の処理 }) }LiveData Builderを使った場合
LiveData Builderを使うと以下のように記述することが出来ます。
ViewModel// LiveData Builder val data: LiveData<Repos> = liveData { try { val repos = api.getRepos() // getReposはsuspend関数 emit(repos) // emit()で値を更新する } catch (e: Exception) { ... } } // fun getRepos()は削除MainActivity// viewModelを定義 private val viewModel: ViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { ... // dataの値を監視 viewModel.data.observe(this, Observer { // 値が更新された時の処理 }) }ViewModel内の記述がかなりスッキリしたことが分かります。
liveData { ... }
はいつ呼ばれるのか?という疑問があると思いますが、
Viewが値をobserve
した時になります。LiveData Builderを使うことによって、
- コード量が削減できる
- MutableLiveDataをImmutableなLiveDataに置き換えることができる
- functionをActivity・Fragmentから呼び出さずに済む
などのメリットがあります。
Viewのロード時に一度だけAPIを叩くような場合は、
MutableLiveData
を使わずにLiveData
で十分になるかと思います。おわりに
まだLiveData Builderを触り慣れてないので、他にもこんなメリットがあるよ!などのご意見お待ちしております。
今回の記事を書くにあたって、以下のリンクを参考にさせて頂きました。