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)もありますが、今回は省略します。(もしくは追記します。)

2022年12月19日月曜日

2022年の振り返りと2023年への抱負

どうも。どっことです。

気がつけば2022年ももうすぐ終わりということで、開発者としての2022年の振り返りと来たる2023年への抱負なんて書かせてもらえればと思います。

2022年の振り返り

SimpleBookmarkについて

夏頃まではちょいちょい更新しました。しかし、これ以上の機能追加は利用者に対しての労力に見合わないなと思ったので、2022年の目標としていた大型アップデートはお蔵入りとなりました。また、今後もアップデートが入るか微妙です。

新規アプリ開発について

2022年の目標としてSimpleBookmarkに続いてさらに新しいアプリを公開することが目標となっていましたが、実現することができませんでした。アイディアはじゃんじゃか出てくるのですが、それを実現するための時間とエネルギーが全くもって足りませんでした。

技術ブログの更新

定期的に、とはいきませんでしたがAndroidやFlutterを中心に備忘録として技術ブログを更新しました。同じことを調べることが多いので、できる限り自分のブログにノウハウを集約させたいですね。

あとはDroidKaigiの登壇を視聴したときのメモ書きもブログページに載せました。DroidKaigiは毎年開催されるので、次回も気になるものは視聴+投稿しようと思います。本当はオフラインで参加してみたいんですけどね。

その他

GitHubPage上で、Flutter for web によるウェブページを構築しました。

Flutter for web によるUIは非常にネイティブ感の強いUIで、普通のウェブページとは一味違った印象を持てる反面「なんとなくコレジャナイ感...」を感じることになりました。もともとはFlutterで良さげなものを作ってみようというところから始めたので当初の目的は達成できたものの、ウェブサイトを作るならやっぱりReactのほうが良いのかなと思いました。

2023年への抱負

ReactによるGitHubPageの更新

GitHubPageをさらにリニューアルしようと思います。Flutterとは毛色が全然違いますが、アーキテクチャを意識した設計がしやすそうですし、また別の面白さを得られればと思います。

新規アプリ開発

...2023年こそね。まずは自分が一番の利用者として使いたいアプリを作れればと思います。

2022年8月28日日曜日

ShellScript 引数を参照する実装について解説

どうも。どっことです。今回はシェルスクリプトの引数を参照する実装について解説します。

手作業が面倒なものをスクリプトで自動化して、ゆくゆくは「自分はボタンを押すだけ」なんて野望は誰しもが持つかと思いますが、まずはその第一歩(笑)として、シェルスクリプトの引数を参照してみましょう。

引数を参照する方法について解説

さっそくサンプルを載せます。 $(数字) の形でアクセスすることで、スクリプト名から各引数の値や文字列にアクセスすることができます。また、$#とすることで、引数の個数を取得することもできます。

#! /bin/bash

#スクリプト名を表示
echo "script=" $0 

#引数の個数を表示
echo "args=" $# 

#1つ目の引数を表示
echo "argv[0]=" $1 

#2つ目の引数を表示
echo "argv[1]=" $2

#渡された引数すべてを表示
echo "argv[]=" $@ 

わかってしまえば、簡単ですね。

2022年8月23日火曜日

Android seletorにcolorを設定するときの注意点

android:textColorに設定する場合は以下。

<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:state_selected="true" android:color="#aaa" />
  <item android:state_selected="false" android:color="@android:color/transparent" />
</selector>

一方で android:background に設定する場合は以下。

<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:state_selected="true" >
    <color android:color="#aaa" />
  </item>
  <item android:state_selected="false">
    <color android:color="@android:color/transparent" />
  </item>
</selector>

参考

Android TextViewの一部にだけリンクをつけたい時の実装方法を解説

どうも。どっことです。今回はTextViewの一部にだけ、外部リンクを表示したい時の実装方法を解説します。

TextViewのテキストの一部にリンクをつける

例えばお知らせの記事にある「詳しい内容はこちら」のこちらだけ別ページへのリンクにしたいとき、TextViewを横並びにすれば実現できますが、そのテキストが二行以上になったり、他のテキスト間に含まれた箇所にあるとこの方法では実現できません。これを解決する実装方法を2種類解説していきたいと思います。

実装方法

