2023年12月22日金曜日

Mac ホットコーナーをカスタマイズする方法を解説

どうも。どっことです。今回は、Macのホットコーナーのカスタマイズについて解説します。

Macのホットコーナーをカスタマイズする

Macを使っていると右下にカーソルを当てるとクイックメモが表示されますが、ショートカットやアイコンなどの位置によっては、煩わしさを感じる人も多いと思います。私もそのうちのひとりでした。どうにか表示されないようにできないかなと調べたので、紹介します。

設定方法

手順は簡単で、Macの設定から以下を設定してあげれば解決します。

  • システム設定 > デスクトップとDock > ホットコーナー > 右下を - に設定

調べたら、MissionControlも設定することができました。

  • ホットコーナー > 右上を MissionControl に設定

参考

2023年12月15日金曜日

Android ソフトキーボードを閉じる実装について解説

どうも。どっことです。今回はAndroidでキーボードを閉じる実装を解説します。

ソフトキーボードを閉じる実装

以前このブログで、iOSでのキーボードを閉じる実装について解説しました。今回はそのAndroid版です。

解説

さっそくサンプルを載せます。InputMethodManagerにアクセスし、キーボードを閉じるようhideSoftInputFromWindowで依頼する、という具合ですね。

val inputMethodManager = getSystemService(INPUT_METHOD_SERVICE) as? InputMethodManager
inputMethodManager?.hideSoftInputFromWindow(v.windowToken, 0)

「いうほど難しいか?」と言われればそうでもなさそうですが、
「キーボード消すの、どのマネージャーに依頼するんだっけ...?」とか
InputMethodManagerに依頼するのはいいけど、どのメソッドだっけ...?」とか
「引数には何を渡すんだっけ..」とか、ちょっとした煩わしさが見え隠れしているように感じます。。

まとめ

今回はAndroidでソフトウェアキーボードを閉じる実装方法について解説しました。表示されるべき時に表示され、消えるべき時に消えるあるべき姿が、ユーザに良い体験を提供する考え方なので、意識していきましょう。

Android アプリ内にXMLを組み込む/読み込む実装を解説

どうも。どっことです。今回は、アプリ内へのXML組み込み/読み込みをする実装について解説します。

XMLをアプリ内に埋め込む・読み込む

AndroidのリソースにはXMLを配置することができます。AndroidResourceよりも単純なファイル管理として扱えるので、アプリの共通設定などの管理に利用することができます。

実際に配置する場所はapp/res/xmlフォルダです。無い場合は新規に追加して、XMLファイルを配置してください。

例えばsample.xmlを配置した場合、参照する時は以下のようにXMLファイルを指定します。

val parser : XmlResourceParser = context.resources.getXml(R.xml.sample)

Android開発ではAndroidResourceがよしなにやってくれるので、XmlResourceParserは使い馴染みのない人が多いかもしれませんが、getEventType()/next()を駆使してIterableのように解析するものです。

var eventType: Int = parser.getEventType();
// XMLドキュメントが終了になるまでwhile文を繰り返す
while (eventType != XmlPullParser.END_DOCUMENT) {
    if(eventType == XmlPullParser.START_DOCUMENT) {
        System.out.println("XMLドキュメントの読み取り開始");
    } else if(eventType == XmlPullParser.START_TAG) {
        System.out.println("新しいタグの参照:"+parser.getName());
    } else if(eventType == XmlPullParser.END_TAG) {
        System.out.println("タグが閉じられた:"+parser.getName());
    } else if(eventType == XmlPullParser.TEXT) {
    // タグに設定されている値を参照
        System.out.println("値:"+parser.getText());
    }
    // 次の要素の参照へ
    eventType = parser.next();
}

まとめ

今回はアプリ内にXMLを組み込み、それを参照する実装方法について解説しました。アプリの設定管理などの1案として挙げられるので、有効活用していきたいですね。

参考

Android 現在表示しているActivityやFragmentを確認するコマンドを紹介

どうも。どっことです。今回は、現在表示しているActivityFragmentを確認するためのコマンドを紹介します。

表示中のActivity/Fragmentを確認する

Androidアプリの開発中に「今表示しているActivityFragmentはなんだ...?」と調べる機会があったので、調べた結果を紹介します。

表示しているActivityやFragmentを確認する

Activityを確認する

以下のコマンドで確認できます。

$ adb shell dumpsys activity top

Fragmentを確認する

以下のコマンドで確認できます。

$ adb shell dumpsys activity top | grep 'Added Fragments' -A 10

まとめ

今回は表示しているActivityFragmentを確認するためのコマンドを紹介しました。Android Studioを使っているだけではあまり知る機会のないものですが、開発・デバッグに活かしていきたいですね。

参考

groupId,artifactIdについて解説

どうも。どっことです。今回はgroupIdartifactIdについて解説します。

groupIdとは?

他のすべてのプロジェクトと区別するためのID。グループを表現したID。一般的にはドメイン名を逆にしたものが挙げられる。例えばcom.exampleなど。自分のWebページなどがあるなら、それを使うのが無難そうです。

artifactIdとは?

プロジェクト名という表現がわかりやすい。ライブラリ名とかですね。

versionとは?

そのプロジェクトで管理しているバージョン。デフォルトで1.0-SNAPSHOTとなっているが、 フォーマットは決まっていない。

参考

2023年12月12日火曜日

Android テキスト周りの超細かい余白を取得する方法を解説

余白をテキストのピクセル単位でデザイン要求してくるデザイナー、絶対に許さない。

解説

テキストのベースラインから、上の余白(top, ascent)や下の余白(descentやbottom)をピクセル単位で取得することができます。この値を使って、数ピクセル単位の余白のズレも調整することができます。

val textPaint = Paint(Paint.ANTI_ALIAS_FLAG)
textPaint.setTextSize(12)
// FontMetricsの取得
val fontMetrics : FontMetrics = textPaint.getFontMetrics()
val topY = fontMetrics.top
val ascentY = fontMetrics.ascent
val descentY = fontMetrics.descent
val bottomY = fontMetrics.bottom

正直に言います。もう二度とやりたくありません。

参考

2023年12月4日月曜日

Android Kotlin Coroutineを並行に実行する実装を解説

どうも。どっことです。今回はKotlin Coroutineの並行実行する処理の実装方法について解説していきます。

Kotlin Coroutineを並列に実行する

非同期処理をとっても素敵に実装できるKotlin Coroutineですが、いくつかの処理を同時に実行したいようなケースにも対応することができます。APIによる通信やデータベースデータの永続化といった、時間がかかる処理をできるだけ短縮していきましょう。

実装方法

実装だけなら以下の2パターンで実現することができます。

async/awaitによる実装

ひとつはasync/awaitを使って実装する方法です。

viewModelScope.launch {
    val apiList = listOf(
        // async でコルーチンを生成 その中でAPIを実行する
        async { api1() },
        async { api2() },
        async { api3() },
    )
    // すべてのコルーチンを実行。全部終わるまで待つ。
    apiList.awaitAll()
}

launch/joinAllによる実装

もうひとつはlaunch/joinAllを使って実装する方法です。

viewModelScope.launch {
    // lanuch でコルーチンを作成し、その中でAPIを実行する。
    val apiJobsList = listOf(
        launch { api1() },
        launch { api2() },
        launch { api3() },
    )
    // すべてのコルーチンを実行。全部終わるまで待つ。
    apiJobsList.joinAll()
}

まとめ

今回は、Kotlin Coroutineによる処理を並列実行する実装について解説しました。ユーザの待ち時間を減らせるように活用していただけると幸いです。

参考

2023年12月1日金曜日

iOS UILabelの高さを可変にする実装方法を解説

どうも。どっことです。今回はiOSにおけるUILabelの高さを可変にする実装について解説していきます。

UILabelの高さを可変にする

Androidでいうところのandroid_height:"wrap_content"ですが、iOSではStoryBoard(AutoLayout)の都合上、Viewの高さを固定させなければならないことが非常に多いので、直感的にはできない印象です。ですが、できるんです。

そしてとても簡単で、numberOfLines0を設定し、sizeToFit()をコールするだけ。

label.numberOfLines = 0
label.text = "ここにはとても長いテキストが入ります。ここにはとても長いテキストが入ります。ここにはとても長いテキストが入ります。"
label.sizeToFit()

これだけで、UILabelの高さを可変にすることができます。

参考

2023年11月30日木曜日

Android DeprecatedされたPullToRefreshについて少しだけ紹介

???「PullToRefresh(PtR)、iOSと同じような見た目にしてほしいんだけど!」

私「AndroidにはSwipeRefreshLayoutというものがありまして...」

???「うるせえ!」

どうも。どっことです。deprecatedですが、iOSと同じような見た目のPtRがあります。SwipeRefreshLayoutが導入されたことにより、このライブラリはもうメンテナンスされていないようですが、用法用量を守って渋々いい感じに使いましょう。

参考

2023年11月28日火曜日

VBA ワークシートを参照する

ワークシートの参照パターンは大きく2パターン。Indexかワークシート名かで指定するパターン。

Index指定による参照

Application.Worksheets(Index)
Workbook.Worksheets(Index)

ワークシート名による参照

Application.Worksheets(“SheetName”)
Workbook.Worksheets(“SheetName”)

参考

2023年11月27日月曜日

Android Studio スペルチェック用辞書に単語を追加する手順を紹介

どうも。どっことです。今回はAndroid Studioで設定できるスペルチェック用辞書に単語を登録する/削除する方法を解説します。

Android Studioの単語帳を登録する/削除する

Android Studioにて、固有名詞にはニョロ線が出てウザイ結構気になりますよね。本当にスペルミスをしていたなら嬉しいことこの上ないのですが、日本語をローマ字表記させた場合ほとんどのものがニョロ線表示になると思います。Android Studioにはそういった固有名詞や単語を登録する機能がありますので、固有名詞は積極的に登録してニョロ線フラストレーションから解放されましょう。

手順

手順はそれぞれ以下の通りです。

単語の登録

  1. ニョロ線(警告線)が表示されている箇所で Alt + Enter
  2. Save ニョロ線単語 to top-level project dictionary を選択

単語の削除

  1. File > Settings > Editor > Spelling を選択。
  2. Accepted Wordsタブ内で削除対象の単語を選択し、- をクリック。

単語の共有

