Little Strange Software

スマホアプリの開発を行う LittleStrangeSoftware のブログです。

Androidアプリ開発初心者がkotlinでカラーピッカーを作ってみるよ! その3(完成!)

 

 どうも!LSSです!!
 カラーピッカー作成、第3回、やっていきまーす!

 

 自分の学習目的で作成しはじめましたが、特に自分がモノにしたかったのは

「配列」

「イベント処理」

の2つでした。(思いのほか、xmlのデザインにも新たに学ぶ事がありましたが)

 と、いうわけで、やっと本題のkotlinコード作成に取り掛かります!

 

配列変数について

 配列変数は、ループ処理と合わせて、プログラミングの最重要な部分だと思っています。プログラミングってのがそもそも
「人間がいちいちするのが面倒くさい処理をコンピュータにさせる」
ための指示書で、それは例えば小学校の算数ドリルで、

5×4=

6×3=

8×7=

みたいな単純な計算がずらずらっと20問ぐらい並んでた場合、算数ドリルなら自分が覚えるためにするので自力で解くべきですが、これが大人になって、学習のためではなく、「仕事」としてするハメになったら?それも20問どころじゃなく一万回ぐらい。

 となった時なんかに、設問を先に全部配列に突っ込んでおいて、全て処理するループをプログラムすれば、あとはコンピュータが文句を言わず、しかも高速・正確に答えを作ってくれるわけです。

というように、コンピュータの利点を引き出しやすいものの大きなひとつが「配列変数」だと思うのです。

 

 さて、kotlinで配列はどうやって作るのかなー、とGoogle先生に「kotlin 配列」とお伺いを立て、先人の方が作ってくださっているサイトを見ると、

listOf

setOf

mapOf

arrayOf

ArrayList

などが出てきて、「???」ってなりました^^;
 何種類もあるけどどう使いわけるんだろう??ってとこで。

 いくらか違いが分かったものもあり、分からない部分もあり…ですが、とりあえず使っていきます!

 

カラーピッカーでkotlinにして欲しい仕事

 赤・緑・青のシークバーを、ユーザー(人)が指でツマミを動かした時に、その都度、ツマミの位置から「赤・緑・青の強さ」から色コードを生成して、画面の背景色(LL0と名付けたLinearLayoutのbackgroud)をその色に染め上げ、ついでにet0と名付けたEditTextにその色コードを表示する。

というモノを作ろうとしています。

ツマミを動かした時」に処理をしてほしいので、「シークバーに変更があった時」を監視して、その時に処理を行うようなプログラムであれば良さそうです。

 また、今回の場合、シークバー3つありますが、3つのうちのどれかに変更があった時に、全部計算しなおして色コードを生成するつもりなので、「3つのうち、どれが変更があったか」を区別する必要はない事から、

3つのシークバーを配列に入れて、ループ処理でイベントリスナーを定義させよう!

って事にしました! 

※そうしたい理由いくつか
・自分が配列を使ってみて、覚えたい。
・イベントリスナー、まだよく分かっていませんが、シークバーのイベントリスナーを
 試してみた事はあって、使わないものまでoverrideして書かなきゃいけないのもあって
 シークバーを複数使うとなると、なおさら面倒くさい。
・ループ処理でイベントリスナーの定義、ってのが実際できるのか試してみたい。 

 

 

配列にシークバーを設定

f:id:little_strange:20190921165955p:plain

  onCreateの中に書くので、setContentView(R.layout.activity_main)の行の後にEnterキーでスキマ行をいくつか開けておきます。

 そしてそこに、
 val sb=listOf(sbR,sbG,sbB)

と入力します。

f:id:little_strange:20190921170639p:plain

sbR、まで打ったところで、こんなのが出てきました。
同意、ってつもりでEnterキーを押すと、xmlで設定したsbRをkotlinから使えるようにするためのimportが自動的に追加されます。

f:id:little_strange:20190921170950p:plain

  直後の画面は↑こちら。importが追加されている事と、配列がちゃんと「これはSeekBarである」と認識してくれている事が確認できます。

 あと「,sbG,sbB」も付け足して、
sbRとsbGとsbBという3つのシークバーを要素に持つsbという名前の配列」が完成です!

f:id:little_strange:20190921171416p:plain

 

ループ処理を書くよ~

 さて、ループ処理を書いていきます。
 配列sbを宣言した後の行に、

sb.forEach(){

}

と入力します。画面上には、

f:id:little_strange:20190921181347p:plain

のように表示され、やはりsbがSeekBarである事をAndroidStudioが認識している事と、forEachのループ内で「it」って単語によって配列内の要素を扱える事を教えてくれています。

 

ループ内でイベントリスナーを定義

 forEachの中(次の行)にカーソルを合わせ、さっそくその「it」を手打ちで打ち、「.」(ピリオド)を打ちます。すると…

f:id:little_strange:20190921182004p:plain

こんな感じで、続きの候補がずらずらっと出てきます。
 ここで、setOnSeekBarChangeListenerを選択します。
すると、選択したsetOnSeekBarChangeListenerが出てくると同時に、その後ろに「()」がついてきますので、その()の間に

object : SeekBar.

と打つと…
:の前後にスペースを入れる事と、SeekBarの部分的に大文字にする事に注意)

 

f:id:little_strange:20190921182442p:plain

また、こんな感じで候補が出てきます。

 素直にここは、このOnSeekBarChangeListenerで決定しておきます。

 

 決定後、まだ先ほどの「)」が残っていますが、
その「)」の手前に「{」を入力します。そしてEnterキーを何度か押してスキマを開けておきます。

 「}」が自動補完されて、画面はこんな感じになります。

f:id:little_strange:20190921183318p:plain

…何やら、objectが赤い波線で何かを訴えかけているので、objectをクリックして、「ALT+Enter」キーを押してあげてみます。

f:id:little_strange:20190921183556p:plain

 すると、こんな候補が出てきます。
 一番上のImprement membersを選択します。すると…

f:id:little_strange:20190921183754p:plain

 こんなダイアログが出てきます。

 ここでは、onなんとかが3つありますが、
青くなってない残り2つも、CTRLキーを押しながらマウスでクリックしてあげて、

f:id:little_strange:20190921184057p:plain

3つとも青くなったところで「OK」をクリックします。

 すると…

 

f:id:little_strange:20190921184317p:plain

突然、3つのoverride funで始まるモノが挿入されます。
行数が多くて、ちょっとビックリ!

 

整形します。使わない部分は極力短くする方向で…。

 今回、onStartTrackingTouchonStopTrackingTouch  には用がないので、邪魔にならないよう、極力短くしておきます。
とは言え、「{」と「}」は残しておかなきゃ、ってとこに注意しながら…

f:id:little_strange:20190921185127p:plain

 こんな感じになりました。

 さらに、onProgressChangedの中身もTODOの部分は1行まるごと削除して、そこに

sbChanged()

と入れておきます。(これは、これから作成する関数名で、任意の文字列です。)

 ここまでの時点での、コードの全容は↓のようになります。

package jp.littlestrangesoftware.a20190919colorselect

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.SeekBar
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val sb=listOf(sbR,sbG,sbB)

sb.forEach(){
it.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{

override fun onProgressChanged(p0: SeekBar?, p1: Int, p2: Boolean) {
sbChanged()
}
override fun onStartTrackingTouch(p0: SeekBar?) {}
override fun onStopTrackingTouch(p0: SeekBar?) {}
})

}





}

}

 

 ここまでで、イベントリスナーの設定は終わりです。

 forEachのループによって、3つのシークバーのProgressChangedされた場合、つまりシークバーの示す値が変更された場合、どのシークバーであっても「sbChanged」という処理を呼び出すように、という設定ができたところです。

 まだsbChangedを作っていないので、画面上では

f:id:little_strange:20190921190500p:plain

こう、赤くなっちゃってますけどね^^;

 

続いて、処理内容を書いていきます

 sbChangedと名付けた処理を作っていきます!

 入れる場所は、onCreateの外側、かつ、MainActivityの内側、なので

 f:id:little_strange:20190921200214p:plain

 ここ!下から数えて1つめの}と2つめの}の間に書きます。

 画面外、下部にMainActivityって出ていればOKです。

 

 ここに、

fun sbChanged(){

}

と入力します。

…さて、ここでさせたい処理は、

et0というEditTextにカラーコードを表示する。
LL0というLinearLayoutの背景色を設定する。

の2つですが、まずはEditTextのほうからやっていきます。

 シークバーの値は、「sbR.progress」みたいな書き方でInt型の数値が取得できるので、それを「#FFFFFF」みたいな形式に変換したいのです。

 やり方は何通りかありますが、自分でいくつか試してみたうち、「これがスマートな書き方そう!」と思ったやり方でやってみます!

 sbChangedの次の行に、

et0.setText(String.format("#%02X%02X%02X",sbR.progress,sbG.progress,sbB.progress))

と、入力します。

 

