🚀

[Android] Kotlinで動的にボタンを作成し、パズドラみたいな動きにする

2021/12/26に公開

こんにちは〜
最近インターンでAndroidの開発を始めたので、今日からそこで学んだことをアウトプットしていきます。

やることは、タイトルの通りです。

まずはボタンの作成

今回は、文字列の文字を一文字づつボタンのテキストとして、ボタンを作成したいと思います。
また、作成したボタンはGridLayoutに追加していきます。
ViewBindingを使ってますが、今回はこれが主役ではないので、説明は省きます。

MainActivity.kt
// 文字列を作成し、分解
val word = "Android"
val wordSplit = word.split("").filter { it != "" }

// 文字列の長さ分だけボタンを作成
var i = 0
while (i < wordSplit.size) {
	// GridLayoutを使用するので、rowとcolumnを指定
	val params = GridLayout.LayoutParams().also {
		it.rowSpec = GridLayout.spec(0)
		it.columnSpec = GridLayout.spec(i)
	}
	val button = Button(this).also {
		it.text = wordSplit[i]
		it.layoutParams = params
	}
	binding.gridLayout.addView(button)
	i++
}

ボタンにOnDragListenerとOnTouchListenerを追加

MainActivity.kt
var i = 0
while (i < binding.gridLayout.childCount) {
	val v = binding.gridLayout.getChildAt(i)
	v.setOnTouchListener()
	v.setOnDragListener()
	i++
}

binding.gridLayout.childCountで、GridLayoutにあるViewの数を表しています。今回の場合だと、挿入されたボタンの数ですね。
そこから、binding.gridLayout.getChildAt(i)で一つ一つのViewを取り出して、それをvに代入してリスナーを追加しています。

onTouchListenerとonDragListenerの処理を記述

いよいよ、それぞれのボタンを動かすための処理をやっていきます。
まず、一番はじめにやるべきことは、onTouchListeneronDragListenerを使用する際に、activityに対して下記の記述をする必要があります。

MainActivity.kt
class MainActivity : View.OnTouchListener, View.OnDragListener {

}

この記述がないと、onDragonTouchをオーバーライドできないのでこれは必須です。
では、onDragとonTouchをオーバーライドしていきましょう。

MainActivity.kt
//ドラッグしたボタンを保存しておく変数
private lateinit var dragView: View

// eventは、タッチイベントを保持
override fun onTouch(v: View, event: MotionEvent): Boolean {
	if (event.action == MotionEvent.ACTION_DOWN) {
		dragView = v
		v.startDragAndDrop(null, View.DragShadowBuilder(v), v, 0)
		v.alpha = 0F
	}
	return true
}

ドラッグを開始させるために、v.startDragAndDropメソッドを使用しています。
v.alpha = 0Fでは、ドラッグされたボタンを透明にしています。

では、次はonDragの実装をしていきます。

MainActivity.kt
override fun onDrag(v: View, event: DragEvent): Boolean {
	when (event.action) {
		DragEvent.ACTION_DRAG_ENDED -> {
			mainExecutor.execute { dragView.alpha = 1F }
		}
		DragEvent.ACTION_DRAG_LOCATION -> {
			mainExecutor.execute { swap(v, dragView) }
		}
	}
	return true
}

// Viewの位置を入れ替えるメソッド
private fun swap(v1: View, v2: View) {
	if (v1 == v2) return
	val p1 = v1.layoutParams as GridLayout.LayoutParams
	val p2 = v2.layoutParams as GridLayout.LayoutParams
	binding.gridLayout.removeView(v1)
	binding.gridLayout.removeView(v2)
	binding.gridLayout.addView(v1, p2)
	binding.gridLayout.addView(v2, p1)
}

まず最初に使用している、ACTION_DRAG_ENDEDは、ドラッグが終了したイベントです。
先ほど、ドラッグを開始した時にボタンを元のボタンを透明にしたので、ドラッグが終了したタイミングで元に戻しています。
ACTION_DRAG_LOCATIONは、ドラッグ中に他のViewの上に乗った際のイベントです。
他にも全部で6つのイベントがあるので、見たい方はこちらからどうぞ

最後に実際にボタンを入れ替えているのが、swapメソッドになります。
最初の、if (v1 == v2) returnでViewが動いていなければ動かす必要がないので、それを確かめています。

最後に

初めての記事だったので、まじ何言ってるか意味わからないと思いますが、これから毎日投稿してわかりやすいものが書けるように頑張ります🙇‍♂️
화이팅!!🔥

Discussion