(AndroidStudio4.2)
kotlinで、タッチしたところに
このようにボールがくっついてくる、
というアプリを作ってみたいと思います。
前回、onTouchEventで、画面をタッチしたときの
プログラミングしました。
今回もその延長です。
今回は、onTouchEventを使って
押した位置、座標を取得して、
それから、このように、画面の上を自由に動かす、
というのをやっていきましょう。
動画
コード
▼xml
使わない
▼MainActivity.kt
package com.example.balltouchapp import android.content.Context import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.MotionEvent import android.view.View class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) //setContentView(R.layout.activity_main) //2)新しいクラスを表示させる val ballView = BallView(this) setContentView(ballView) } //1)Viewを継承したクラス class BallView(context: Context?) : View(context) { private var paint:Paint = Paint() private var circleX : Float = 200F private var circleY : Float = 200F //3)onDrawで描画の準備 override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) canvas?.drawColor(Color.RED) //カンバス(背景)色 //4)ペイントする色の指定と、丸い図形 paint.color = Color.YELLOW canvas?.drawCircle(circleX,circleY ,50F,paint) } //5)画面タッチ override fun onTouchEvent(event: MotionEvent?): Boolean { //タッチポジション circleX = event!!.x circleY = event.y invalidate() //return super.onTouchEvent(event) return true } } }
テキスト
【ざっくりと概要の説明】
作業に行く前に、今回の作業内容というか
仕組みについてざっくりと説明しておきます。
今までは、プロジェクトを起動すると
xmlとkt,2つのファイルが出来ていました。
xmlでレイアウト、
それからActivity.ktでプログラミング操作する、という仕組みでした。
でこっちで書いたものが、こっちの画面で表示されると、いうことでした。
で、結論から言うと、今回はxmlは使いません。
じゃあどうするかという話なんですが。
kotlinの中にですね、
もう1つclassを作って
onDrawという描画メソッドを用意して、
そこにカンバスを敷きます。
そうすると、このように文字通りカンバスができるので
そいつを表示させると、いうことになります。
このカンバスを画面に表示させることで、
今までのような、左右中央とか、余白がいくら、とかよりももっと
自由度の高い、動きや画面表示が可能になる、ということです。
onDraw。
「Draw」ってついているので、字面でなんとなく見てわかるとおり
お絵描きアプリなんかを作るときに使うメソッドです。
いずれお絵描きアプリも解説したいなとは思っていますが
とりあえず今日は、その予備知識と、
前回の発展形ということで、
onDrawというメソッドでカンバスをしいて、
そこに図形を表示させたり
タッチしたところの座標を取得していく、というところをやっていきましょう。
ちなみに、もうちょっと複雑になってくると、
このクラス独立させてこっちにもう1個作った方が
何かと都合がよかったりします。
実は以前javaの解説でonDrawを使ったお絵描きアプリを解説したんですが
その時はonDraw自体を別の独立したjavaクラスを作って、
それをxmlに重ねる、というのをやったんですけども、
もし興味のある方は概要欄に貼っておきますのでご覧ください。
今回はシンプルにKTの中にまとめて書いちゃいます。
そんなところですかね。
あ、それから最後に、
このクラスについてもう少し補足しておきますね。
このクラスは、詳しく、というか厳密に言うと「Viewを継承したclass」になります。
継承についてはまたどこかで詳しく説明しようと思いますけど、
onDrawも、textViewとかImageViewとかと同じく
Viewグループに属します。
なので、Viewをお父さん・お母さんに持つ子供のクラス、というのを
プログラミングでは「継承」といいます。
お父さん・お母さんの遺伝子を引き継ぐことで
その一族のメソッド、今回でいうとonDrawが使えるようになります。
逆にいうとonDrawを使いたいから、Viewを継承した、という言い方もできるわけですが。
まあそんな感じで、ふんわりとイメージがつかめたところで、
実際作業していきましょう。
***********************************
【1.新しいクラスを作る】
それでは始めていきましょう。
AndroidStudioを起動して
名前は適当にBallTouchApp
とかにしておきます。
言語はコトリンです。
はい、プロジェクトが起動できたようです。
xmlとktとありますが、今回は先ほどの説明のとおりxmlは使いません。
ということで、まずはこの、新しいクラス、
Viewを継承したクラスを1つ作りましょう。
クラスを作るにはclassと書いて、名前を付ければOKです。
で、継承する場合はコロンに続けて、親の名前を書いてあげます。
今回はViewを継承するので
: View
と書いてあげます。
で、コンストラクタ引数を渡してあげる必要があるんですが
BallViewのあとに()を書いて
Context
Viewの中にも
context
と書いてあげてください。
そうすると赤い波線が消えたかと思います。
Contextっていうのがこれまた解説がややこしいんですけど
Viewクラスを継承するサブクラス、つまり子供のクラスは
引数にContextを渡すことで、親のメソッドを呼び出すことができるようになっています。
: View と書くことで、親子関係がつながった。
でContextと書いてあげることで、親の道具、メソッドも使えるようになった、
みたいなイメージです。
まあぶっちゃけこれはAndroidの仕様なので、
「こう書いたらとりあえず動く」というくらいの理解でOKです。
//1)Viewを継承したクラス
class BallView(context: Context) : View(context){
}
***********************************
【2.新しいクラスが表示されるように変更】
では、今作ったクラスを表示させるように
矢印をかえてあげましょう。
今はsetContentViewがレイアウトのactivity_mainになっていますので
ここをコメントで消して、かわりに今作ったクラスに変えてあげましょう。
//setContentView(R.layout.activity_main)
//2)新しいクラスを表示させる
val ballView = BallView(this)
setContentView(ballView)
これで、表示がlayoutのactivity_mainではなくなって
今用意した、BallViewが表示されるはずです。
表示されるというか、何もないので真っ白な画面が表示されるはずです。
このHello World!ではなくって、ただの真っ白画面が表示されればOKです。
エミュレータを起動して確認・・・
の前に、そうかvalについても説明しておきましょう。
宣言のところで、第1回の時だったかな
valとvarがあって、て話をしたんですよね。
valはvalueの略で、再代入ができない、つまり途中で変更ができないもの
varはvaluableとかvaryetyの略で、「色々な」という意味があるので途中から変更できるもの
という話をしたと思います。
基本的には変更できないvalを使って
必要なところだけvarを使うことで保守性の高いプログラミングができるんですが
あるときはこっち、別の時はこっちってやると、習いはじめの時は混乱するかなと思ったので
varで統一しようかなって思ったんですが。
いざ、使い分けるアプリになるとですね
両者を説明するのに格好のテーマだと気づきまして(遅っ・・・。)
なので、今後は、やっぱり基本に立ち返って
基本的にはvalを使って
必要なところだけvarで宣言していこうと思います。
ということで、これで今、
setContentViewがレイアウトのactivity_mainではなくって
こちらのballViewが表示されるようになったはずなので
一旦エミュレータを起動して確認してみましょう。
こうですね。
Hello World!ではなくって、ただの真っ白画面が表示いるのがわかります。
それではここにonDrawメソッドで
カンバスを敷いて、色を付けたり、図形を描画したりしてみましょう。
***********************************
【3.onDraw】
それでは、今作ったクラスの中に
onDrawメソッドを書いてあげて、色々な描画ができるようにしてあげましょう。
onDって入れるとonDrawが出てくるので選択。
そうすると、ドーンとひな形が出てきます。
これで色々できるようになりました。
それではまずは、挨拶がてら、
カンバスに色をぬってあげましょう。
今は真っ白なので、色を塗ると、画面がわかりやすくなりますね。
canvas?.drawColor(Color.RED) //カンバス(背景)色
ドットのところに波線が出ているのでマウスを合わせて見てください。
ワーニングが出ているのでクリック。
ここは前回説明しましたが、nullチェックで
nullを許容するのかしないのか、ということです。
ここは許容してかまわないので「?」を選択でいいでしょう。
エミュレータを起動して確認してみましょう。
はい、画面が赤色になったので、ここにカンバスが表示されているのが少し実感できたかと思います。
それでは次に、カンバスにボール、丸い図形を1つ表示してみましょう。
***********************************
【4.ボール(丸い図形)を表示】
図形を描画するにはpaintを使います。
なのでクラスの下にまずはpaintを用意してあげます。
private var paint :Paint = Paint()
型はPaintで
Paint()というグラフィックを代入してあげましょう。
ここでvarを使います。
線とか図形って、途中で色を変えたり、大きさを変えたり
線だったら太さを変えたり、変更しますよね。
だからここではvalではなくてvarを使った、ということです。
こういう説明だと、少し具体的でわかりやすいですかね。
どうですかね。
で、paintが用意できたので
早速色の指定をしてあげましょう。
//4)ペイントする色の指定と、丸い図形
paint.color = Color.YELLOW
canvas?.drawCircle(500f,500f,50f,paint)
そうすると、X500、Y500、半径50の
円が1つできました。
あたりまえですが
canvas?.drawCircle(900f,900f,300f,paint)
とすると、もっと右の、もっと下の、もっと大きい円が描画できているかと思います。
ここはいったんもとに戻しておきます。
で、このままだと、
位置が固定されているので
タッチした座標に毎回変更する必要があります。
なので、X軸とY軸は変数に置き換えておきましょう。
private var circleX:Float = 200F
private var circleY:Float = 200F
canvas?.drawCircle(circleX,circleY,50f,paint)
これで、今は
X200、Y200、半径50の円が表示されるはずです。
エミュレータを起動して確認してみましょう。
こうですね。
左上の方に、小さい丸が1つ表示されたかと思います。
あとは、タッチした座標を取得して、
そこにcircleXとcircleYを再代入してあげれば
drawCircleの位置が変わってボールが動く、ということになります。
画面タッチは、前回やったオンタッチイベントですね。
それでは最後、タッチした座標を取得して完成です。
***********************************
【5.タッチした座標を取得】
それでは最後に、画面をタッチした座標を取得しましょう。
画面タッチは前回やりましたがオンタッチイベントですね。
onDraw{}の下に
同じくオンタッチイベントを作ります。
circleXに、イベントのx座標を、という場合はこのように書きます。
ということで
circleYにイベントのy座標を、という場合はこのように書きます。
今回nullを許容すると上手くいかないので
nullを否定するという意味で「!!」の方にしてあげます。
で、このままでもいいんですが
実は、1個だけ修正箇所があります。
とりあえずエミュレータを起動して確認してみましょう。
はい、これでタッチしたところにボールが移動できるようになったかと思います。
ただし、ずーっと押し続けた場合、
ついてきてくれません。
これ、ずーっとついてきて欲しいですよね。
returnをturueにしてあげてください。
//return super.onTouchEvent(event)
return true
ここ、ちょっとわかりにくいんですが
return super.onTouchEvent(event)
は、return falseを意味します。
で、falseということは、まだ処理が終わってませんよということで
次の処理にいってしまいます。
ただ、次の処理はないので、先ほどのように、おかしなことになるんです。
※1回目の処理は通るので、最初の「押した時」だけは反応する。(押し続けた場合に更新されない、という意味)
returnでturueを返してあげると
作業が完了しました、ということでreturn、つまり、報告が返されます。
で、押し続けているので、この中の処理が更新されつづけると、いうことです。
**********************************
以上、タッチ操作で座標を取得するプログラミング解説でした。
この仕組みを使うと、ペイントアプリなんかもできるんで、
またどこかで紹介したいと思います。