Android
Kotlin
AndroidStudio
android開発

カスタムビュー(custom view)の実装

More than 1 year has passed since last update.

カスタムビュー

子Viewを複数箇所で使用する場合や、
処理を独立させたい場合にカスタムビューを使用する。

更に子Viewを表示している間は後ろのViewにタッチを伝搬させないようにする。

実装

ActivityにaddViewする先の親ViewであるFrameLayoutを保持する

Activity

activity_main.xml
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- 子Viewが表示中、このScrollViewがスクロールされなければOK -->
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/gradation">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="1000dp"
            android:orientation="vertical">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="500dp"
                android:text="activity_main top"
                android:textSize="30sp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="activity_main bottom"
                android:textSize="30sp" />
        </LinearLayout>

    </ScrollView>

    <!-- 親View -->
    <FrameLayout
        android:id="@+id/customViewContainer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</FrameLayout>
MainActivity.kt
class MainActivity : AppCompatActivity() {

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

        // 親View
        val container = findViewById<FrameLayout>(R.id.customViewContainer)

        // 子View
        val firstCustomView = FirstCustomView(this, {
            container.removeAllViews()    // 親Viewに設定した子Viewを削除
        })

        // 親Viewに子viewを設定
        container.addView(firstCustomView)
    }
}

カスタムビュー(子View)

widget_custom_view.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#66000000">

    <FrameLayout
        android:layout_gravity="center"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:background="@android:color/black">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="custom view first"
            android:textColor="@android:color/white"
            android:textSize="30sp" />

        <Button
            android:id="@+id/customFirstButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|center"
            android:layout_marginBottom="30dp"
            android:text="close"
            android:textColor="@android:color/black" />
    </FrameLayout>

</FrameLayout>
FirstCustomView.kt
class FirstCustomView(context: Context): FrameLayout(context) {

    var onClick: (()->Unit)? = null

    constructor(context: Context, onClick: ()->Unit): this(context){
        this.onClick = onClick
    }

    init{
        // Viewを設定
        inflate(context, R.layout.widget_custom_first, this)

        findViewById<Button>(R.id.customFirstButton)?.setOnClickListener {
            onClick?.invoke()
        }

        // 背後のViewへタッチを伝搬させない
        // これがないと後ろのスクロールViewがスクロールできてしまう
        setOnTouchListener { _, _ -> true }
    }
}

結果

スクリーンショット

子view表示時 - スクロール不可

screen.png

CLOSEタップして子View削除後

screen.png

スクロール可能に

screen.png

説明

親ViewのContainerにFrameLayoutを継承した子ViewのFirstCustomViewを
addView()にて設定

        // 親Viewに子viewを設定
        container.addView(firstCustomView)

子Viewの"CLOSE"Buttonをタップした際にActivityへタップされたことを通知
親ViewにてremoveAllView()にて子Viewを削除。

        // 子View
        val firstCustomView = FirstCustomView(this, {
            container.removeAllViews()    // 親Viewに設定した子Viewを削除
        })

使い勝手が非常に良いので、中規模以上のアプリでは使用すると良い感じです。