上記で登録した単語は.idea/dictionaries/で管理されています。ですので、このディレクトリを.gitignoreに含めないようにすることで、gitで登録された固有名詞を共有することができます。

まとめ

今回はAndroid Studioで設定できるスペルチェック用辞書に単語を登録する/削除する方法を解説します。わかっているのに表示されるニョロ線を表示されないようにすることで、本当に必要なものだけ表示されるようにして、スペルミス等に早めに気付けるようにしましょう。

参考

2023年10月18日水曜日

Android ライブラリの依存関係を確認する

ライブラリでトラブった時に、とりあえず依存関係を確認する。

./gradlew dependencies

参考

Android リップルエフェクト(Ripple Effect)を簡単に実装する方法を解説

どうも。どっことです。今回はリップルエフェクト(Ripple Effect)の実装方法について解説していきます。

リップルエフェクトを設定する

リップルエフェクトは、Viewにタップした時に水の波紋のように色が広がっていくような表示のことです。

これを設定しなくても動作に影響することはありませんが、ユーザにタッチしたことを知らせるには重要な効果だと思います。

実装方法

実装自体は簡単で、android:backgroundに以下を設定するだけです。

android:background="?attr/selectableItemBackground"

なお、すでに背景色を設定済みであれば、android:foregroundに設定することで同様に表示することができます。

android:foreground="?attr/selectableItemBackground"

まとめ

今回はリップルエフェクトの実装方法について解説しました。もはや一行追加するだけという非常に簡単な設定だけなので、解説というにはあまりにもネタが少ないかなと頭を悩ませています。

2023年10月17日火曜日

SQL テーブルにカラムを追加するALTER文を紹介

どうも。どっことです。今回は、既存のテーブルにカラムを追加するALTER文について紹介したいと思います。

テーブルにカラムを追加するALTER文

アプリのアップデートに伴い、もともと管理していたデータテーブルにさらに情報を追加したい、など、どうしてもテーブルにカラムの追加が必要になることがあると思います。そんな時は、ALTERを使って既存のテーブルにカラムを追加することができます。

サンプル①

まずはシンプルなALTER文です。

alter table テーブル名 add column カラム名 型;

テーブル名カラム名は必要な値を設定してください。

サンプル② オプション付き

またオプションとして、初期値を設定することができます。DEFAULT 節を使って初期値を指定します。

alter table テーブル名 add column カラム名 型 default 初期値;

例えば、userというテーブルにage カラムをデフォルト値を18として追加する場合は以下となります。

alter table user add column age int default 18;

まとめ

今回は、既存のテーブルにカラムを追加するALTER文について紹介しました。

新しいテーブルに引っ越したり、そもそも別テーブルで管理するなどの方法もありますが、今回はそんな中での一つの解決案として紹介させていただきました。

SQLはデータベースにおけるデータの永続化をする上で避けることができない技術だと思いますので、頭の片隅にいれておくだけでも開発効率やスピードに違いが出てくると思います。

参考

2023年10月16日月曜日

iOS 可変長引数を持つメソッドの定義

Javaと似てて、末尾に ... を追加するだけ。参照する際は、配列と同じように扱える。

func print(texts: String...) {
    // 配列のように要素数を参照できる
    if texts.count == 0 {
       return
    }
    // 配列のように要素を参照できる
    print("first: \(texts[0])")
    for text in texts {
      print("element: \(text)")
    }
}

参考

2023年10月15日日曜日

iOS よくわからないエラー集「Undefined symbol:...」

ライブラリをアプリに組み込んでビルドしたら、以下のエラーが出力されたが、知ってしまえばなんてことはない。

Undefined symbol: _GULLogBasic
Undefined symbol: _OBJC_CLASS_$_***
Undefined symbol: _OBJC_CLASS_$_***
Undefined symbol: _OBJC_CLASS_$_***
Undefined symbol: _OBJC_CLASS_$_***

「それ実機でしかビルドできないのに、シミュレータを対象にビルドしているよ」というときに表示されるエラーなので、「シミュレータでも動作するライブラリを使う」か、それが無理なら「シミュレータをターゲットにビルドするときは、該当のライブラリを組み込まずに使う、という方針となる。

2023年10月14日土曜日

アジャイル開発 と スクラム開発 スクラムイベントついて解説

どうも。どっことです。今回は、アジャイル開発(アジャイル)とスクラム開発の違い、そしてスクラム開発におけるイベントについてします。

アジャイル

アジャイルとは「俊敏性の高いソフトウェア開発」のことを指します。

スクラム(スクラム開発)

そして、スクラムとは「俊敏性の高いソフトウェア開発」の手法のひとつです。つまりスクラム開発はアジャイルに分類される手法の一つとなります。

固定的かつ長期的なメンバーで開発が進行することを想定しています。それによりチームワークを高まり、結果として生産性の最大化やビジネス目標の達成を図ります。

スクラム開発ではプロダクトオーナー、開発者、スクラムマスターの役割があり、各役職はメンバーに割り当てられます。

スクラムイベントについて

スクラム開発には4つのプロセス(イベント)があります。

1.スプリントプランニング

スプリントの最初に実施するプロセスです。

プロダクトバックログ(プロジェクトで消化すべきタスクの一覧)から優先度の高いアイテムを抽出し、タスク化します。タスク化する対象は、メンバーで認識相違がないことをあらかじめ確認したうえで決まります。そうしてタスク化されたアイテムがそのスプリントで消化するバックログ(スプリントバックログ)となります。

2.デイリースクラム

平日の決まった時間(午前中、特に朝が一般的)に短い時間でのミーティングを実施し、進捗確認や要求変化の確認を行います。スクラムマスターが全員に「今日は何やりますか」と質問し、その回答を確認することだけが実施される。問題があれば、スクラムマスターは別途会議などを設定することで解決を促しますが、スクラムマスターは、その問題解決意思決定の責務を負います。

3.スプリントレビュー

スプリント後に実施します。開発されたソフトウェアのレビューを行い、必要に応じてバックログをアップデートします。製品の機能や品質が十分であると判断されるまでスプリントとスプリントレビューを繰り返されます。

4.スプリントレトロスペクティブ

いわゆる振り返りです。スプリントの最後に実施します。

「スプリントの成果」にフォーカスが宛てられるスプリントレビューに対して、スプリントレトロスペクティブは「スクラムチームの動き」などの、プロセスにフォーカスされる。例えば、以下の事項について議論されます。

  • 直近のスプリントについての評価
  • 良かった点や今後の課題
  • スプリントの中で課題を解決した事例
  • 次回以降のスプリントにおける改善点

補足

アジャイル開発にはスクラム以外にもエクストリーム・プログラミング(XP)やカンバンなどがあげられます。

まとめ

今回は、アジャイル開発(アジャイル)とスクラム開発の違い、そしてスクラム開発におけるイベントについて解説しました。

参考

2023年10月13日金曜日

HTML/CSS CSSを設定するときに頻出する設定項目

このブログの表示感を調整するためにCSSをカスタマイズしていたが、毎回調べなおしていたので備忘録。

  • width
    • タグ要素の幅。divで頻出。
  • height
    • タグ要素の幅。divで頻出。
  • background-color
    • タグ要素内の背景色。RGB指定。
  • font-size
    • タグ要素内のテキストのテキストサイズ。 指定方法が色々ある。
    • px,emとか。
    • larger/smaller (親要素のフォントサイズとの相対)とか。
  • color
    • タグ要素内のテキストの色。RGB指定。
  • font-style
    • タグ要素内のテキストのスタイル。
    • normalitalicobliqueとか。
  • padding
    • タグ要素内に設定する余白。px,em指定。
    • 1つ指定は全方向に同一の値を設定。
    • 4つ指定はの順に設定される。
  • margin
    • タグ要素外に設定する余白。px,em指定。
    • 1つ指定は全方向に同一の値を設定。
    • 4つ指定はの順に設定される。
  • list-style
    • リストタグ(ul)限定。左につくボッチの指定。
    • noneを指定すると消える。
  • border-style
    • 枠線を表示する際のスタイルを指定できる。
    • solid(実線),dashed(破線)など。
  • border-width
    • 枠線を表示する際の枠線の太さ設定。
    • px, em指定。
  • border-color
    • 枠線を表示する際の枠線の色指定。
    • RGB指定。
  • border
    • border-style, border-width, border-colorを一括で指定できるプロパティ。

2023年10月12日木曜日

iOS ボタンタップ中の色を変えたい

ViewのプロパティであるisHighlightedの更新を受けて、背景色を設定するだけ。

カスタムViewを実装する際に、isHighlightedをオーバーライドし、didSetbackgroundColorを設定すればいいだけ。こんなん。

override open var isHighlighted: Bool {
    didSet {
        backgroundColor = isHighlighted ? .lightGray : .blue
    }
}

ちなみにisHighlightedとは、Androidでいうところのstate_pressedUIButtonなどのUIControlクラスを継承したViewは状態に対して背景を変えるメソッドがあるため、それを使えばタップ中の色を変えることができる。

参考

2023年10月11日水曜日

iOS UIViewを角丸にしたり、枠線を付ける実装方法を解説

どうも。どっことです。今回は、iOSのUIViewについて角丸や枠線をつけ方について実装を解説していきたいと思います。

iOSで角丸や枠線を付ける実装方法

私自身がAndroidエンジニアとしてやってきた時間が長いので、どうしてもAndroid開発者目線となってしまうのですが、Viewを角丸にしたり、枠線をつけたりする実装が本当に面倒くさいです。簡単に見せるだけでも、新規に背景用ファイルを用意してあげる必要があります。

しかし、iOSは角丸や枠線を超簡単に実装できます。本当に羨ましいです。別に悔しくねーし。

実装方法

それでは、実装方法について解説していきたいと思います。

角丸にする

UIViewが共通に持っているlayerという変数に対応するパラメータがあるので、コード上であれば以下に値を設定することで角丸にすることができます。

layer.cornerRadius

枠線を付ける

枠線の実装も同様で、以下に値を設定することで枠線を付けることができます。

layer.borderWidth
layer.borderUIColor

ちなみに、borderUIColorCGColorクラスを設定する必要があることに注意してください。

まとめ

今回は、UIViewを角丸にしたり枠線をつけたりする実装方法を解説しました。特に見せ方を柔らかくする角丸の実装が簡単なのは、iOSはとても良いなと思いました。

参考

2023年10月10日火曜日