外部のWebページに遷移する場合であれば簡単です。以下の2種類を解説していきます。

  • TextViewautoLinkwebを設定する方法
  • HtmlCompat.fromHtmlを利用する方法

TextViewのautoLink属性にwebを設定する方法

TextViewを配置しているレイアウトXML内で、autoLink="web"を設定するだけです。仕込むリンクが単純なURLであれば、それをテキストとして設定するだけでそれっぽい表示になります。

<TextView
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:text="https://www.google.co.jp/ とテキストを仕込むとGoogleページのリンクになります。"
  android:autoLink="web"
  />

実際に表示した時のイメージは以下です。https://...のURL部分がリンクになっていると思います。タップするとURLのページに遷移します。

HtmlCompat.fromHtmlを利用する方法

こちらは上記の例で挙げたような「こちら」という文言をリンクにする方法です。AndroidにはSpanというクラスがあります。HTMLを使ってホームページを作ったことがある方ならspanというタグを使うことも頻繁にあると思いますが、Androidでもこの概念を使ってテキストの一部を装飾することができます。こちらを使って外部リンクをつけます。

HtmlCompat.fromHtml に、HTMLのテキストを渡すとWebページのようにテキストを表示してくれるSpannedを生成してくれるので、これを表示したいTextViewに設定してください。

val textView = findViewById(R.id.text_view)
val span = HtmlCompat.fromHtml("詳しい内容は<a href='url'>こちら</a>")
textView.setText(span)
textView.movementMethod = LinkMovementMethod()

実際に表示した時のイメージは以下です。こちらの部分がリンクになっていると思います。タップするとurlで指定したページに遷移します。

まとめ

今回はテキストの一部にだけリンクをつけたい時の実装方法について解説しました。実際に使うケースはかなり限定的になりますが、こういったポイントをおさえることで、アプリ全体の見栄えを良くしていければ嬉しいですね。

参考

2022年8月20日土曜日

Android deprecatedとされたsetTargetFragmentを推奨先に変える方法を解説

どうも。どっことです。今回はdeprecatedとなっているFragment#setTargetFragmentの移行対応について解説します。

setTargetFragmentの移行対応について

複数のFragmentを使って画面遷移を実装するとき、遷移先の画面で決めたことを遷移元の画面に教えてあげたいときがあります。たとえば

  • 設定画面で設定の要素をタップしたらそれに関する設定値の一覧が表示される。
  • ユーザは一覧から設定項目を選択する。
  • 選択した値を設定画面に返してあげて、設定画面にはユーザが選択したものを最新の設定値として表示する。

などですね。

これまではFragment間のデータのやりとりはtargetFragmentを使うことで実現できました。呼び出し元のFragmentを遷移先から参照できるようにして、その参照から呼び出しもとにデータを返してあげる。という具合です。

さて、「これまでは」と書いた通り、このメソッドdeprecated(非推奨)になってしまいました。これの扱いが簡単だったので個人的に非常にありがたかったのですが、Google様のお達しなので、今後は推奨されるやり方で実装する必要があります。今回は備忘録も含め、新しい実装方法を載せたいと思います。

解説

以下の順に解説していきます。

  1. 呼び出される側の実装
  2. 呼び出す側の実装
    1. Activity → Fragmentの場合
    2. Fragment → Fragmentの場合

呼び出される側の実装

parentFragmentManager.setFragmentResult("request_key", Bundle())    

呼び出される側の実装はシンプルで、(自身を管理している)parentFragmentManagerに渡したいデータを設定します。 通知したい情報をBundleに詰める必要があるため、上記の例は必要最低限のスニペットです。

呼び出す側の実装

Activity -> Fragment の場合

supportFragmentManager.setFragmentResultListener("request_key", this) { _, result ->
    // result を参照して結果を処理する
}

呼び出すFragmentActivityが持っているsupportFragmentManagerが管理しているので、それに対してResultListenerをセットしてあげます。ActivityDialogFragmentを表示するときは上記の実装になります。

Fragment -> Fragment の場合

childFragmentManager.setFragmentResultListener("request_key", this) { _, result ->
  // result を参照して結果を処理する
}

一方で Fragment -> Fragment の場合はちょっとだけ注意が必要です。呼び出すFragmentを呼び出し元のFragmentが持つFragmentManager管理している場合、こちらの実装パターンが利用できます。例えばFragmentが、自身が持っているchildFragmentManagerを使ってDialogFragmentを表示するなどの場合は、こちらの実装が使えます。

