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を使っている開発者も少なからずいるかと思いますので、どんどん新しい機能を使えるよう情報をアップデートしていきたいですね。

参考

2022年8月4日木曜日

Android DialogFragmentをフルスクリーン化する実装方法を解説

どうも。どっことです。今回はDialogFragmentをフルスクリーン化する実装方法を解説します。

DiagloFragmentをフルスクリーンで表示する

DialogFragmentはダイアログを表示するためのクラスです。シンプルなダイアログを表示するために使うのはもちろんですが、自分で色々カスタマイズできる拡張性の高いクラスでもあります。今回は、そんなDialogFragmentをカスタマイズするために画面いっぱいに表示する実装(フルスクリーン化)を解説します。

実装方法

今回はまとめてスニペットを載せたいと思います。ポイントはonStart()です。
class CustomDialogFragment : DialogFragment() { 
  
  override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    return Dialog(requireActivity()).also { dialog ->
      // 表示したいViewをここで生成します。LayoutInflaterなどを使ってもOK
      val view = View(requireContext()) 
      dialog.setContentView(view)
    }
  }

  override fun onStart() {
    super.onStart()
    // onStart() で以下の処理をします。
    dialog?.window?.also {
       // ①背景色を背景透過にするよう設定(完全にフルスクリーンにするなら必要)
       it.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
       // ②画面サイズに合わせて領域を広げる
       it.setLayout(
          ViewGroup.LayoutParams.MATCH_PARENT,
          ViewGroup.LayoutParams.MATCH_PARENT
       )
    }
  }
}

onStart①背景色を透過にする②表示領域を画面いっぱいに広げるように設定します。これにより表示するダイアログをフルスクリーン化することができます。

まとめ

今回はDialogFragmentのフルスクリーン化について解説しました。DialogFragmentは拡張性が高い反面、使いこなすのが非常に難しいクラスです。ただ正しく理解さえできれば一つ一つの処理は難しいものでは無いので、どんなカスタマイズでもできるようにマスターしていきたいですね。


Android ProgressBarで進捗を表示する

横に伸びるProgressBarを表示する。

レイアウトファイル

    <ProgressBar
        android:id="@+id/progress_bar"
        style="?android:attr/progressBarStyleHorizontal"
        android:max="100"
        android:min="0"
        android:layout_width="240dp"
        android:layout_height="30dp"/>

コードで進捗を更新する。

val progressBar = it.findViewById<ProgressBar>(R.id.progress_bar)
progressBar.progress = 0 // アニメーション無し
progressBar.setProgress(progressBar.progress + 50, true) // アニメーション有り

// 滑らかなアニメーション
val animation = ObjectAnimator.ofInt(progressBar, "progress", progressBar.progress + 50)
animation.setDuration(1000); // 1000ms = 1sec の時間でアニメーションさせる
animation.setInterpolator(new DecelerateInterpolator());
animation.start();

2022年8月3日水曜日

Android ディレクトリを作成する

どうも。どっことです。今回はアプリ固有のファイル領域にディレクトリを生成する実装方法を解説します。

さっそくですが、実装サンプルです。階層構造のあるディレクトリを生成するケースとして、階層ごとに生成するケースとまとめて生成するケースを紹介します。

ディレクトリ階層ごとにディレクトリを生成する

階層ごとに生成するケースを紹介します。最初にディレクトリを生成したあと、そのディレクトリを親ディレクトリとして指定する形で子ディレクトリを生成します。

val parent1 = File(requireContext().filesDir, "parent1")
// ディレクトリ(parent1)を作成する
parent1.mkdir()
val parent2 = File(parent1, "parent2")
// 作成したparent1ディレクトリを親にして、子ディレクトリ(parent2)を作成する
parent2.mkdir()

複数のディレクトリ階層があってもまとめて生成する

こちらはまとめて生成するケースです。ディレクトリ名の指定に親ディレクトリを含めることで、その親ディレクトリもまとめて生成してくれる。というわけです。唯一注意が必要なのは、ディレクトリを生成する関数が前のサンプルではmkdir()をつかっていましたが、今回はmkdirs()を使っている点でしょうか。

val parents = File(requireContext().filesDir, "parent1/parent2")
parents.mkdirs()

移行予定

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