![](/img/trans.png)
[英]Android: How to make AlertDialog disappear on clicking ok button?
[英]Android make view disappear by clicking outside of it
我有一些視圖可以在按下按鈕時顯示。 如果我在這些視圖之外單擊,我希望它們消失。
這將如何在 Android 上完成?
此外,我意識到“后退按鈕”還可以幫助 Android 用戶使用此功能 - 我可能會將其用作關閉視圖的輔助方式 - 但有些平板電腦甚至不再使用“物理”后退按鈕了,它已經非常不重視了。
一個簡單/愚蠢的方法:
創建一個虛擬的空視圖(假設 ImageView 沒有源),使其填充父視圖
如果它被點擊,然后做你想做的事。
您需要將 XML 文件中的根標簽作為相對布局。 它將包含兩個元素:您的虛擬視圖(設置其 position 以align the Parent Top
)。 另一個是包含視圖和按鈕的原始視圖(此視圖可能是 LinearLayout 或您制作的任何內容。不要忘記將其 position 設置為align the Parent Top
)
希望這對你有幫助,祝你好運!
找到視圖矩形,然后檢測點擊事件是否在視圖之外。
@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);
這是一個老問題,但我想我會給出一個不基於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);
}
因此,如果我的視圖可見,它就會關閉,如果不可見,它的行為就像正常的觸摸事件一樣。 不確定這是否是最好的方法,但它似乎對我有用。
基於王凱回答:我建議首先檢查您的視圖的可見性,根據我的場景,當用戶點擊 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);
}
我需要特定的能力,不僅可以在單擊外部視圖時刪除視圖,而且還需要允許單擊正常傳遞到活動。 例如,我有一個單獨的布局,notification_bar.xml,我需要動態膨脹並在需要時添加到當前活動的任何內容。
如果我創建一個屏幕大小的覆蓋視圖以接收 notification_bar 視圖之外的任何點擊並在點擊時刪除這兩個視圖,則父視圖(活動的主視圖)仍然沒有收到任何點擊,這意味着,當 notification_bar 可見時,單擊一個按鈕需要兩次單擊(一次關閉 notification_bar 視圖,一次單擊按鈕)。
要解決這個問題,您可以創建自己的 DismissViewGroup 擴展 ViewGroup 並覆蓋以下方法:
@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" ...
這將允許您與視圖交互,並且當您在視圖外部單擊時,您將關閉視圖並與活動正常交互。
第 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);
}
});
實現onTouchListener()
。 檢查觸摸的坐標是否在您的視圖坐標之外。
可能有某種方法可以使用onFocus()
等 - 但我不知道。
我創建了自定義 ViewGroup 來顯示錨定到另一個視圖(彈出氣球)的信息框。 Child view 是實際的信息框,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);
}
我想分享我的解決方案,我認為它在以下情況下會很有用:
首先,我們創建一個自定義的 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 步:為要為其生成點擊外部事件的元素的外部容器創建一個 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;
}
}
);
在視圖外執行單擊時隱藏視圖:
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)
}
感謝@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 之外觸摸時有效
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.