当我按下按钮时,我会显示一些视图。如果我在这些视图之外点击,我希望它们消失。
在Android上,应该如何实现这个功能呢?
此外,我意识到“返回按钮”也可以帮助Android用户完成这个操作-我可能会将其用作关闭视图的辅助方法-但是一些平板电脑甚至不再使用“物理”返回按钮,因为它已经几乎不被强调了。
一种简单/愚蠢的方法:
创建一个虚拟的空视图(比如说没有源头的 ImageView),让它充满整个父布局。
如果该视图被点击,那么就执行你想要做的事情。
你需要在你的 XML 文件中设置 RelativeLayout 为根标签。它包含两个元素:虚拟视图(将其位置设置为与父布局顶部对齐
)。另一个是包含视图和按钮的原始视图(这个视图可以是 LinearLayout 或者其他类型的布局,不要忘记把它的位置设置为 与父布局顶部对齐
)。
希望这对你有所帮助,祝你好运!
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Rect viewRect = new Rect();
mTooltip.getGlobalVisibleRect(viewRect);
if (!viewRect.contains((int) ev.getRawX(), (int) ev.getRawY())) {
setVisibility(View.GONE);
}
return true;
}
如果你想在其他地方使用触摸事件,请尝试
return super.dispatchTouchEvent(ev);
dispatchTouchEvent()
应该返回一个布尔值,但在你的示例中没有返回任何内容。此外,你没有定义也没有解释什么是 viewRect
。 - CaptJakmToolTip
是什么? - ToMakPo@Override public boolean dispatchTouchEvent(MotionEvent ev) { Rect viewRect = new Rect(); mTooltip.getGlobalVisibleRect(viewRect); if (!viewRect.contains((int) ev.getRawX(), (int) ev.getRawY())) { setVisibility(View.GONE); return true; } return super.dispatchTouchEvent(ev); }
- SadeQ digitALLife这是一个老问题,但我想给出一个不基于onTouch
事件的答案。就像RedLeader所建议的那样,使用焦点事件也可以实现此功能。 我有一个案例需要在自定义弹出窗口中显示和隐藏一堆排列在同一ViewGroup
中的按钮。您需要执行以下操作才能使其工作:
您希望隐藏的视图组需要设置View.setFocusableInTouchMode(true)
。 这也可以使用android:focusableintouchmode
在XML中设置。
您的视图根,即整个布局的根,可能是某种线性或相对布局,也需要能够作为#1上述进行聚焦
当显示视图组时,请调用View.requestFocus()
来将其聚焦。
您的视图组需要覆盖View.onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect)
或实现自己的OnFocusChangeListener
并使用View.setOnFocusChangeListener()
当用户点击您的视图之外时,焦点将转移到视图根(因为您已在#2中设置它为可聚焦)或直接转移到其他本质上可聚焦的视图(如EditText
等)。
当您使用#4中的方法检测到焦点丢失时,您知道焦点已转移到视图组之外的某个位置,因此可以将其隐藏。
我猜这种解决方案并不能在所有场景下都起作用,但它适用于我的具体案例,并且听起来似乎也适用于OP。
我一直在寻找一种方法,当触摸屏幕外部时关闭我的视图,但是这些方法都不太适合我的需求。我找到了一个解决方案,在这里发布一下,以便有兴趣的人参考。
我有一个基础活动,几乎所有的活动都是继承自它。在这个基础活动中,我有:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (myViewIsVisible()){
closeMyView();
return true;
}
return super.dispatchTouchEvent(ev);
}
如果我的视图是可见的,它将会关闭;如果不可见,则会像普通的触摸事件一样处理。我不确定这是否是最好的方法,但这对我来说似乎可行。
基于Kai Wang的建议:我建议首先检查您的视图的可见性。根据我的情况,当用户点击fab时,myView会变得可见,然后当用户再次点击外部时,myView会消失。
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Rect viewRect = new Rect();
myView.getGlobalVisibleRect(viewRect);
if (myView.getVisibility() == View.VISIBLE && !viewRect.contains((int) ev.getRawX(), (int) ev.getRawY())) {
goneAnim(myView);
return true;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
ViewParent parent = getParent();
if(parent != null && parent instanceof ViewGroup) {
((ViewGroup) parent).removeView(this);
}
return super.onInterceptTouchEvent(ev);
}
<com.example.DismissViewGroup android:id="@+id/touch_interceptor_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent" ...
<LinearLayout android:id="@+id/notification_bar_view" ...
这将允许您与视图进行交互,当您点击视图外部时,您既可以关闭视图,又可以正常地与活动进行交互。
class OutsideClickConstraintLayout(context: Context, attrs: AttributeSet?) :
ConstraintLayout(context, attrs) {
private var viewOutsideClickListenerMap = mutableMapOf<View, () -> Unit>()
fun setOnOutsideClickListenerForView(view: View, listener: () -> Unit) {
viewOutsideClickListenerMap[view] = listener
}
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
viewOutsideClickListenerMap.forEach { (view, function) ->
if (isMotionEventOutsideView(view, ev)) function.invoke()
}
return super.onInterceptTouchEvent(ev)
}
private fun isMotionEventOutsideView(view: View, motionEvent: MotionEvent): Boolean {
val viewRectangle = Rect()
view.getGlobalVisibleRect(viewRectangle)
return !viewRectangle.contains(motionEvent.rawX.toInt(), motionEvent.rawY.toInt())
}
}
使用方法:
....
outsideClickContainerView.setOnOutsideClickListenerForView(someView) {
// handle click outside someView
}
....
实现onTouchListener()
。检查触摸的坐标是否在视图的坐标之外。
可能有一些用onFocus()
等方法实现的方式,但我不知道。
Fragmelayout
创建一个包装器视图,该视图将覆盖您的主布局。 <FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- This is your main layout-->
</RelativeLayout>
<View
android:id="@+id/v_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- This is the wrapper layout-->
</View>
</FrameLayout>
步骤2:现在在您的Java代码中添加逻辑,如下所示 -
View viewOverlay = findViewById(R.id.v_overlay);
View childView = findViewByID(R.id.childView);
Button button = findViewByID(R.id.button);
viewOverlay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
childView.setVisibility(View.GONE);
view.setVisibility(View.GONE);
}
});
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
childView.setVisibility(View.VISIBLE);
// Make the wrapper view visible now after making the child view visible for handling the
// main visibility task.
viewOverlay.setVisibility(View.VISIBLE);
}
});
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
if (isMenuVisible) {
if (!isWithinViewBounds(ev.rawX.toInt(), ev.rawY.toInt())) {
hideYourView()
return true
}
}
return super.dispatchTouchEvent(ev)
}
private fun isWithinViewBounds(xPoint: Int, yPoint: Int): Boolean {
val l = IntArray(2)
llYourView.getLocationOnScreen(l)
val x = l[0]
val y = l[1]
val w: Int = llYourView.width
val h: Int = llYourView.height
return !(xPoint < x || xPoint > x + w || yPoint < y || yPoint > y + h)
}