Android Activityを透明にするための設定を解説

どうも。どっことです。今回はActivityを透明にするための設定について解説します。

Activityを透明にする

完全に透明なActivityでなにかしら処理する(つまりAPI通信する、DBの更新処理するなどを完全に透明なActivityで実行する)のはユーザ観点ではよくないですが、例えばダイアログを表示するためだけのActivityなどであれば、背景が見えて別画面に見えてしまうのは不自然であるため、確かに透明である必要があります。そういった、不自然なことを回避する目的でActivityを透明にする設定を紹介していきたいと思います。

設定方法

AndroidManifest.xmlで透明にしたいActivitythemeに以下を設定します。

<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>

例えば、上記の設定を適用したthemeTransparentActivityThemeとして用意しました。

<style name="TransparentActivityTheme" parent="AppTheme.NoActionBar">
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:colorBackgroundCacheHint">@null</item>
    <item name="android:windowIsTranslucent">true</item>
</style>

上記をAndroidManifest.xmlの透明にしたいActivitythemeに適用します。以下はTransparentActivityというActivityに設定したときの例です。

<activity
    android:name="com.sample.application.TransparentActivity"
    android:theme="@style/TransparentActivityTheme"/>

orientation指定をするとクラッシュするので注意

ただし実際にアプリ内に組み込む場合、注意が必要です。透明なActivityには危険が含まれているのです。 透明なActivityに対してorientationの指定をするとクラッシュします。実装でorientationを指定したり、AndroidManifestでのActivityscreenOrientationを指定するとアプリがクラッシュするようです。呼び出し元のActivityと同じorientationで制御されるようなので、透明なActivityを呼び出す時は気をつけてください。

参考

2023年10月9日月曜日

Android Activityをダイアログぽく表示する方法を紹介

どうも。どっことです。今回はActivityをダイアログっぽく表示する方法を紹介します。

Activityをダイアログのように表示する

色々カスタマイズできるのは嬉しいDialogFragmentですが、実装が面倒で「それっぽければActivityで十分なんだよなぁ…」と思ってしまった私への、「ActivityだけでDialogぽい見た目の画面(というかダイアログ)の作り方」備忘録です。

最小限の設定の場合

対象のActivityに以下のThemeを設定するだけです。

Theme.MaterialComponents.DayNight.Dialog

DialogFragmentStyleとして設定される値を、Activityに設定してあげるだけです。本当にそれっぽくできるだけで、これだけだと決定もキャンセルもなにもないのでレイアウト側で追加の実装が必要となります。

色々カスタマイズしたい場合

Themeを継承してカスタマイズします。

最低限の設定を参考にダイアログぽいものを実装するとテーマの設定が引き継がれていないため、他のActivityと統一感が取れていないダイアログが表示されます。レイアウト側で調整すればなんとかなりますが、いい感じの統一感を目指すならThemeを意識したカスタマイズが必要となります。

Theme.MaterialComponents.DayNight.Dialogの継承元を追っていくと分かりますが、ダイアログぽいActivityにしてくれるこのテーマはいくつかの属性が設定されています。以下抜粋。もしかしたら漏れてるかも。

<item name="android:windowFrame">@null</item>
<item name="android:windowTitleStyle">@style/RtlOverlay.DialogWindowTitle.AppCompat</item>
<item name="android:windowTitleBackgroundStyle">@style/Base.DialogWindowTitleBackground.AppCompat</item>
<item name="android:windowBackground">@drawable/abc_dialog_material_background</item>
<item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">true</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowAnimationStyle">@style/Animation.AppCompat.Dialog</item>
<item name="android:windowSoftInputMode">stateUnspecified|adjustPan</item>

アプリ共通のテーマを継承しつつ、これらの設定をテーマに設定することで、統一感のあるダイアログを実装することができます。

まとめ

今回はActivityをダイアログぽく表示する方法について紹介しました。DialogFragmentは実装に少しクセがあるコンポーネントなので、単に表示するだけなら今回の設定で簡単に実装することができます。ただ、ここまで書いてていまさらだが、Activityをダイアログとして表示するのはAndroidのお作法的に正直あまりオススメしません。用法用量を守って計画的に。

2023年10月8日日曜日

VBA セルやセル範囲を参照・選択する

VBAって、ワークシートとセルの参照、検索が手になじむくらい使えるようになれば、あとはいくらでも融通が利くようになると思うんだ。

Cells を使ってセルを参照する

Cells(1, 1) = "A1に値を設定します"
Cells(1,"A") = "A1に値を設定します"
Cells(2, 1) = "A2に値を設定します"
Cells(4,"D") = "D4に値を設定します"

Range を使ってセルを参照する

Range("A1") = "A1に値を設定します"
Range("A2,D2") = "A2,D2に値を設定します"
Range("A3:D3") = "A3~D3 (=A3,B3,C3,D3)に値を設定します"
Range("A:D") = "A~D列(=A,B,C,D列のすべてのセル)に値を設定します"
Range("1:4") = "1~4行(=1,2,3,4行のすべてのセル)に値を設定します"
Range("A2:C3") = "A2~C3(=A2,A3,B2,B3,C2,C3)に値を設定します"

参考

2023年10月6日金曜日

Android ファイルアクセスが拒否されるせいでビルドが全然通らない問題と解決方法を紹介

どうも。どっことです。今回ですが、ここ2,3年、AndroidStudioでのビルドが20回に1回程度しか成功しなくなり開発モチベが著しく低下していたが、ついにその問題を解決することができたので備忘録として記載します。

現象と根本の原因

How to solve this error app:packageDebug FAILED

A failure occurred while executing com.android.build.gradle.tasks.PackageAndroidArtifact$IncrementalSplitterRunnable java.io.FileNotFoundException: C:\Users\HP\AndroidStudioProjects\Chatter\app\build\intermediates\incremental\packageDebug\tmp\debug\zip-cache\androidResources (Access is denied)

このエラーが解決できずに、この2,3年本当に困っていました。「はー。もうダルイし、Macでも買うか。」とここぞとばかりに物欲全開に検討していました。

そして解決へ

そんなこんなでPCのリプレースも検討していたのですが、上記のページをみていくとコメント欄に以下の記述がありました。

The actual problem is that your anti-virus is preventing the access to the files. Make the Android Studio application as trusted application(recommended) or try turning off your antivirus.(おまえのPCのウイルス対策ソフト、ちゃんと設定しとるんか?AndroidStudioを信頼できるアプリに設定しとるか?あとは試しにソフトの機能をOFFにしてビルドしてみ?)

私「...」
私「...」
私「それだわー」

ウイルス対策ソフトの機能をOFFにしてビルドしたところ、何事もなかったかのようにビルドが成功しました。

ウイルス対策ソフトをインストールしてからすぐのAndroid Studio起動で「なんか知らんツールがアクセスしたがってるけどええか?」という警告が表示されていたので脳死でOKを設定していましたが、それだけじゃ足りなかったようです。公式サイト(参考に記載)にも「ビルドのパフォーマンスが著しく低下するで?ちゃんと設定するんやで?」というアナウンスがされていました。

原因が分かったので、ウイルス対策ソフトの設定に対象外フォルダを設定することで無事に解決しました。本当に良かった。

最後に

私のPCではソースネクスト社のウイルスセキュリティソフトをインストールしています。絶対にツッコませない。このソフトでの対象外フォルダ指定手順を紹介して終了したいと思います。

  1. ウイルスセキュリティソフト のホーム画面を開く
  2. 設定 > ウイルス・スパイウェア対策 >設定(セクションタイトル下部に導線有)
  3. ウイルス自動検知タブ > 検査したくないファイル、フォルダを指定する
  4. ファイルやフォルダを追加で検査対象外のフォルダを追加する。以下にチェックを入れる。
    • ウイルス自動検知の対象にしない
    • サブフォルダも含む

参考

2023年10月5日木曜日

VBA HTTP通信して情報取得する実装を紹介

どうも。どっことです。毎度おなじみ通信して取得する処理の紹介です。今回はVBAでの実装を備忘録として紹介します。

VBAで通信して情報取得する

実装サンプルを以下に載せます。XMLHTTP60DOMDocument60JsonConverterを使うことで、比較的簡単に実装することができます。

Set http = New XMLHTTP60
url = "取得対象となるリソースのURL"
http.Open "GET", url, False
http.send
'エラーハンドリング
Set http = New XMLHTTP60
If http.statusText <> "OK" Then
  MsgBox "サーバーへの接続に失敗しました"
  Exit Sub
End If
'データの解析 XML形式の場合
Set doc = New DOMDocument60
doc.LoadXML (http.responseText)
'データがjson形式の場合はこっち
Set jsonObj = JsonConverter.ParseJson(http.responseText)

まとめ

簡単ですが、以上です。業務上、EXCELを利用するエンジニアは多いと思いますが是非ともVBAも活用して、業務効率の向上などに役立ててください。

参考

2023年10月4日水曜日

Android gradleのキャッシュを削除するコマンドを紹介

どうも。どっことです。今回は、毎回忘れるGradleのキャッシュ削除のコマンドを備忘録として紹介します。

gradleのキャッシュを削除するコマンド

早速ですが、コマンドを載せます。

rm -rf ~/.gradle/cashes

~/となっていることを見ても分かる通り、削除するディレクトリはアプリプロジェクトにあるディレクトリではなくユーザディレクトリにあるキャッシュのディレクトリになります。これを実行すると、次回ビルドは非常に時間がかかるのが見込まれますので、「PCやプロジェクトがおかしいな?」と思った時にやるのがちょうどいいと思います。

ちなみに .gradle フォルダを削除しても、Gradle がビルドする時に再構築してくれるらしいので気にすることは全くありません。

独り言

ただ、最近AndroidStudioのビルドが全く通らない。

C:\Users\XXXXX\Documents\project\sampleProject\app\build\intermediates\incremental\packageDebug\tmp\debug\zip-cache\androidResources (アクセスが拒否されました。)

いったいなんだってばよ。

追記。なんか解決した。新規に記事にした。

2023年10月2日月曜日

SQL SQLでもビット演算は計算できる

どうも。どっことです。今回はSQLでのビット演算について備忘録を記載します。

SQLでビット演算

SQLでもビット演算することができます。ビット演算は情報系の学生やエンジニアには基礎的な内容ですが、Androidだけで言えば使うことがあまり多くないかもしれません。

