［Android］Screen に Viewが表示されたか判定する

# はじめに

View が Screen に表示されたか判定する処理を作成します。例えば Player が Screen に表示されたら動画の再生を開始したい、TextView が表示されたらテキストカラーを変えたい、といった処理に使えるサンプルを作成してみたいと思います。

![Feb-18-2020 00-47-23.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/88e37226-7559-67af-f6f4-c09b5f4b6f28.gif)

# TL;DR
表示されたか判定する方法はシンプルです、 Screen の Rectと View の Rect を取得して、 Screen の Rectに View のRect が含まれているか判定するだけです。

1． Screen の範囲(Rect)を取得する

```Kotlin:MainActivity.kt
private fun getScreenRect() : Rect {
    val displayMetrics = DisplayMetrics()
    windowManager.defaultDisplay.getMetrics(displayMetrics)
    return Rect(0, 0, displayMetrics.widthPixels,displayMetrics.heightPixels)
}
```

2． View の範囲(Rect)を取得する

```kotlin:MainActivity.kt
private fun View.getViewRect() : Rect {
    val array = IntArray(2)
    this.getLocationOnScreen(array)
    return Rect(this.width, this.height, array[0], array[1])
}
```

3． Screen に View に表示されているか判定する

```kotlin:MainActivity.kt
  scroll_view.viewTreeObserver.addOnScrollChangedListener {
    val screenRect = getScreenRect()

    val oneRect = one.getViewRect()
    if (screenRect.contains(oneRect)) {
      // 含まれているときの処理
    }
    else {
      // 含まれていないときの処理
    }
```

# レイアウトの準備する

判定処理を記述するためにスクロールしなければスクリーンに表示できないようなレイアウトを作成します。ScrollView の中に TextView が3つ含まれているレイアウトを準備しておきましょう。

![Feb-17-2020 21-54-33.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/b058ef57-0988-ff58-4dac-e062b67a3617.gif)

```xml:activity_main.xml
<ScrollView android:id="@+id/scroll_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
        android:id="@+id/linear_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        <TextView
            android:id="@+id/one"
            android:layout_width="match_parent"
            android:layout_height="200dp"
            android:textAlignment="center"
            android:textSize="80sp"
            android:text="One" />
             ︙
    </LinearLayout>
</ScrollView>
```

# Screen の範囲(Rect)を取得する

Screen の位置(サイズ)は `windowsManager.defaultDisplay.getMetrics()`に取得できます。この`getMetrics`で取得した幅と高さを利用して、Screen の範囲を示す `Rect` オブジェクトを作成しましょう。（ここで取得できるサイズは Pixel になります）

![Screen no.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/29c544bb-25f6-58fd-f9a2-e9becc636d8b.png)

```Kotlin:MainActivity.kt
private fun getScreenRect() : Rect {
    val displayMetrics = DisplayMetrics()
    windowManager.defaultDisplay.getMetrics(displayMetrics)
    return Rect(0, 0, displayMetrics.widthPixels,displayMetrics.heightPixels)
}
```


# View の範囲(Rect)を取得する

View の左上の座標を`View.getLocationInWindow`で取得できます。また View の高さと幅は `View.width` と `View.height`で取得できます。これらの値を組み合わせて View が表示されている範囲を示す `Rect`オブジェクトを作成しましょう。（ここも取得できるサイズは Pixel になります）

![Viewno.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/866a8a12-2acd-321d-80b7-495e53fbee2b.png)

```kotlin:MainActivity.kt
private fun View.getViewRect() : Rect {
    val array = IntArray(2)
    this.getLocationOnScreen(array)
    return Rect(this.width, this.height, array[0], array[1])
}
```

# Screen に View に表示されているか判定する

あとは ScrollView のスクロール位置が更新された時、Screen に View が表示されているか判定する処理を記述すれば完成となります。判定処理を記述する箇所は `ScrollView `の `viewTreeObserver` に `addOnScrollChangedListener`になります。そこに Screen の範囲(Rect)に View の範囲(Rect)が含まれるか判定する処理を記述していきます。（含まれるか判定するには Rect の contains が便利です、含まているかそうでないかを Boolean で返すメソッドになります。）

![Feb-18-2020 00-47-23.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/55fbdf4e-a9e6-3adf-6ada-adff2bbc3da4.gif)

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

    val oneTextView = one
    val twoTextView = two
    val threeTextView = three

    scroll_view.viewTreeObserver.addOnScrollChangedListener {
        val screenRect = getScreenRect()

        val oneRect = one.getViewRect()
        val oneColor = if (screenRect.contains(oneRect)) {
            Color.RED
        } else {
            Color.WHITE
        }
        oneTextView.setBackgroundColor(oneColor)

        val twoRect = twoTextView.getViewRect()
        val twoColor = if (screenRect.contains(twoRect)) {
            Color.RED
        } else {
            Color.WHITE
        }
        twoTextView.setBackgroundColor(twoColor)

        val threeRect = threeTextView.getViewRect()
        val threeColor = if (screenRect.contains(threeRect)) {
            Color.RED
        } else {
            Color.WHITE
        }
        threeTextView.setBackgroundColor(threeColor)
    }
}
```

# 参考文献

- [Display | Android Developers](https://developer.android.com/reference/android/view/Display)
- [View | Android Developers](https://developer.android.com/reference/android/view/View)