ただし、表示するDialogFragentActivityが持っているsupportFragmentManagerが表示する場合、このパターンではなく Activity -> Fragmentのパターンで実装することになります。表示するDialogFragmentをどのFragmentManagerが管理するかが違うためです。

まとめ

今回はdeprecatedとなっているFragment#setTargetFragmentの移行について解説しました。開発期間が長いアプリケーションでは、まだまだsetTargetFragmentFragment間での結果のやり取りを処理しているものも多いのではないでしょうか。deprecatedとなっているメソッドのため、なるべく早めに解消しておきたいですね。

Kotlin Collectionsをシャッフルするときの実装について解説

どうも。どっことです。今回はKotlinによる実装で、Collections派生の要素をシャッフルする実装について解説します。

Collectionsの要素をシャッフルする時の実装

Javaでいう所の以下のメソッドのことです。

Collections.shuffle(List)

Kotlinでは、以下のように実装します。

// シャッフル対象となるList
var mutableList = mutableListOf<Any>() 
Collections.shuffle(mutableList)

ポイントはシャッフルするCollectionsがMutableである点です。

Kotlinでは同じCollectionsでも、要素を加工できるMutableという概念があります。要素が加工できるCollectionsは、クラス名の接頭語にMutableが付与されています(MutableList, MutableHashMapなど)。逆に要素を加工できないものはMutableが付与されていません。そのため、Kotlinに慣れていないJavaユーザがいざListなどのインスタンスを作っても「要素の追加や削除ができない...」といったトラブルが発生しがちです。

上記の経緯から、シャッフル対象のCollectionsはMutableにする必要があります。という話でした。

まとめ

今回はCollections派生の要素をシャッフルする実装について解説しました。JavaユーザがKotlinに移行するときのすごくよくあるハマりポイントだと思うので、ぜひ参考にしてくださいね。

2022年8月13日土曜日

Android deprecatedとなったstartActivityForResult()の修正方法を解説

どうも。どっことです。今回は、deprecatedとなってしまったActivity#startActivityForResult()の修正方法について解説します。

deprecatedとなったstartActivityForResult()の対応

一昔前のAndroidアプリ開発であればActivity#startActivityForResult()による画面間のデータやり取りは鉄板中の鉄板として解説されていたネタですが、残念なことにこのメソッドがdeprecatedとなってしまいました。初級者でも使い勝手がよい背景から、さまざまなプロジェクトで使用されていたこのメソッドですが、いざdeprecatedとなっても修正できず困っている人も多いと思います。今回は、実は簡単にできるstartActivityForResult()の修正方法について解説します。

修正方法

修正方法は以下の手順です。

  1. registerForActivityResult()launcherを用意する。
    • Activityが終了したときの結果をここで実装しておく。
  2. Activityを起動するためのIntentを生成する。
  3. launcherを使ってIntentlaunchする。
順に解説していきます。

registerForActivityResult() でlauncherを用意する

Activityに新しく用意されているregisterForActivityResult()を使って、launcherを用意します。この時に、Activityから渡される結果を制御するための処理を実装しておきます。

val launcher = registerForActivityResult(StartActivityForResult()) { result ->
    // onActivityResultに対応するコールバック
    if (result.resultCode != Activity.RESULT_OK) {
        // Activity.RESULT_OK 以外
        return@registerForActivityResult
    }
    // RESULT_OK時の処理 
    // setResultで設定したintentから値を取得する
    val value = result.data.getStringExtra(key)
    // 取得した値を良しなにする
}

Activityを起動するためのIntentを生成する

ここまで実装すれば、あとはstartActivityForResult()の時と同じです。Intentを作成します。

val intent = Intent(applicationContext, NextActivity::class.java)

launcherを使ってIntentをlaunchする

作成したintentlauncherlaunchしてあげれば、修正できます。

launcher.launch(intent)

まとめ

今回はActivity#startActivityForResultのdeprecated解消方法について解説しました。いまだにstartActivityForResultを使っている開発者も少なからずいるかと思いますので、どんどん新しい機能を使えるよう情報をアップデートしていきたいですね。

参考

移行予定

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