しかし、確かにビット演算による機能実装は行われており、例えばIntentに付与するflagの値はこのビット演算により制御されています。

これをもとに考えると、アプリやデータなどの複数の重複した状態を管理するのに向いていそうです。例えばSNSのコメントの「お気に入り」「リブート/リポスト」「いいね」などを管理するとき、booleanで管理するのが一番シンプルな実現方法ですが、最大値7の整数(000〜111)でまとめてしまう、という管理でも実現することができます。これの嬉しいことは、似たようなデータ要素を追加するとき、例えば「ミュート」を追加する場合にも、変数を追加せずに最大桁数を7→15の整数(0000〜1111)に上げることだけで十分実現できるということです。

変数を追加するというのはプログラムのデータ上だけでなくデータベースのテーブルやAPIでのやりとりにも影響してくるので、このようなOn/Offのデータをビット管理するのは拡張性を意識したときに優れていると考えられます。

積(and)

select 1 & 1; 1
select 1 & 0; 0

和(or)

select 1 | 1; 1
select 1 | 0; 1
select 0 | 0; 0

反転(not)

select ~0; -1
select ~1; 0

左シフト

select 1<<2; 4

右シフト

select 4>>1; 2

2023年9月21日木曜日

Android つぶやきながらJetpackComposeのサンプルを覗いてみる その1

巷ではJetpackComposeが流行っているらしい。これまでAndroid開発では xml により画面のレイアウトを実装していたけど、今後は 宣言型UIフレームワーク が主流になってくる様子。xml が慣れている人にとってはまだすこし取っ掛かりにくいけど、画面の構成がより明示的に表現されるのであれば、それはそれで嬉しいと思う。

サンプルを見てみる。

Android Studio>File>New>Import Sample...で、入力フォームにcomposeを入力すると、それっぽいものがフィルタリングされるので、適当に選択する。Androidarchitecture>Architectureを選択してみる。(どうやらTODOアプリのサンプルが見れる様子。SunFlowerも画像表示のサンプルとしては重要だけど、今回はシンプルそうな方を眺めてみる)

とりあえずDLが終わったけどどこを見ればいいのかわからないので、AndroidManifest.xmlを見てみる。TodoActivity というActivityが登録してあるみたい。

TodoActivityを覗いてみる

override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  setContent {
    AppCompatTheme {
      TodoNavGraph()
    }
  }
}

setContentしか実装されていない... AppCompatThemeは名前からなんとなくテーマが設定されているコンポーネントかと予想できるので、その内側に設定されているTodoNavGraph を見てみる。(deprecatedなっていたが、趣旨から外れるのでここでは気にしないことにする。)

TodoNavGraphを覗いてみる

で、初っ端にでてきた実装がこれ。

