Android点击视图外部使视图消失

42

当我按下按钮时,我会显示一些视图。如果我在这些视图之外点击,我希望它们消失。

在Android上,应该如何实现这个功能呢?

此外,我意识到“返回按钮”也可以帮助Android用户完成这个操作-我可能会将其用作关闭视图的辅助方法-但是一些平板电脑甚至不再使用“物理”返回按钮,因为它已经几乎不被强调了。

14个回答

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);
}

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;
                                        }
                                    }
);

0

我想分享我的解决方案,如果:

  • 您能够将自定义ViewGroup添加为根布局
  • 您希望消失的视图也可以是自定义视图。

首先,我们创建一个自定义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>

-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 之外时,它会起作用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接