Commit dedb09a1 authored by Simon Chae's avatar Simon Chae Committed by Sebastian Kaspari
Browse files

Closes #4536: Add View.findViewHierarchy()

This allows EngineViewBottomBehavior.onDependentViewChanged() to call setVerticalClipping on any nested EngineView
parent ada6efe3
......@@ -10,6 +10,7 @@ import android.util.AttributeSet
import android.view.View
import androidx.coordinatorlayout.widget.CoordinatorLayout
import mozilla.components.concept.engine.EngineView
import mozilla.components.support.ktx.android.view.findViewInHierarchy
/**
* A [CoordinatorLayout.Behavior] implementation to be used with [EngineView] when placing a toolbar at the
......@@ -37,8 +38,13 @@ class EngineViewBottomBehavior(
return super.layoutDependsOn(parent, child, dependency)
}
/**
* Apply vertical clipping to [EngineView]. This requires [EngineViewBottomBehavior] to be set
* in/on the [EngineView] or its parent. Must be a direct descending child of [CoordinatorLayout].
*/
override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
(child as EngineView).setVerticalClipping(dependency.height - dependency.translationY.toInt())
val engineView = child.findViewInHierarchy { it is EngineView } as EngineView?
engineView?.setVerticalClipping(dependency.height - dependency.translationY.toInt())
return true
}
}
......@@ -9,6 +9,7 @@ import android.graphics.Rect
import android.os.Handler
import android.os.Looper
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.view.inputmethod.InputMethodManager
import androidx.annotation.MainThread
......@@ -105,6 +106,22 @@ fun View.toScope(): CoroutineScope {
return scope
}
/**
* Finds the first a view in the hierarchy, for which the provided predicate is true.
*/
fun View.findViewInHierarchy(predicate: (View) -> Boolean): View? {
if (predicate(this)) return this
if (this is ViewGroup) {
for (i in 0 until this.childCount) {
val childView = this.getChildAt(i).findViewInHierarchy(predicate)
if (childView != null) return childView
}
}
return null
}
/**
* Registers a one-time callback to be invoked when the global layout state
* or the visibility of views within the view tree changes.
......
......@@ -5,9 +5,12 @@
package mozilla.components.support.ktx.android.view
import android.app.Activity
import android.content.Context
import android.view.View
import android.view.WindowManager
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlinx.coroutines.Dispatchers
......@@ -23,6 +26,7 @@ import mozilla.components.support.test.robolectric.testContext
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
......@@ -183,4 +187,31 @@ class ViewTest {
assertFalse(latch.await(5, TimeUnit.SECONDS))
assertFalse(coroutineExecuted)
}
@Test
fun `correct view is found in the hierarchy matching the predicate`() {
val root = LinearLayout(testContext)
val layout = RelativeLayout(testContext)
val testView = TestView(testContext)
layout.addView(testView)
root.addView(layout)
val rootFound = root.findViewInHierarchy { it is LinearLayout }
assertNotNull(rootFound)
assertTrue(rootFound is LinearLayout)
val layoutFound = root.findViewInHierarchy { it is RelativeLayout }
assertNotNull(layoutFound)
assertTrue(layoutFound is RelativeLayout)
val testViewFound = root.findViewInHierarchy { it is TestView }
assertNotNull(testViewFound)
assertTrue(testViewFound is TestView)
}
private class TestView(context: Context) : View(context)
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment