Little Strange Software

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

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

 

 

 どうも!LSSです。

 kotlinでAndroidスマホ用カラーピッカー作成、続きやっていきまーす!

 

仕様変更

 これまでのプログラムの構成は、シークバーの値を基準としてEditTextや背景色を変更する、というものでしたが、これから機能を追加していくにあたり、「基準」となるものは別に用意したほうが良いと思いましたので、そのへんゴリゴリ書き換えていきます。

 まずは、使用する変数を用意します。

f:id:little_strange:20190923144843p:plain

↑この、class MainActivity~と、override fun onCreate~の間に、変数の宣言を書いていきます。

lateinit var sb : List<SeekBar>
var cl= mutableListOf<Int>(0,0,0)
var i :Int = 0

の、3行を追記します。

 そして、

f:id:little_strange:20190923145711p:plain

onCreate内にある

val sb=listOf(sbR,sbG,sbB)

の「val」の3文字だけを削ります。

 いったん、ここで試しにビルドしてみて、今まで通り動作する事を確認してから、次に進みます。

 なお、「cl」がこれからsbR.progressにとってかわって基準となる配列変数です。
 cl[0]、cl[1]、cl[2]としてアクセス可能で、中身はInt型、初期値はどれも0、としています。
 アプリ実行中に中の数値がコロコロ変わるので、mutableListOf、で宣言しています。

※全然、余談なんですが、mutableじゃない時はlistOf、murableの時はmutableListOf、とLの大文字小文字が違うのは要注意かなと思いました。
 Intが他の言語だとintだったのが、kotlinではIntって書く事になってるのも要注意かなと。

 

次に、funを置き換えていきます。

 コードの下のほうの、

f:id:little_strange:20190923151755p:plain

とある部分、この辺を書き換えていきます。

 まず、