String.format について

 ちょっと解説すると…formatは他のプログラム言語でも「書式」を決めて、そこに値を流し込んで、形式の整った文字列を作る時に出てくる関数です。
 今回の例で言うと、"#%02X%02X%02X"が書式部分になりますが、

  • 最初の#、これはそのまま文字として出てきます。
  • %はそれに続くのが書式設定用の記号である事を意味しています。
  • 0は、桁数が足りない時、0で補完する事を意味します。
    俗に「ゼロパディング」と言われるものです。
    (例えば、3文字分の場所を書式として確保してるところに、数値「1」を流し込んだ場合、3文字分出力するために「001」となって出てきます。)
  • 2は、桁数・文字数。
  • Xは、「16進数に直して大文字で出力」を意味します。
    ちなみにxにすると小文字になります。(#ffffff)みたくなります)
  • で、それを3回繰り返して書いているので、「16進数2文字」×3回、という事になります。
  • ,(カンマ)」で区切って、流し込む値を決めます。
    3回分、書式で必要としているので、3つ書きます。
  • sbR.progress赤いシークバーのツマミ位置を示す0~255のInt型の数値
  • sbG.progress緑のシークバーのツマミ位置を示す0~255のInt型の数値
  • sbB.progress青いシークバーのツマミ位置を示す0~255のInt型の数値

です。 

 

 このカラーピッカーを作ってみるよ!の「その1」で、

> で、「#FF0000」は、任意の文字列ではありません。
> kotlinに限らず、他のプログラム言語(HTMLとかも)で色を表すのに使われる
>文字列で、その中身は「#」のあとの6桁の16進数、これは実際は2桁×3で
>構成され、「1つめの2桁」は赤、「2つめの2桁」は緑、「3つめの2桁」
>は青、の強さを表現しています。 

と書いたものを、こうやって作っています。

 作ったものをet0.setTextでEditTextにぶち込んでいますので、この処理を通った時点で、et0にカラーコードを表示させるミッション達成!!です^^

 

ここでちょっと試しにビルド!

 ここまで作ったところで、ちょっとビルドしてみます。

 エミュレータを対象にしてビルドし、エミュレータ上で起動したアプリのツマミを適当~に触ってみます。

f:id:little_strange:20190921211359p:plain

 こ~んな感じ^^

 まだ色をつける処理は書いていませんが、ツマミを動かす毎に#の文字列が変わっていくのが確認できたらおっけ!!

 

色つけの処理を足します!

 先ほどのet0.setText~の行の次に1行、次のように加えます。

LL0Redraw()

↓こんな風になります。

 

f:id:little_strange:20190921211959p:plain

で、ここから、LL0Redrawって処理を書いていきます。

fun LL0Redraw(){

}

と、手打ちで入力し、{}の間の行に、

LL0.setB

まで打つと、

 

f:id:little_strange:20190921212403p:plain

 

またもやこんな風に候補が出てきますので、setBackgroudColorを選択します。

 するとまたもや、後ろに「()」がついた状態で入力されるので、その()の間に、

Color.rgb

と手打ち入力します。(Cは大文字で入れなきゃな点に注意!)

 すると…

f:id:little_strange:20190921212858p:plain

こんな感じで候補が出てきます。

 同じ「rgb」で、中身がFloatなのとIntなのと2種類出てくるので、一瞬迷いますが、今回はIntのほうを選択します。
(ここにsbR.progressとかを入れるのですが、それがInt型の値なので。)

 またもや、後ろに「()」がついた状態で入力されるので、その()の間に、

sbR.progress,sbG.progress,sbB.progress

を入力します。

 結果、

fun LL0Redraw(){
LL0.setBackgroundColor(Color.rgb(sbR.progress,sbG.progress,sbB.progress))
}

という処理が出来上がります。

 ここで、もう一度、ビルドしてみると、ツマミを動かす事で背景色がぐりぐり変化し、EditTextの文字も変化する事が確認できます!!

 

締めに、もう一か所だけ書き足し!

 これでカラーピッカーとして必要十分な動作をしますが、実際に触ってみると違和感を感じるところがありました。

 最初は真っ白な背景から始まり、ツマミをちょっとだけ触るとすぐに黒っぽい画面になる事と、突然、起動時には無かったEditTextの文字が出現する事、の2点です。

 使用する上で不便はありませんが、簡単に解消できる部分ですので、最後にその問題を解決します。

 

f:id:little_strange:20190921214655p:plain

 onCreateの中、forEachの外に、↑のように

sbChanged()

と追加するだけです。

 これを追加する事により、起動直後、まだツマミを触らないうちから一度、sbChangedの処理、つまり
「ツマミの内容に基づいて、カラーコードをEditTextに表示して、背景色を設定する」
処理が行われるため、「ツマミをちょっと触ったら突然~」っていう違和感は無くなります。

 これで…これで、ついに完成!!!です!

 

完成したカラーピッカーで遊んでみる!!

f:id:little_strange:20190921215244p:plain

 

 3つのツマミを自由に調整して、背景色をぐりぐり変更できます。

 いい感じ!な色が見つかったら、#で始まる16進数文字列をメモっておくと、今後のアプリ開発や、Webデザインにも使えるカラーコードが手に入ります^^

 

 これでカラーピッカーは完成ですが、次は更なる追加機能とかやってみようかな、と思っています!

 よろしければその時はまた、お付き合いいただけると嬉しいです♪

 

 では今回はこのへんで!

 読んでくださった方、ありがとうございました^^