はじめに
Android では公式にあるとおりカスタムビューを作成する方法が何パターンかあります。
今回はフルカスタムコンポーネントを作成してみたので作成方法をまとめたいと思います。
名称 | 説明 |
---|---|
フルカスタムコンポーネント |
View を継承していちから新たなコンポーネントを作成するパターン |
複合コンポーネント | 既存のコンポーネントを複数組み合わせて新たなコンポーネントを作成するパターン |
今回はnumber
とdisplayMode
を設定すると、モードごとに数字を表示する次のようなビューを作ろうと思います。
1. 独自のView
クラスを継承する
フルカスタムコンポーネントを作成するために、まずView
を継承したクラスを実装します。
class NumberView(context : Context, attributeSet: AttributeSet) : View(context, attributeSet)
このようにView
を継承したクラスを定義するとレイアウトでCustomView
を記述できるようになります。
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<kaleidot725.customviewsample.NumberView
android:id="@+id/number_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:number="1234567890"
app:displayMode="japanese"/>
</FrameLayout>
2. 独自のAttributes
を宣言する
フルカスタムコンポーネントで独自のAttriubtes
を利用するには、
values
にattrs.xml
を作成し、独自のAttributes
を宣言する必要があります。
次のようにattrs.xml
を記述すると、レイアウトでAttributes
で利用できるようになります。
<resources>
<declare-styleable name="NumberView">
<attr name="number" format="integer"/>
<attr name="displayMode" format="enum">
<enum name="number" value="0"/>
<enum name="english" value="1"/>
<enum name="japanese" value="2"/>
</attr>
</declare-styleable>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<kaleidot725.customviewsample.NumberView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:number="1"
app:displayMode="number"/>
</androidx.constraintlayout.widget.ConstraintLayout>
3. 独自のAttributes
を取得する
obtainStyledAttributes
を利用すれば、独自に宣言したattributes
を取得できます。
attributes
を宣言するとR.styleable.<CustomView名称>_<attributes名称>
を指定できるようになっているので、
次のようにそのID
をobtainStyledAttributes
に指定してattributes
を取得します。
private var number : Int = 0
private var displayMode : Int = 0
init {
context.theme.obtainStyledAttributes(attributeSet, R.styleable.NumberView, 0, 0).apply {
try {
number = getInteger(R.styleable.NumberView_number, 0)
displayMode = getInteger(R.styleable.NumberView_displayMode, 0)
} finally {
recycle()
}
}
}
4. onDraw
に描画処理を記述する
View.onDraw
をオーバーライドすることでView
の描画内容を変更できます。
View
の描画にはCanvas
を利用します、Canvas
でどのような描画ができるかは次に詳しくまとまっています。
今回はCanvas
のdrawText
で文字の描画、drawRect
で背景の描画を実装してみます。
また文字の描画ではnumber
とdisplayMode
に応じて描画する文字列を変換するようにしてみます。
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val backgroundPaint = Paint().apply {
setColor(Color.GREEN)
}
canvas?.drawRect(0f, 0f, this.width.toFloat(), this.height.toFloat(), backgroundPaint)
val textPaint = Paint().apply {
setColor(Color.BLACK)
textSize = 50f
}
val text = getDisplayNumber(number, displayMode)
val textWidth = getTextWidth(text, textPaint)
canvas?.drawText(text, (this.width / 2f) - (textWidth / 2), this.height / 2f, textPaint)
}
private fun getTextWidth(text : String, paint : Paint) : Float {
val bounds = Rect()
paint.getTextBounds(text, 0, text.length, bounds)
return bounds.width().toFloat() + bounds.left.toFloat()
}
private fun getDisplayNumber(number : Int, displayMode : Int) : String {
fun String.replace(vararg pairs: Pair<String, String>): String =
pairs.fold(this) { acc, (old, new) -> acc.replace(old, new, ignoreCase = true) }
return when(displayMode) {
0 -> { number.toString() }
1 -> { number.toString().replace(
"0" to "〇", "1" to "一", "2" to "二", "3" to "三", "4" to "四",
"5" to "五", "6" to "六", "7" to "七", "8" to "八", "9" to "九"
)
}
else -> { "error" }
}
}
すると指定したNumber
がJapanse
に変換されて表示されるようになります。
5. 独自のAttributes
を変更できるようにする
最後に独自のAttributes
を変更できるようにsetNumber
とsetDisplayMode
メソッドを用意します。
Attributes
の変更を反映させるために変更後はinvalidate
とrequestLayout
を呼ぶ必要があります。
private var number : Int = 0
private var displayMode : Int = 0
fun setNumber(number : Int) {
this.number = number
this.invalidate()
this.requestLayout()
}
fun setDisplayMode(displayMode: Int) {
this.displayMode = displayMode
this.invalidate()
this.requestLayout()
}
試しにNumberView
を取得して、setNumber
とsetDisplayMode
を呼び出してみます。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val numberView : NumberView = findViewById(R.id.number_view)
numberView.setNumber(999999)
numberView.setDisplayMode(0)
}
}
こんな感じで設定変更が適用され表示も変化するようになります。
おわりに
今回作成したサンプルは次にまとめています。
なので必要に応じて閲覧していただければと思います。