我有一些观点,按下按钮后可以看到。如果我在这些视图之外点击,我希望它们消失。
如何在Android上完成?
此外,我意识到“后退按钮”也可以帮助Android用户 - 我可能会将其作为关闭视图的第二种方式 - 但有些平板电脑甚至没有使用“物理”后退按钮它已经非常不再强调了。
答案 0 :(得分:32)
一种简单/愚蠢的方式:
创建一个虚拟空视图(让我们说没有源的ImageView),让它填充父
如果单击它,则执行您想要执行的操作。
您需要将XML文件中的根标记设置为RelativeLayout。它将包含两个元素:您的虚拟视图(将其位置设置为align the Parent Top
)。另一个是包含视图和按钮的原始视图(此视图可能是LinearLayout或您创建的任何内容。不要忘记将其位置设置为align the Parent Top
)
希望这会对你有所帮助,祝你好运!
答案 1 :(得分:24)
这是一个老问题,但我想我会给出一个不基于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
或类似的)< / p>
当您使用#4中的某个方法检测到焦点丢失时,您知道焦点已转移到视图组之外的某个位置,您可以隐藏它。
我想这个解决方案在所有情况下都不起作用,但它在我的特定情况下起作用,听起来好像它也适用于OP。
答案 2 :(得分:22)
找到视图矩形,然后检测click事件是否在视图之外。
df %>%
filter_at(vars(target_columns), all_vars(. >= 0) )
id sth1 tg1_num sth2 tg2_num others
1 1 dave 2 ca 35 new
2 4 leroy 0 az 25 old
3 5 jerry 4 mi 55 old
如果您想在其他地方使用触摸事件,请尝试
@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;
}
答案 3 :(得分:10)
我一直在寻找一种在外面触摸时关闭视角的方法,这些方法都不能很好地满足我的需求。我确实找到了一个解决方案,只是在这里发布以防万一有人感兴趣。
我有一个基本活动,几乎所有活动都延伸了。在其中我有:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (myViewIsVisible()){
closeMyView();
return true;
}
return super.dispatchTouchEvent(ev);
}
因此,如果我的视图可见,它将关闭,如果不是,它将表现得像普通的触摸事件。不确定这是否是最佳方式,但它似乎对我有用。
答案 4 :(得分:2)
我需要具体的功能,不仅可以在外部点击时删除视图,还可以让点击正常传递给活动。例如,我有一个单独的布局,notification_bar.xml,我需要动态膨胀并添加到需要时的当前活动。
如果我创建一个覆盖视图,屏幕大小可以在notification_bar视图之外接收任何点击,并在点击时删除这两个视图,那么父视图(活动的主视图)仍然没有收到任何点击,这意味着,当notification_bar可见时,点击一个按钮需要两次点击(一个用于关闭notification_bar视图,另一个用于点击按钮)。
要解决此问题,您只需创建自己的扩展ViewGroup的DismissViewGroup并覆盖以下方法:
@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" ...
这将允许您与视图进行交互,并且当您在视图外部单击时,您都会关闭视图并与活动正常交互。
答案 5 :(得分:0)
实施onTouchListener()
。检查触摸的坐标是否在视图的坐标之外。
使用onFocus()
等可能有某种方法可以做到这一点 - 但我不知道。
答案 6 :(得分:0)
我已创建自定义ViewGroup以显示锚定到另一个视图(弹出气球)的信息框。 子视图是实际信息框,BalloonView是全屏,用于孩子的绝对定位和拦截触摸。
public BalloonView(View anchor, View child) {
super(anchor.getContext());
//calculate popup position relative to anchor and do stuff
init(...);
//receive child via constructor, or inflate/create default one
this.child = child;
//this.child = inflate(...);
//this.child = new SomeView(anchor.getContext());
addView(child);
//this way I don't need to create intermediate ViewGroup to hold my View
//but it is fullscreen (good for dialogs and absolute positioning)
//if you need relative positioning, see @iturki answer above
((ViewGroup) anchor.getRootView()).addView(this);
}
private void dismiss() {
((ViewGroup) getParent()).removeView(this);
}
处理儿童内部的点击次数:
child.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//write your code here to handle clicks inside
}
});
通过点击外部关闭我的视图,不要将触摸委托给基础视图:
BalloonView.this.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
通过单击外部以及将触摸委派给基础视图来解除我的视图:
@Override
public boolean onTouchEvent(MotionEvent event) {
dismiss();
return false; //allows underlying View to handle touch
}
按下后退按钮时关闭:
//do this in constructor to be able to intercept key
setFocusableInTouchMode(true);
requestFocus();
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
dismiss();
return true;
}
return super.onKeyPreIme(keyCode, event);
}
答案 7 :(得分:0)
基于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);
}
答案 8 :(得分:0)
我想分享我的解决方案,我认为,如果:
首先,我们创建一个自定义ViewGroup来拦截触摸事件:
class OutsideTouchDispatcherLayout @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
private val rect = Rect()
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
if (ev.action == MotionEvent.ACTION_DOWN) {
val x = ev.x.roundToInt()
val y = ev.y.roundToInt()
traverse { view ->
if (view is OutsideTouchInterceptor) {
view.getGlobalVisibleRect(rect)
val isOutside = rect.contains(x, y).not()
if (isOutside) {
view.interceptOutsideTouch(ev)
}
}
}
}
return false
}
interface OutsideTouchInterceptor {
fun interceptOutsideTouch(ev: MotionEvent)
}
}
fun ViewGroup.traverse(process: (View) -> Unit) {
for (i in 0 until childCount) {
val child = getChildAt(i)
process(child)
if (child is ViewGroup) {
child.traverse(process)
}
}
}
如您所见,OutsideTouchDispatcherLayout
拦截触摸事件并通知每个后代视图,这些实现向OutsideTouchInterceptor
表示某个触摸事件发生在该视图之外。
这是后代视图如何处理此事件的方法。请注意,它必须实现OutsideTouchInterceptor
接口:
class OutsideTouchInterceptorView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr),
OutsideTouchDispatcherLayout.OutsideTouchInterceptor {
override fun interceptOutsideTouch(ev: MotionEvent) {
visibility = GONE
}
}
然后,仅通过儿童与父母的关系即可轻松进行外部触摸检测:
<?xml version="1.0" encoding="utf-8"?>
<com.example.touchinterceptor.OutsideTouchDispatcherLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.touchinterceptor.OutsideTouchInterceptorView
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#eee"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.example.touchinterceptor.OutsideTouchDispatcherLayout>
答案 9 :(得分:0)
步骤1:通过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);
}
});
答案 10 :(得分:0)
以下是完成工作的简单方法:
第 1 步:为要为其生成外部点击事件的元素的外部容器创建 ID。
就我而言,它是一个线性布局,我为其指定了 id 为“outsideContainer”
第 2 步: 为该外部容器设置一个 onTouchListener,它将简单地充当内部元素的点击外部事件!
outsideContainer.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// perform your intended action for click outside here
Toast.makeText(YourActivity.this, "Clicked outside!", Toast.LENGTH_SHORT).show();
return false;
}
}
);
答案 11 :(得分:0)
在视图外执行点击时隐藏视图:
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)
}
答案 12 :(得分:-1)
感谢@ituki的想法
FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/search_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#80000000"
android:clickable="true">
<LinearLayout
android:clickable="true" // not trigger
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="#FFF"
android:orientation="vertical"
android:padding="20dp">
...............
</LinearLayout>
</FrameLayout>
和java代码
mContainer = (View) view.findViewById(R.id.search_container);
mContainer.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
Log.d("aaaaa", "outsite");
return true;
}
return false;
}
});
在LinearLayout外部触摸
时可以正常工作