@Composable
fun TodoNavGraph(
  modifier: Modifier = Modifier,
  navController: NavHostController = rememberNavController(),
  coroutineScope: CoroutineScope = rememberCoroutineScope(),
  drawerState: DrawerState = rememberDrawerState(initialValue = DrawerValue.Closed),
  startDestination: String = TodoDestinations.TASKS_ROUTE,
  navActions: TodoNavigationActions = remember(navController) {
    TodoNavigationActions(navController)
  }
) {
...

fun... メソッドやんけ...しかも戻り値もない...ほんとうにこれでいいんか...?もう少し眺めてみよう。

@Composableアノテーションが付与されたメソッド内の実装を眺めてみる。

どうやらNavHostというコンポーネント内で、必要なものを構成するぽい。

NavHost(
  navController = navController,
  startDestination = startDestination,
  modifier = modifier
) {

んで、その NavHost での実装は composable というコンポーネントで構成されている。

composable(
  TodoDestinations.TASKS_ROUTE,
  arguments = listOf( navArgument(USER_MESSAGE_ARG) { type = NavType.IntType; defaultValue = 0 }
  )
) { ... }
composable(TodoDestinations.STATISTICS_ROUTE) { ... }
composable(
  TodoDestinations.ADD_EDIT_TASK_ROUTE,
  arguments = listOf(
      navArgument(TITLE_ARG) { type = NavType.IntType },
      navArgument(TASK_ID_ARG) { type = NavType.StringType; nullable = true },
  )
) { ... }

で、そのコンポーネントが各画面やサイドメニュー(ドロワー)の要素に対応していそう。

まとめ(所感)

ここまでの所感など

  • TodoDestinations で定義された定数が、このアプリを構成する画面やドロワーに対応していそうに見える。であれば実際に運用する場合この定義は列挙型にしておいて、引数としてはordinalなどを渡してあげたほうが良さそうな。
  • TodoDestinations.ADD_EDIT_TASK_ROUTEcomposable を見ると、どうやら画面に渡すパラメータもここで定義しておく様子。ここでの定義と実際の画面とで乖離が起きないよう、画面側のどこかで管理しておいたほうが良さそう。
  • アプリを構成する画面定義は見えたけど、各画面間での画面遷移や処理結果の戻しなどをどのように実装するかは見えていないので、次にサンプルを眺めるときはこの辺りをフォーカスして確認できれば嬉しい。

とりあえずここまで。

参考

2023年9月17日日曜日

Android ViewのコンストラクタとカスタムView実装に伴うスタイル定義について解説

どうも。どっことです。今回は独自ViewとしてEditTextのカスタマイズクラスを実装したときにハマった問題について、備忘録を載せたいと思います。

Viewのコンストラクタとスタイル定義

ListViewなどを使うために追加する独自View(①)と、TextViewなどのコンポーネントのViewをカスタマイズするView(②)とで、特にコンストラクタの実装方針が異なります。今回はそんなコンストラクタの実装方針とそれに伴うスタイル定義について解説していきます。

コンストラクタについて

そもそもAndroidのView派生クラスには、3つのコンストラクタがあります。

1.Contextのみを引数とするコンストラクタ

constructor(context:Context)

これはコードからViewを生成するときに使われるコンストラクタです。LayoutInflaterを使わない場合は、このコンストラクタでViewを生成するコースがほとんどかと思います。属性値がほぼ初期値なので、ViewGroupaddView()するときに自分で値を設定する必要があります。

2.Context, AttributeSetを引数とするコンストラクタ

constructor(context:Context, attrs:AttributeSet?)

これはxmlからViewを追加したときに呼ばれるコンストラクタです。

3.Context, AttributeSet, defStyleAttrを引数とするコンストラクタ

constructor(context:Context, attrs:AttributeSet?, defStyleAttr: Int)

これもxmlからViewを追加したときに呼ばれるコンストラクタです。「2.と何が違うの?」という疑問はAndroidアプリ開発者なら誰しも一度は思う謎ですが、それはxml内にstyleが指定されているか否かの違いです。xml内でstyleが指定されていれば3.、されていなければ2.が呼び出されます。

コンストラクタの実装方針について

それではコンストラクタを紹介したところで、先ほどの①と②での実装方針の違いについて説明していきます。

①の場合

気にしなくて問題ありません。以下のようなコードをコピペして使います。(以下はLinearLayoutの派生クラスとして定義した時の例です。)

class HogeView(context: Context, attrs: AttributeSet?, defStyleAttrs:Int) : LinearLayout(context, attrs, defStyleAttrs) {
  constructor(context: Context): this(context, null, 0)
  constructor(context: Context, attrs: AttributeSet?): this(context, attrs, 0)
  // ...
}

②の場合

こちらは適切なスタイルの定義が必要になります。デフォルトの場合、例えばEditTextはこのような実装になっています。

public EditText(Context context) { // ①のコンストラクタ
    this(context, null);
}
public EditText(Context context, AttributeSet attrs) { // ②のコンストラクタ
    this(context, attrs, com.android.internal.R.attr.editTextStyle);
}
public EditText(Context context, AttributeSet attrs, int defStyleAttr) { // ③のコンストラクタ
    this(context, attrs, defStyleAttr, 0);
}

第4引数については参考を見てもらうとして2.のコンストラクタが3.のコンストラクタをオーバーロードしているのは同じですが、引数が異なっています。①の場合は0 を渡していましたが、こちらはcom.android.internal.R.attr.editTextStyleを渡しています。

EditTextStyleにはEditTextが必要としている各属性の初期値が設定されており、これを渡してやらないと非常に残念な表示になってしまいます。(EditTextなのに、テキストが編集できないとか。1敗。)特に気にしないなら、これと同じように com.android.internal.R.attr.editTextStyleを渡してやれば問題ありませんが、「せっかく独自Viewを追加したのだから、スタイルもいい感じにしたい」ということであれば、追加で以下を実装する必要があります。(今回はEditTextを例として挙げます。)

実装手順

属性の追加

とりあえず何も考えずに attrs.xml に以下を追加します。

名前も適当です。必要に応じて適切な名前にしてください。

<resources>
    <attr name="hogeEditTextStyle" format="reference" />
</resources>

スタイルの追加

続いて何も考えずに styles.xml に以下を追加する。

名前も...(略)

<resources>
    <style name="HogeEditTextStyle" parent="android:Widget.EditText">
    </style>
</resources>

テーマの追加

前項までに追加したものをつなぐために、themes.xml に以下を追加します。

な...(略)


<style name="Theme.AppTheme" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
    <!-- ... -->
    <!-- Customize your theme here. -->
    <item name="hogeEditTextStyle">@style/HogeEditTextStyle</item>
</style>

コンストラクタでの参照

最後に、「属性の追加」で追加したものを参照します。

class HogeEditText(context: Context, attrs: AttributeSet?, defStyleAttr: Int) :
    AppCompatEditText(context, attrs, defStyleAttr) { // ③のコンストラクタ
    constructor(context: Context) : this(context, null, R.attr.hogeEditTextStyle) // ①のコンストラクタ
constructor(context: Context, attrs: AttributeSet?) : this( // ②のコンストラクタ context, attrs, R.attr.hogeEditTextStyle ) //... }

必要な実装は以上です。これでカスタムしたHogeEditTextでも残念な表示にならず、いい感じの表示とすることができます。

余談

第3引数が必要なコンストラクタの変数名が defStyleAttrとなっており、これは default Style Attributeを意味しているものと考えられます。つまり、デフォルトのStyleのattributeが設定されることを想定されているということです。xml上でstyleが設定されていなければ②の、styleが設定されていれば③のコンストラクタが呼ばれる点からもこの想定が正しいものと思われます。独自Viewとして想定しているデフォルトのスタイルなどは、①、②の場合に限らず、無理やりコード上で設定するのではなく今回紹介した方法のdefStyleAttr設定するのがスマートな実装方法のように感じられます。

まとめ

今回はコンストラクタと、カスタムView実装におけるスタイル定義について解説しました。カスタムViewの実装は、ソースコードの整理やロジックをまとめる際に非常に有効な手法です。今回のように、必要なコンストラクタがどれか、スタイル定義が必要か、などを理解した上で効率よく開発していきたいですね。

参考

2023年9月1日金曜日

Python HTTP通信で情報取得

どうも。どっことです。通信取得する処理について紹介します。今回はPythonでサーバ通信・情報取得する処理です。

説明

さっそくサンプルを載せます。pythonをインストールできていれば、以下でOKです。

import sys
import json 
import urllib.request

# 取得したい情報のURL
url = "取得したい情報のURL"
header = {
  # 例えば header に仕込む key-value
  'key': 'value', 
  # 例えば header に仕込む 認証情報
  'Authorization': 'Bearer <YOUR TOKEN>'
}
req = urllib.request.Request(url, headers = header)

with urllib.request.urlopen(req) as res:
    body = json.load(res)
    print(body)

Android/iOSといったネイティブアプリのように、非同期処理などのことを考えなくていいので、とても簡単ですね(逆に不安になる)。巷ではrequestsといった、通信処理を簡単に実装できるようなライブラリはごまんとあるようですが、今回は「可能な限り簡単に使えるスクリプト」を意識したいので、標準のurllibを使いました。

参考

2023年8月11日金曜日

Android 裏にいるActivityもすべて終了するfinishAffinityメソッドを紹介

どうも。どっことです。今回はスタックに積まれているActivityをすべて終了させるメソッドを紹介します。

裏のActivityもすべて終了するfinishAffinity

Activity AからActivity BstartActivity()でスタックを積んだとき、Activity Aが明示的に自身をfinish()させなければ、Activity Bの裏にActivity Aはスタックとして存在します。

そしてこのときActivity Bfinish()を呼び、自身を終了させても、スタックに残っているActivity Aが表示され、アプリを終了することができません。

今回は、Activity BからスタックにいるすべてのActivityを終了させるメソッドを紹介したいと思います。

Activity#finishAffinity()

すべてのActivityを終了させるにはfinishAffinity() というメソッドを使います。単純なケースであれば、このメソッドを呼び出すことで裏にいるActivityをすべて終了することができます。

単純なケースと書いたのは、Activityには親和性(affinity)という概念があるためです。これが厳格に管理されているActivityですと、今回のケースは期待した動作をしない可能性があります。

ただし基本的に同一のAndroidアプリという領域のActivityであるためaffinityが厳格に管理されているケースは非常に稀で、まずはやりたいことが実現できるかをこのメソッドを呼んで試してみるのが解決への一番の近道になるかと思います。

参考

2023年8月10日木曜日

Android 通知チャンネルの設定画面を表示する実装方法について解説

どうも。どっことです。今回は、通知チャンネルの設定画面を表示するための実装方法について解説します。

通知チャンネルの設定画面を表示する

Android O から追加された通知チャンネルですが、アプリからは追加・削除以外は通知チャンネルの設定値を修正・変更することができません。その代わりに端末の設定画面に遷移させるための導線を具備することで、ユーザに通知チャンネルまで簡単にアクセスできるようにしてあげましょう。

実装方法

アプリが追加したチャンネルの設定画面に遷移する方法は以下となります。

val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
intent.putExtra(Settings.EXTRA_CHANNEL_ID, channelId)
startActivity(intent)

通常のstartActivityと同様に、必要なActionやパラメーターを設定することで通知チャンネルの設定画面に遷移することができます。

まとめ

今回は、通知チャンネルの設定画面を表示するための実装方法について解説しました。このブログでは、アプリで通知を表示するために必要な実装として他にも投稿しているので、もしよろしければそちらも参考にしていただけると幸いです。

参考

2023年8月9日水曜日

Android AndroidPでhttp通信に失敗する問題

targetSDKVersionをAndroidP以上に設定すると、セキュリティの問題でアプリでのhttp通信が失敗する。AndroidManifest.xml のapplicationタグに以下の属性を追加する。

<application
    ....
    android:usesCleartextTraffic="true">
</application>

開発段階ではローカル環境にサーバ立ててそこにスタブデータを置いて動作確認することが多いが、本番環境では回避すべき設定なので、debug/releaseでAndroidManifest.xmlを分けるなど、きちんと切り分けること。

2023年8月8日火曜日

HTML/CSS マウスオーバーしたときの表示をカスタマイズする

マウスオーバーしたときに表示を切り替えたいときは、CSSに疑似クラスを指定する。

a:hover {
  マウスオーバー中に表示したい内容
}

この指定を複数の要素に指定することで、例えばテキストやイメージの表示切替などもできる。

a:hover {
  マウスオーバー中に表示したい内容
}
.a-covered:hover {
  マウスオーバー中に表示したい内容
}

マウスオーバー以外にも疑似クラスの指定により表示を変えられるものは以下。

  • link:御訪問リンク
  • visited:訪問済みリンク
  • hover : マウスオーバー
  • active : リンククリック中

参考

2023年8月7日月曜日

Android Parcelableを実装する方法を解説

どうも。どっことです。今回はParcelableの実装方法について解説します。

Parcelableを実装する

ActivityFragmentなどの画面間におけるデータ受け渡しはアプリ全体の動きを考える上でなくてはならない処理ですが、自前で用意したデータクラスはそのままだとBundleに乗せることができません。

それではどうするかというと、そのデータクラスにParcelableSerializableを実装します。取り出すときにキャストが必要にはなるが、これでBundleに乗せることができるようになり画面間のデータ受け渡しができるようになります。

今回はその呪文の如きParcelableのインターフェース群の実装について、説明します。

実装

必要な実装ですが、データクラスに以下のクラス・メソッドを実装します。

  • Parcelableを実装する
  • describeContentsを実装する
  • 引数がParcelのコンストラクタを実装する
  • writeToParcel()を実装する
  • CREATOR : Parcelable.Creatorを実装する

サンプルとしてItemクラスにParcelableを実装する場合は以下となります。

data class Item(
    var title: String,
    var description: String,
) : Parcelable {
    constructor(parcel: Parcel) : this(
        parcel.readString()!!,
        parcel.readString()!!
    )

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeString(title)
        parcel.writeString(description)
    }

    override fun describeContents(): Int {
        return 0
    }

    companion object CREATOR : Parcelable.Creator<Item> {
        override fun createFromParcel(parcel: Parcel): Item {
            return Item(parcel)
        }

        override fun newArray(size: Int): Array<Item?> {
            return arrayOfNulls(size)
        }
    }
}

あとは実装したデータクラスのオブジェクトをBundle#putParcelable()Bundleに詰め

val bundle = Bundle().apply {
    putSerializable("適当なキー", item)
}

Bundle#getParcelable()Bundleから取り出すことで

val item = bundle.getParcelable("適当なキー", Item::class.java)

画面間でデータクラスをやり取りすることができます。

ここまで書いててですが…

Percelableをいい感じに実装してくれるアノテーションがあると公式サイトが謳っていました。

Parcelable 実装生成ツール

アノテーション付けるだけで上記の面倒臭い実装をしなくてよくなるなんて、なんて素晴らしいんだ!!ありがとうGoogle様!!!

まとめ

今回はParcelableの実装方法について解説しました。ボイラープレートなParcelableの実装ですが、すでに自動生成してくれるツールがあるので、そちらを使う方が無難でしょう。今回の紹介した内容がほぼ無用なものになってしまいますが、必要なものが簡単に実装できることの方が重要だと思うので、まあ仕方ないでしょう笑

2023年8月6日日曜日

VBA 今日の日付を取得する

単純な日付だけであれば Dateをって取得する。

Dim date As Date: date = Date()

時刻まで必要であれば Now を使って取得する。

Dim now As Date: now = Now()

参考

2023年8月5日土曜日

VBA 変数宣言時に初期値を設定する

Dimによる変数宣言と、値の設定は一行で書ける。

Dim numberValue As Integer: numberValue = 30000
Dim text As String: text = "sample text"

2023年8月4日金曜日

SQL 複数のキーを主キーに設定するPRIMARY KEY節を紹介

どうも。どっことです。今回は複数のキーをPRIMARY KEYに設定するSQLを紹介します。

複数のキーをPRIMARY KEYに設定する

まず、前提として主キーが1つだけの場合です。

テーブルを生成する際、1つのキーだけを主キーに設定する場合は以下のようになります。

create table user(id int primary key,  name text);

しかし複数のキーを主キーに設定する場合、上記と同じような形にするとエラーが発生します。

複数のキーを設定する場合は、最後の方にPRIMARY KEY節を追加します。例えばid,subIdを主キーに設定する場合は以下となります。

create table user(id int, subId int, value text, ..., PRIMARY KEY(id, subId));

今回は簡単ですが以上です。

2023年8月3日木曜日

iOS TextFieldのキーボードを閉じる実装方法を解説

どうも。どっことです。今回はiOSでのキーボードを閉じる実装について解説します。

解説

iOSではアプリからキーボードを閉じるのは非常に簡単で、UITextField#endEditing(true)を呼び出すだけでOKです。

@IBOutlet weak var textField: UITextField!

func closeKeyboard() {
    // キーボードを閉じる
    textField.endEditing(true)
}

AndroidではInputManagerにアクセスしたり必要な情報を渡したりで面倒臭いのに、iOSはとにかくシンプルで羨ましいですね。

参考

2023年8月2日水曜日

Android Koin の使い方を調べたので解説

どうも。どっことです。今回はKoinの使い方について解説します。

Koinを使ったDIの実装

Koinってなんだよ、というあなたはこちらを見てくださると大変嬉しいです。ちなみに、Daggerというライブラリを使ってDIを実現するパターンもありましたが、実装難易度の観点から今回はKoinを使う方針としました。

それでは実装方法について解説します。

ライブラリの組み込み

まずはライブラリを組み込む必要があるのでapp/build.gradle.ktdependenciesに以下を記述します。

dependencies {
    // 他にもあるけど説明のためこれだけ。
    implementation("io.insert-koin:koin-android:3.4.2")
}

Applicationクラスの実装

続いて、Applicationクラスに必要な処理を追加していきます。Applicationってどうすんだっけ、というあなたはこちらを見てくださると大変嬉しいです。

moduleインスタンスを作ってstartKoin を実行します。moduleインスタンスには DIするインターフェースとそれの実装クラスを登録します。以下はSampleRepositoryImpl(dao: Dao)をDIするサンプルです。

    private val appModule = module {
        single<SampleRepository> { SampleRepositoryImpl(get()) }
    }
    protected open val dao = module {
        single {
            Room.databaseBuilder(
                applicationContext,
                Database::class.java,
                DATABASE_NAME
            ).build().dao()
        }
    }
    override fun onCreate() {
        super.onCreate()
        startKoin {
            androidContext(this@SampleApplication)
            modules(appModule, dao)
        }
    }

メモ:single で登録しておくと、シングルトンパターンでオブジェクトを管理してくれる。

各機能にアクセスする

あとはDIしたい箇所で、by inject()を使ってインジェクトすると、簡単にアクセスすることができます。

private val repository : SampleRepository by inject()

まとめ

今回はDIをシンプルにアプリに導入できるライブラリであるKoinの使い方を解説しました。昨今のプログラムのレガシー化やCI/CDに向けたモジュールの疎結合を意識する上でDIは必要不可欠な機能の一つです。DIを実現するものは今回のKoin以外にもさまざまなものがありますので、比較・検討した上で導入しましょう。

参考

2023年8月1日火曜日

CI/CD Bitriseでaabを生成する

前回の続きです。今回はリリースファイルを作るための各種設定を備忘録として記載したいと思います。

リリースファイル(aab)を生成するための設定

とはいえ、基本は以下の手順通りに設定すればOK。日本語ページはなかなか残念なことになっているので、英語ページをベースに作業したほうが結果的にストレスが少なくて済む。

Generating and deploying Android app bundles

ただし、少しだけハマりポイントがあるので注意が必要。ハマったポイントは以下。

gradlewのパス

唯一納得いかなかったハマりポイント。gradlewのパスがデフォルトのままだとビルドが通らない。

上記ページの手順で追加した「Gradle Runner」のステップ内にあるInput variables>gradlew file pathの項目に以下を設定する。

./gradlew

最小構成のプロジェクトなら、これだけ手直しすればビルドが通ってaabができるはず。

(追記)最新の設定だと、「Gradle Runner」を追加せずともaabファイルを生成できる模様...何のために試行錯誤したのか。。。

google-services.jsonの準備

google-services.jsonはFirebaseの機能を使うためにアプリに組み込む設定ファイル。秘匿情報が含まれるため、原則Gitには登録しない。しかし、Gitに登録しないとBitriseでビルドする時にこの設定ファイルが無いせいでビルドに失敗する。

この矛盾を解消するために、Bitriseに用意されているテキスト生成のステップを利用する。ざっくり書くと、以下。

  1. ファイルの中身をまるっとコピーする。
  2. コピーしたテキストをBitriseの定数として保存。
    • 定数の名前は適当でOK。例えば直感的にGOOGLE_SERVICES_JSONとか。
  3. テキストファイル生成のステップ(Generate Text File)を追加。
    • 定数として定義したテキストを使って、google-services.jsonファイルとして生成する。

「テキストファイルを生成する」ステップが用意されているのが、直感的で非常にわかりやすい。

センシティブな情報の設定

おそらくAndroidアプリ開発者ならほとんどの人がハマるポイント。build.gradleBuildConfigFieldを定義する際、ダブルクォーテーションを定義に含めないと、生成されるBuildConfigでいい感じに定義できない問題。これがBitriseでも(というより環境変数として定義する場合でも)同様に発生する。

何も考えないならbuild.gradle内で

buildConfigField "String", "AD_UNIT_ID", "\"広告ID\""

って形で定義すれば問題ない。

しかし、これを(センシティブな情報だから、外部に設定+それを参照するような形とするために)環境変数などに設定しそれを参照する形で

buildConfigField "String", "AD_UNIT_ID", System.getEnv(定義したキー)

って書くと残念ながらビルドに失敗する。設定値側(環境変数)にダブルクォーテーションを含めて定義するか、参照時に文字テンプレートを使うことで解消できる。

AndroidManifest.xmlも参照する必要が出てくるはずなので、文字テンプレートを使った方が無難。つまり以下。

buildConfigField "String", "AD_UNIT_ID", "${System.getEnv(定義したキー)}"

参考

2023年7月31日月曜日

CI/CD 各種サービスを比較し使うものを選定する

この前の続き。CI/CD環境の構築に向けて、良さそうなサービスを探す。

CI/CDと聞いて真っ先に思いつくのはJenkinsだが、オンデマンドに構築できるほど私のプライベート環境は充実していないので却下。外部サービスで検討する。

調査・知っている範囲で、以下のサービスをピックアップした。

  • Github Action
  • Circle CI
  • TravisCI
  • Bitrise

で、結論としてBitriseで進めることにした。

理由としては

  • Bitriseはモバイルアプリ(特にAndroid)のインテグレーション(単体テストなど)、デプロイ(apkやaabの生成、GooglePlayStoreへのアップロード)が初期状態でほぼ構築済み。ユーザーは必要なパラメーターを入力するだけでほぼ実現できる。

Bitrise以外のサービスについては

  • GitHubActionはリポジトリをpublicにしたいという要件を満たせない。署名キーや広告IDは秘匿にできるにせよ、それにより生成されたバイナリが表に丸見えになりうる。やり方次第かもだが、バイナリを生成するワークフローを表に見せたくない…。
  • CircleCIはfastlaneの導入が必要になる。iOSアプリ開発者だと、コマンド一つでipaを生成するために導入したりなど比較的馴染みのあるツールだが、AndroidでCI/CDするためだけにわざわざ導入したいと思わない。
  • 残るTravisCIがBitriseの対抗馬となったが、導入の容易さから結果としてBitriseに軍配があがることとなった。

ただし注意事項として、Bitriseは1アカウントにつき処理できるジョブやタスクの回数?リソースの使用量?に制限がある。無料アカウントでは、毎月「300クレジット」という単位でサービスを利用できるが、これを越えると次のクレジット支給まで使えなくなる様子。
正直どんなもん?というところだが、(構築後日談として)1回のワークフロー(ビルドをするためのセットアップからビルド完了までの一連の流れ)を処理するのに大体10クレジット程度を消費しているため、30日間ではおよそ30回しか利用できない、ということになる。
とはいえ「プライベートで回している開発フローで、複数のプロジェクトを加味する必要があるとはいえ1ヶ月に30回もリリースビルド→デプロイすることあるか…?」と考えると、これが制限となってしまうことは稀になりそうだ。

というわけで今回はCI/CD環境を構築するためのサービスの選定とその理由について投稿した。次回はBitriseを利用したAndroidアプリのデプロイ環境の構築について投稿予定だ。

2023年7月30日日曜日

正規表現 文字列の一部だけを加工したい

Logcatとかコンソールでログとか漁ってるときに、「必要なところを探して、必要な所だけくり抜くのマジだるい…」ってなる。

正規表現を使えば簡単なんだけど、「必要な所だけくり抜く」表現がわからなかったので放置してた。でもいい加減(ちゃんと書ければ秒で済む話を何時間もかけて作業してるの、マジ無能…と思い)なんとかしたかったので、調べて備忘録として残す。

知ってしまえば本当に簡単なんだわ。

検索での表現

(検索したいけど置換したくない文字)なにかしら置換したい文字(検索したいけど置換したくない文字)

置換の表現

$1置換後の文字$2

ポイントは

  • 「くり抜くところ」を括弧書き()にすると、置換するときに$*(数字)て書けば検索したときに括弧書きに該当するところがそのまま残る。ちなみに、カッコはグループ化する表現らしい。
  • そのまま残したいところが複数ある場合でも、$に付ける数字を増やしていけばOK。

<div>タグを<p>タグに変えたい。"/"も気にせず置換したい。

検索での表現

<(/*)div>
  • 検索対象のdivを指定する。
  • /があるかないかは、置換後のテキストにも影響するので、()で書く。
  • *は0文字以上の意味。

置換での表現

<$1p>

  • 置換したい文字としてpタグを指定する。
  • ()で書いた箇所が$1として置換後のテキストに反映される。
  • 今回は、/があった場合に限り置換後に/が入るようにする。

参考

2023年7月29日土曜日

Android 環境変数や定数を build.gradleに持たせる実装を解説

どうも。どっことです。今回は環境変数や定数を build.gradleで管理する実装を解説します。

build.gradleで環境変数・定数を管理する実装

Gradleが自動生成してくれるBuildConfigを使うことで、buildTypesproductFlavorごとに初期値や定数値を持たせることができます。よくあるのはAPIのドメインや、広告IDの切り分けなどですね。開発版(buildType:debug)とリリース版(buildType:release)で開発用と本番用で切り替えるなどに利用できます。今回はBuildConfigにアプリ内で参照できるパラメータを乗せる実装を解説します。

BuildConfigに定数を乗せる実装

buildTypes(debug/release)ごとに定義する場合は以下でOKです。

  • defaultConfigに初期値を設定する。
  • buildTypesごとに定数の値を上書き設定する。
android {
  // ...
  defaultConfig {
      // buildTypesで上書きがなかった場合に設定される値
      buildConfigField "String", "AD_UNIT_ID", "\"とりあえずな広告ID\""
  }
  // ...

  buildTypes {
    release {
      // 本物の広告IDを上書きして定義
      buildConfigField("String", "AD_UNIT_ID", "\"本物の広告ID\"")
      ...
    }
    // とりあえずな広告IDをdefaultConfigに書いていますが、
    // debugブロックに書いて、defaultConfigは空文字などを指定しても問題ありません。
  }
}

あとは上記の定数を使いたいところで、以下ように参照すれば値を取得することができます。

const val AD_UNIT_ID_SIDE_BAR = BuildConfig.AD_UNIT_ID

まとめ

今回は、BuildConfigに定数を持たせる実装について解説しました。同じアプリでも開発環境やクライアントに見せる版、一般公開版など種別がさまざまになることが多いと思います。このような機能を利用して、ソースコード上に煩わしい実装が増やさないように心がけましょう。

参考

2023年7月27日木曜日

Android キーイベントを発行する実装方法を解説

どうも。どっことです。今回はキーイベントを発行する実装方法を解説します。

キーイベントを発行する

例えば方向キーのキーイベントを発行する場合、以下をつかいます。

KeyEventを生成して、dispatchKeyEventで投げるだけ。これだけです。

dispatchKeyEvent(KeyEvent(ACTION_KEY_DOWN, KEYCODE_DPAD_UP))

気をつけないといけないのは、KeyEventコンストラクタに渡している第一引数には ACTION_KEY_DOWN を指定することです。

そもそもACTION_KEY_DOWNはキー開始(画面やキーが押され始めた)、ACTION_KEY_UPはタッチ終了(画面やキーから離れた)を表すイベントですが、第一引数にACTION_KEY_UPを指定しても、OS側で制御してくれません。というのもOSからしてみれば

「押されてないのに離れたとか何事!?怖いから無視しとこ…」

という形に整理されていそうだからです。そのため、どうしてもACTION_KEY_UPでやりたかったら、ACTION_KEY_DOWNによりキーが押され始めたイベントも一緒に発行する必要があります。(どれだけ頑張る必要があるかは知らない…)ACTION_KEY_UPに思い入れしなければ、非常に簡単ですね。

まとめ

今回はキーイベントを発行する実装方法を解説しました。カスタムキーボードなどに役立てていただければと思います。

参考

2023年7月26日水曜日

Android Activityのスタックをクリアする

Intent.FLAG_ACTIVITY_CLEAR_TOPIntent#setFlag(int)でセットするだけ。

fun showTop(View view) {
  let intent = Intent(getApplication(), MainActivity::class.java)
  intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
  startActivity(intent);
}

参考

2023年7月24日月曜日

iOS Xcode ドキュメンテーションコメントを追加するショートカットキーを紹介

メソッドの上部で option + command + / を入力すると、自動でドキュメンテーションコメントが追加されます。

あとで見返すときのために、「何をするメソッドか」「引数にはどんな値が必要か、どういう値が想定されているか」「戻り値は何か」などを記入しておきましょう。

参考

2023年7月23日日曜日

Android 共有ボタンのデータを受け取る、送信する

どうも。どっことです。今回は共有ボタンの実装について解説します。

X(旧:Twitter)でのポストやブログの記事など、Webにはいたる所に共有ボタンがあります。今回は共有ボタンによりデータが送信された時にアプリ側でそれを受け取るための実装と、アプリで共有ボタンを用意する実装をそれぞれ解説していきます。

受け取る側の実装

よくあるBroadcastReceiverの実装パターンです。2つやることがあります。

1つは、AndroidManifest.xmlに共有ボタンによるデータ送信のintentを受け取る宣言を明記します。

<activity android:name=".MainActivity" >
  <intent-filter> 
    <action android:name="android.intent.action.SEND" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:mimeType="text/plain" />
  </intent-filter>
</activity>

そしてもう一つは、実際に受け取った時の処理です。受け取るActivityBroadcastReceiverに対してインテントフィルターを設定します。Activityでは、メンバ変数として持っているintentを、BroadcastReceiverではBroadcastReceiver#onReceivedの引数として渡されるintentを参照することで、詰められているデータにアクセスすることができます。

override fun onCreate(savedInstanceState: Bundle?) {
  if (intent?.action == Intent.ACTION_SEND) {
    val text = intent.getStringExtra(Intent.EXTRA_TEXT)
    // 良しなにする
  }
}

送る側の実装

こちらもよくあるstartActivity(Intent)の実装です。

ただintentの作り方だけちょっと独特で、共有したいデータを詰めたintentIntent#createChooserに渡します。そうして返されたintentstartActivityで投げます。

val temp: Intent = Intent().apply {
  action = Intent.ACTION_SEND
  putExtra(Intent.EXTRA_TEXT, "テストのテキストです")
  type = "text/plain"
}
startActivity(Intent.createChooser(temp, null))

まとめ

今回は共有ボタンに関するAndroidの実装を解説しました。特に共有ボタンからデータを受け取る処理はアプリを利用してもらう契機として重要なので、参考にしていただけると幸いです。

参考

2023年7月22日土曜日

Swift CocoaPods導入手順をメモ書き程度に紹介

Cocoapodsのインストール

sudo gem install cocoapods

または

sudo gem install -n /usr/local/bin cocoapods

Cocoapodsのセットアップ

インストール完了後、各プロジェクトで利用するためにCocoaPodをセットアップする。

pod setup

その後プロジェクトフォルダでコンソールを開いて以下コマンドを実行する。

pod init

これにより、Podfileが自動生成される。

ライブラリのインストール

自動生成されたPodfileに必要なライブラリの記述し以下を実行。

pod install

初回は上記、2回目以降は以下を使う。

pod update

これにより、.xcworkspaceという拡張子のファイルが生成される。Xcode でプロジェクトを開くときはこのファイルをクリックして開く。

参考

2023年7月21日金曜日

Mac スクリーンショットを撮影するためのショートカットを紹介

どうも。どっことです。Macでスクリーンショットを撮影したいけど、ショートカットキーを忘れて毎回検索しているので、備忘録として紹介します。

画面全体のスクショを撮影する場合

Shift + command + 3

Windowsの printscreen

範囲を指定してスクショを撮影する場合

Shift + command + 4

Windowsの Shift + win + s

ウインドウを指定してスクショを撮影する場合

Shift + command + 4 → Space

Windowsの alt + printscreen

iOS 数値を0埋めしてテキストとして表示する

Androidでは、String.format(text, value)としてテキストを出力させるが、iOSの場合もほぼ同じでStringのコンストラクタに入れるだけ。

let number = 3
let numberString = String(format: "%03d", number) // 003

参考

2023年7月20日木曜日

iOS アラートダイアログを表示するチートシート

iOSでよくあるアラートダイアログを表示する。

let alert = UIAlertController(title: "タイトルテスト", message: "メッセージテスト", preferredStyle: .alert)
// ボタンを追加する
let action = UIAlertAction(title: "ボタン", style: .default, handler: { action in
    /** do something **/
})
// 以下で、ボタンとそのタップ処理を追加する。ボタンは複数追加できる。
alert.addAction(action)
// 表示
present(alert, animated: true)

presentがライフサイクル上でコールするのが早すぎると無視されるので、viewDidAppear()以降のタイミングで呼ぶこと。

参考

2023年7月19日水曜日

iOS Enumの全要素を配列で取得する

enumの定義に、CaseIterable を実装してあげるだけ。

enum Size: CaseIterable {
  case maximum
  case big
  case medium
  case small
  case minimum
}

ってやると、allCasesにアクセスできるようになる

Size.allCases.map { /* do something. */ }

firstIndexでインデックスもとれるようになる

Size.firstIndex(of: .big)!

参考

2023年7月18日火曜日

Android Applicationクラスの実装方法について解説

どうも。どっことです。今回はApplicationクラスの実装方法について解説します。

Applicationクラスとは

アプリが動作するとき、一番最初に動き出すクラスです。このクラスにアプリの初期設定といった各種処理を実装します。外部のライブラリを利用する場合なども、ApplicationクラスのonCreateメソッド内に処理を実装することが多いので、遅かれ早かれAndroidアプリエンジニアの誰もが通る道かと思います。今回はそんなApplicationクラスの実装方法について解説します。

Applicationクラスの実装方法

以下の手順で実装します。

  1. Applicationクラスを継承したCustomApplicationクラスを追加する
  2. AndroidManifest.xmlCustomApplicationクラスを登録する

順番に解説していきます。

Applicationクラスを継承したCustomApplicationクラスを追加する

Applicationクラスを継承したCustomApplicationを追加します。クラス名に制約は無いので、分かりやすい名前を命名してください。

class CustomApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        ... // ライブラリの初期化やテーマの設定などやりたいことをやる
    }
}