fun LL0Redraw(){

の、LL0を削って、

fun Redraw(){

に変更します。
 次に、その上にある、

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

の1行を切り取り、

LL0.setBackgroundColor(Color.rgb(sbR.progress,sbG.progress,sbB.progress))

の直前の行に貼り付けます。すると、

 

fun sbChanged(){
LL0Redraw()
}

fun Redraw(){
et0.setText(String.format("#%02X%02X%02X",sbR.progress,sbG.progress,sbB.progress))
LL0.setBackgroundColor(Color.rgb(sbR.progress,sbG.progress,sbB.progress))
}

というコードになります。

 そして、

fun sbChanged(){
LL0Redraw()
}

の3行はバッサリ削除します。

 次に、Redraw()の中の2行、どちらにも

sbR.progress,sbG.progress,sbB.progress

という部分が含まれていますが、これを2行とも、

cl[0],cl[1],cl[2]

に書き換えます。

 結果、

fun Redraw(){
et0.setText(String.format("#%02X%02X%02X",cl[0],cl[1],cl[2]))
LL0.setBackgroundColor(Color.rgb(cl[0],cl[1],cl[2]))
}

というコードになります。

 これが画面再表示用の関数となり、以降、Redraw()を呼び出す

  • EditTextの内容を配列変数clの内容に準じて書き換える
  • LL0の背景色を配列変数clの内容に準じて書き換える

を、やってくれる事になります。

 ここに、

  • シークバーのツマミ位置も、配列変数clの内容に準じて書き換える

という処理を追加したいと思います

 
fun Redraw(){
と、

 et0.setText(String.format("#%02X%02X%02X",cl[0],cl[1],cl[2]))

の間に1行、次のコードを追加します。

for (i in 0..2){sb[i].progress = cl[i]}

 これもループ処理で、iが0の場合、1の場合、2の場合、の

sb[i].progress = cl[i]

を全部この1行でやってくれます。

 progressに数値を設定する事で、ツマミ位置が配列変数clの内容に準じたものになります。

 この時点で、画面は次のようになります。

 

f:id:little_strange:20190923153817p:plain

fun sbChanged()をバッサリ削除した事で、2つあった呼び出し部分がどちらも赤文字になっています。とりあえず、これを2つとも

Redraw()

に書き換えます。

 

 

シークバーのイベントリスナーの変更

  次に、シークバーのイベントリスナーの内容を変えていきます。

(変更前)呼び出し先でシークバーの位置に応じて処理してくれていたので、呼び出すだけ。

 ↓

(変更後)ユーザーによって変更されたシークバーのツマミ位置を、配列変数clに代入し、配列変数clの内容に合わせて画面を書き換えてくれるRedraw()を呼び出す。 

 

というものに変更します。

  まず、イベントリスナーの1行目。

 sb.forEach(){

 の部分を、

 for (i in 0..2){

 に書き換えます。すると、

f:id:little_strange:20190923155022p:plain

forEachじゃなくなってしまったため、itが赤文字になります。このitを、 

 sb[i]

 に書き換えます。

 また、その少し下、 

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

overrideの行の次にスキマ行を開けて、そこに、

if(p2) {

と入力します。

 さらに、

Redraw()

の次にもスキマ行を開けて、そこに

}

だけ入れておきます。

f:id:little_strange:20190923160050p:plain

 画面はこんな感じ。

 で、さらにこのRedraw()の上に1行、スキマ行をあけて、

cl[i] = sb[i].progress

と、入力します。

 これで、
「ユーザーがシークバーを触って変更した時、変更されたツマミの位置を対応する配列変数clに代入して、画面を描画しなおすRedraw()を呼び出す」

という内容に書き換えできました。

override fun onProgressChangedにおいて、p2という変数は、そのツマミの変化がユーザーによるものだったら真、そうでなければ偽となります。
 if(p2){処理}という書き方で、ユーザーによる変更の場合だけ「処理」の内容を実行する、という事になります。

 

このへんでまた試しにビルド!

 内部の処理は色々と書き換えましたが、アプリを実行する上では目に見える変化はまだありません。
 正常に動作するかを試してみてから次に進みますが、このへんでちょっとコード内の余分な空行を取り除いてから、現時点でのコード全文を載せておきますね。

 

package jp.littlestrangesoftware.a20190919colorselect

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

class MainActivity : AppCompatActivity() {

lateinit var sb : List<SeekBar>
var cl= mutableListOf<Int>(0,0,0)
var i :Int = 0

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
sb=listOf(sbR,sbG,sbB)
for (i in 0..2){
sb[i].setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{
override fun onProgressChanged(p0: SeekBar?, p1: Int, p2: Boolean) {
if(p2) {
cl[i] = sb[i].progress
Redraw()

}
}
override fun onStartTrackingTouch(p0: SeekBar?) {}
override fun onStopTrackingTouch(p0: SeekBar?) {}
})
}
Redraw()
}

fun Redraw(){
for (i in 0..2){sb[i].progress = cl[i]}
et0.setText(String.format("#%02X%02X%02X",cl[0],cl[1],cl[2]))
LL0.setBackgroundColor(Color.rgb(cl[0],cl[1],cl[2]))
}
}

 

新機能!「暗くする」「明るくする」ボタンを追加するよ

  ここで久々(?)に、xmlのデザイン編集画面を開きます。

  で、

f:id:little_strange:20190923164622p:plain

Paletteの「Layouts」を選び、右に出てくる「LinearLayout(horizontal)」を選び、Component Treeの「et0」の下にドラッグアンドドロップで並べます。
 並べたら、次は画面右のほうにある、Attributesの中から、layout_heightの項目だけを▼をクリックして「wrap_content」に変更しておきます。 

 f:id:little_strange:20190923165326p:plain

 

  次に、今、設置したLinearLayout(horizontal)の配下になるように、ボタンを2つ、ドラッグアンドドロップで設置します。

f:id:little_strange:20190923165704p:plain

↑こんな風に、ボタンが2つ、画面サンプルのほうで横並びになればOKです。

 

 次に、2つあるうちの最初のほう(Content Treeでは上のほう)のボタンをクリックして選択状態にして、画面右のAttributesで、以下の3項目を書き換えます。

「id」…「btD」と入力。

「text」…「暗くする」と入力。

「onClick」…「btDclk」と入力。

 

 次に、同様にもうひとつのボタンも、選択してAttributesを以下のように書き換えます。

「id」…「btL」と入力。

「text」…「明るくする」と入力。

「onClick」…「btLclk」と入力。

 

 以上で xmlの変更は終わりです。

 

kotlinのコードにボタンを押した時の処理を書いていくよ!

 再び、kotlinコードのほう(MainActivity.kt)のほうに画面を切り替えて、コードの一番下から2つめの「}」の後ろにカーソルを合わせ、

f:id:little_strange:20190923171108p:plain

Enterキーを数回叩いて、最後の「}」との間にスキマ行をあけ、そこに

fun btDclk(v:V

まで入力すると、

f:id:little_strange:20190923171807p:plain

こんな候補が出てくるので、「View (android.view)」を選択します。

※裏で密かにimport文が追加されます。

そして、続けて「){」と打ち、Enterキーを押すと「}」が自動補完され

fun btDclk(v:View){

}

となります。

 次に、fun btDclkの次の行に、

for (i in 0..2){cl[i] = (cl[i] * 0.9).toInt() }
Redraw()

という2行を入力します。

 結果、

fun btDclk(v:View){
for (i in 0..2){cl[i] = (cl[i] * 0.9).toInt() }
Redraw()
}

こうなります。これで「暗くする」ボタンのほうの機能は完成です。

 

 ちょっと説明すると、cl[0]cl[1]cl[2]は、それぞれの色の強さを管理する変数として使用しており、その3つを3つとも、0.9を掛ける事で
「3色の比率を保ったまま、弱くする(黒に近づける)」
という演算を行っています。
 配列変数clの内容を変更したあとで、画面再描画を任せているRedraw()を呼び出す事で、シークバー・EditText・背景色に変更が反映されます。

 

 次は、「明るくする」ボタンの処理を追加します。

 内容が似ているので、まず作ったばかりの

fun btDclk(v:View){
for (i in 0..2){cl[i] = (cl[i] * 0.9).toInt() }
Redraw()
}

 をコピーして、そのまま後ろに貼り付けます。

f:id:little_strange:20190923173358p:plain

 こんな感じ。同じ名前のfunが2つあってはおかしいので、赤い波線が出ています。

 後のほうの

fun btDclk(v:View){

の「D」を「L」に変えて、btLclkにします。

 そして、

for (i in 0..2){cl[i] = (cl[i] * 0.9).toInt() }

の行も部分的に書き換えて、

for (i in 0..2){cl[i] = ((cl[i] * 9 + 255)/10).toInt() }

とします。「(」の数が変わっていたりする点、要注意です。

 これで、「明るくする」ボタンの機能も完成!です^^

 

 これもちょっと説明すると、「暗くする」ボタンと同様、cl[0]、cl[1]、cl[2]の3つに演算処理をくわえていますが、その演算内容は「255に近づける」というものです。
 具体的には
cl[i]の内容をつと、「255」をつ足して、それを10で割った平均をcl[i]に格納する
という計算を行っています。
 昔、趣味で簡単なゲームプログラムとか書いてた頃に、自キャラを追尾してくるキャラの動きとかをこんな処理で書いてました(懐古厨)

 

 厳密に、比率を保ってるか?といったらあまり厳密ではありませんが、だいたいこんなものでいいかな、と思っています。

 ここでビルドして、エミュレータか実機上で、適当にツマミを動かして色を作ってから、「暗くする」「明るくする」ボタンで遊べるようになりました!^^

 

  さて、ひと段落ついたところで、今回はこのへんで。

 次回もまた、よろしくお願いしまーす!!