'How to detect if any part of one view is overlapping another view in android

What I am trying to do: I am trying to determine .... If any part of one view crosses other view

What I have done:

Usage: isDragOverlap = textView.isOverlap(buttonView)

Code:

private fun View.isOverlap(other: View, deltaX: Int = 0, deltaY: Int = 0): Boolean {
        val thisXY  = IntArray(2).apply { getLocationOnScreen(this) }
        val otherXY = IntArray(2).apply { other.getLocationOnScreen(this)
            this[0] += deltaX
            this[1] += deltaY
        }
        return thisXY.let {
                                Rect(it[0], it[1], it[0] + width, it[1] + height)
                           }
                .intersect(otherXY.let {
                                 Rect(it[0], it[1], it[0] + other.width, it[1] + other.height)
                            }
               )
    }

Above code works as: Here below intersection is detected

enter image description here


What is not working: Here below intersection is not detected

enter image description here



Solution 1:[1]

I have written the following function and it seems to work. You can try it and let me know if You have found any problems.

private fun View.isOverlap(other: View, deltaX: Int = 0, deltaY: Int = 0): Boolean
{
    val rectThis = Rect()
    this.getHitRect(rectThis)

    rectThis.offset(deltaX, deltaY) //addind delta to every side of Rect

    val rectOther = Rect()
    other.getHitRect(rectOther)

    return rectThis.intersect(rectOther)
}

Main Activity XML

<?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:id="@+id/conLay"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/v1"
        android:layout_width="149dp"
        android:layout_height="114dp"
        android:layout_marginStart="164dp"
        android:layout_marginTop="64dp"
        android:text="v1"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/v2"
        android:layout_width="126dp"
        android:layout_height="114dp"
        android:layout_marginTop="160dp"
        android:layout_marginEnd="232dp"
        android:text="v2"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.kt

import android.graphics.Rect
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity

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

        val v1 = findViewById<Button>(R.id.v1)
        val v2 = findViewById<Button>(R.id.v2)

        v1.setOnClickListener {
            Log.d("One - two", v1.isOverlap(v2, 2, 1).toString())
            Log.d("Two - one", v2.isOverlap(v1, 1, 2).toString())
        }
    }
}

private fun View.isOverlap(other: View, deltaX: Int = 0, deltaY: Int = 0): Boolean {
    val rectThis = Rect()
    this.getHitRect(rectThis)
    Log.d("rectThis before change", rectThis.toShortString())
    rectThis.offset(deltaX, deltaY)
    Log.d("rectThis after change", rectThis.toShortString())

    val rectOther = Rect()
    other.getHitRect(rectOther)
    Log.d("rectOther", rectOther.toShortString())

    return rectThis.intersect(rectOther)
}

Solution 2:[2]

This may help you.

fun View.isOverlappedByOtherView(): Boolean {
    return isOverlappedByOtherView(Rect().apply { getGlobalVisibleRect(this) })
}

private fun View.isOverlappedByOtherView(viewRect: Rect): Boolean {
    val tempParent = parent
    if (tempParent is ViewGroup) {
        val mIndex = tempParent.indexOfChild(this)
        for (childIndex in (mIndex + 1) until tempParent.childCount) {
            val siblingView = tempParent.getChildAt(childIndex)
            val intersecting =
                siblingView != null && siblingView.isShown && intersectedRect(siblingView).let { pair: Pair<Boolean, Rect> ->
                    pair.first && pair.second.intersects(
                        viewRect.left,
                        viewRect.top,
                        viewRect.right,
                        viewRect.bottom
                    )
                }
            val isParentOverlapped = tempParent.isOverlappedByOtherView(viewRect)
            if (intersecting || isParentOverlapped) {
                return true
            }
        }
    }
    return false
}

fun View.intersectedRect(otherView: View): Pair<Boolean, Rect> {
    val viewRect = Rect().apply { getGlobalVisibleRect(this) }
    val otherViewRect = Rect().apply { otherView.getGlobalVisibleRect(this) }
    val intersects = viewRect.intersect(otherViewRect)
    return Pair(intersects, viewRect)
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2