AndroidManifestにCustomApplicationクラスを登録する

前手順で追加したCustomApplicationを登録します。AndroidManifest.xmlにあるapplicationタグにname属性で、クラス名を設定してください。

<application
    ....
    android:name=".CustomApplication">
    <-- よくあるよしなに -->
</application>

あとはビルド+インストールし、アプリを起動してあげると、CustomApplication#onCreate()の処理が最初に呼ばれます。Logなどを仕込んで、確認してみてください。

まとめ

今回はApplicationクラスについて解説しました。テーマの設定や各種ライブラリの初期化など、アプリをよりカスタマイズするにあたり必要になっていくクラスなので、使えるようにしておきましょう。


2023年7月17日月曜日

Android 子Viewをはみ出して表示させたい

「表示する領域のViewGroup」に以下を設定する

android:clipChildren="false"

気を付けないといけないのは、領域外に押し込まれるViewの親ではなく、押し込まれた領域のViewGroupに設定する必要があるということ。つまり以下。

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:clipChildren="false" <!-- 押し込まれる領域にclipChildren:falseを設定 -->
   android:layout_height="match_parent">
   <FrameLayout
     android:layout_width="250dp"
     android:layout_height="250dp"
     android:layout_gravity="center">
    
     <View
       android:layout_width="150dp"
       android:layout_height="50dp"
       android:layout_marginTop="-25dp" <!-- 親のFrameLayoutの領域外 -->
       android:background="@android:color/black"
       android:gravity="center" />
  </FrameLayout>
</FrameLayout>

言われてみればそうか、と思うけどちょっと気持ち悪いと思うのは私だけだろうか。

参考

2023年7月16日日曜日

Swift 引数付きStringイニシャライザ

引数付きString(format:, _ arguments)のイニシャライザの使い方

let month = "12"
let day = "31"
let text = String(format: "%@月%@日", month, day)
print(text)

2023年7月15日土曜日

CI/CD 最近考えていること リリース手順が面倒くさい

毎回毎回、

署名キー探して、
設定値を入力してビルドして
aabファイル生成して
プレイストアにアップロードして

ってフローが面倒になった。どうにかならないものか。

少なくても、署名キー探す~aabファイル生成するまでを自動化できることは知っているけど、例えばGitHubActionでやるなら生成したaabファイルや署名キーを秘匿にしないといけない都合でリポジトリも秘匿にしないといけないし、でもそれを秘匿にするのは正直あまりうれしくない(できるだけパブリックにしておきたい)。どうにかならないものか。

って考えたときに、「ソースを管理する領域(GitHub)とaabファイル生成をする領域を別にすればいいんじゃん。そうすればソースはパブリックのままでいいし、署名キーやら生成したaabファイルやらも秘匿の状態を保てる。とても幸せやん。」と気づいた。

上記を整理すると以下のフローとなる。

  1. ローカル環境で開発
  2. GitHubに変更をプッシュ
  3. GitHubはGitHubActionのジョブを回す
    • テストを回したり
    • Readme更新したり
    • CI/CDサービスに登録してあるジョブのトリガーを引いたり ★1
  4. 前項のトリガーを受けて、CI/CDサービスはaabファイルを生成する。
    1. GitHubからソースコードをクローンしてくる。
    2. aabファイルを生成し、成果物として管理。
  5. CI/CDサービスはプレイストアにaabファイルをアップロードする。 ★2
    1. どこまでできるか次第だが、リリースノートなども合わせてアップロードできればなお嬉しい。
  6. CI/CDサービスはすべての処理が完了したことを通知する。
  7. 私はそれを確認して、特に問題なければリリース。何かあれば適宜修正。

素敵だ。上記フローを実現するために必要な調査は★1,2の内容。

  • ★1:登録したジョブを実行するトリガーを外部から引くことはできる?(CI/CDのサービス次第?)
  • ★2:aabファイルをプレイストアにアップロードするクチ(=IF)は用意されている?どこまでの情報をアップロードできる?

まあ、「そんなのありません」ってことはないんだろうけどね。特に★1については「サービスとしてはあるけど、有料会員様限定のサービスなんすよ」かもしれないし。

というわけで、考えていることでした。調査した結果とか、更新があればまた改めて投稿します。

2023年7月14日金曜日

iOS UILabel/UITextField/UITextViewの違い

  • UILabel:AndroidのTextViewと同じ。テキストを表示するだけのView。
  • UITextField:singleLine限定のEditText。入力は一行のみ。
  • UITextView:AndroidのEditTextと同じ。入力は複数行。

2023年7月13日木曜日

iOS よくわからないエラー集「failed to get the task for proces」

Xcodeのプロジェクトで設定した証明書ファイル群にデバッグビルドによるビルドができるような設定になっていない。

参考

2023年7月12日水曜日

Android OnClickListener はXMLレイアウトファイルからでも設定できる

どうも。どっことです。今回はXML上でクリックリスナーを設定する実装方法について解説します。

XMLでクリックリスナーを設定する

dataBindingでは、XMLでクリックリスナーを設定する処理はよくある実装ですが、実はdataBindingを使わない通常のケースでも似たような実装をすることができます。

解説

XMLでクリックリスナーを設定するには、任意のViewに対して android:onClickを設定してあげます。たとえば、TextViewに設定するときは以下のような形です。

<TextView
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text="テスト"
   android:onClick="testClicked" />

あとはこのXMLを読み込むクラスに、testClicked(View v)関数を実装します。

override fun testClicked(v: View) {
  Log.d("TAG", "TextView テスト is clicked")
}

今回設定したonClickの値はtestClickedなのでそれに合わせていますが、実際に利用する際はTextViewがどんな表示をしているかなどを表現した関数名にしてあげると後々にコードを追いやすくなるかと思います。

まとめ

今回はXMLでクリックリスナーを設定する実装方法について解説しました。dataBindingでも似たような実装方法があると思いますが、そうでなくてもシンプルに利用することができるので、参考にしていただけると幸いです。

参考

2023年7月11日火曜日

Android ダークモードを実装するときの実装手順について解説

どうも。どっことです。今回はダークモード対応に関する実装について解説します。

ダークモードを実装する

アプリをいい感じに見せたいのであれば、ダークモード対応は今や必須の項目の一つではないでしょうか。ユーザへの見せ方をライト/ダークでそれぞれ管理する必要があるのは大変ですが、それ以上にユーザに好印象を与えることができるアプローチになります。今回はそんなダークモード対応をする時のアプローチ方法を解説します。

実装手順

必要最小限でダークモードを対応する場合、以下の手順になります。

  • ダークモードにした時にどのように見えるか確認
  • 修正が必要な箇所について色指定を修正
  • ライト/ダークモードの切り替え動線を用意

順番に解説していきます。

ダークモードにした時にどのように見えるか確認

まずは以下をApplication#onCreate()で呼び出してみましょう。

AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)

これを呼び出すとアプリがダークモードで表示されます。この状態でアプリがどのように見えるか確認しましょう。

修正が必要な箇所について色指定を修正

テキストが背景と同色で見えなくなっているなど、残念な表示となっている箇所は適宜修正しましょう。values-nightフォルダを作ってそこにcolors.xmlを用意し、色指定を追加してください。このときvalues/colors.xmlでの定義とvalues-night/colors.xmlで定義するnameは同じものを指定してください。そうすれば、ライト/ダークの切り替え時にそれぞれの色指定を参照してくれます。逆に異なるものを指定すると、ビルド時に警告表示されるはずです。

ライト/ダークモードの切り替え導線を用意

あとはユーザがライト/ダークモード(+端末の設定に従う設定)を切り替えられるよう、切り替えスイッチのような動線を用意しておきましょう。アプリ内に設定画面などがあれば、そちらに用意するのが適切かと思います。

切り替え時の処理

ユーザがスイッチなどで設定を変えた場合の処理が必要です。ユーザの切り替え設定に従い、テーマを更新します。また、設定した値はアプリ固有領域(例えばSharedPreferenceなど)に保持しておきましょう。以下はSharedPreferenceに保存した時のサンプルです。

/** 
 * ユーザが設定したテーマ。
 * AppCompatDelegate.MODE_NIGHT_YES, 
 * AppCompatDelegate.MODE_NIGHT_NO など 
 **/
val value = AppCompatDelegate.MODE_NIGHT_YES
AppCompatDelegate.setDefaultNightMode(value)
getSharedPreferences("sample",  MODE_PRIVATE)
  .edit().putInt("theme_setting", value).apply()

アプリ起動時の処理

アプリを起動した時に、ユーザの設定に合わせてテーマを更新する必要があります。先ほど実装したApplication#onCreate()の実装を以下に変更してください。

val value = getSharedPreferences("sample", MODE_PRIVATE)
            .getInt("theme_setting", AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
AppCompatDelegate.setDefaultNightMode(value)

まとめ

今回は、アプリにダークモードを実装する時の実装手順について解説しました。ダークモードはユーザに良い印象を提供するためには必須の項目となっているので、今回の内容を参考にして是非対応してみてください。

参考

2023年7月5日水曜日

OutlookでリンクをクリックするとEdgeが開くんだが

以下の設定を更新する。

ファイル > オプション > 詳細設定 > リンク処理

2023年5月23日火曜日

CSS に追加するときの タグ/クラス/IDの指定方法

どうも。どっことです。今回はCSSを追加するときによく忘れるタグ/クラス/IDの指定方法について整理しました。

タグ指定によるカスタマイズ

タグ指定によるCSSは タグ名をそのまま書きます。つまり以下です。

/* bodyタグの指定*/
body {
 ...
}
/* h1クラスの指定 */
h1 {
 ...
}

クラス指定によるカスタマイズ

クラス指定によるCSSはピリオド「.」をつけて書きます。

/* hogeクラスの指定 */
.hoge {
 ...
}
/* fugaクラスの指定 */
.fuga {
 ...
}

また、タグ指定とクラス指定を組み合わせることができます。

/* hogeクラスが設定されているdivタグの指定 */
div.hoge {
 ...
}

/* hugaクラスが設定されているpタグの指定 */
p.fuga {
 ...
}

ID指定によるカスタマイズ

ID指定によるCSSはシャープ「#」をつけて書きます。

/* ID:header */
#header {
 ...
}

/* ID:footer */
#footer {
 ...
}

また、ID指定も、タグ指定、クラス指定を組み合わせることができます。

/* ID:headerが指定されているdivタグ */
div#header {
 ...
}

/* new-articleクラス、ID:title 指定 */
.new-article#title {
 ...
}

/* divタグ、commentクラス、ID:title 指定 */
div.comment#title {
 ...
}

指定のタグ/クラス/ID内の要素に対してCSSを指定できます。

/* ID:headerが指定されている要素内のaタグ指定 */
#header a {
 ...
}

/* new-articleクラス内の div 指定 */
.new-article div {
 ...
}

参考

2023年4月3日月曜日

CSS 親要素を基準に位置を決めたい

親の領域を基準に特定の要素の位置を決める、AndroidでいうところのRelativeLayoutな位置調整をしたかったので実装方法を調べた。

親のスタイルに対して以下を設定する

position:relative

位置を決めたい要素のスタイルに対して以下を設定する

position:absolute

上記を設定したら、あとは特定の要素に対してleft,top,right,bottomを指定すればOK。特にやりたかった右下を指定する場合は以下。

right:0
bottom:0

参考

2023年2月17日金曜日

SQL テーブルを内部/外部結合するSQL文を解説

アプリの中でデータベースを実装するとき、いくつかのテーブルを結合させてまとめて情報取得したい機会が多く出てきます。テーブルの結合にはいくつか種類がありますが、使用頻度が多いのは、内部/外部結合でしょうか。

今回は、頑張ってテーブルを設計していい感じに正規化したけど、いざ必要なデータを取ろうとしたときに内部結合/外部結合どう書けばいいんだっけ...という時のために、それぞれの書き方を備忘録として説明します。

内部結合の場合

SELECT * FROM table_a INNER JOIN table_b ON table_a.id = table_b.id

内部結合は、結合する際に結合条件に合致するもののみ一覧として出力される結合方法です。本当に必要なものだけ取得したい、という場合はこちらになると思います。

外部結合の場合

SELECT * FROM table_a LEFT JOIN table_b ON table_a.id = table_b.id

内部結合は合致するもののみ出力してくれる一方で、外部結合は結合する際に結合条件に合致しないものもNULLとして出力してくれる結合方法です。すべての項目を一覧として表示したいなどはこちらになると思います。結合には上記の左外部結合(LEFT OUTER JOIN)だけでなく右外部結合(RIGHT OUTER JOIN)もありますが、今回は省略します。(もしくは追記します。)

移行予定

どうも。どっことです。 タイトルの通りですが、諸事情により GitHubPage に移行予定です。 https://mkt120.github.io/ この備忘録に記載の内容を転記しつつ、今後はこちらのページを更新していく予定です。