web-dev-qa-db-ja.com

Androidビューの外側をクリックしてビューを非表示にします

ボタンを押すと表示されるビューがいくつかあります。これらのビューの外側をクリックすると、表示されなくなります。

これはAndroidでどのように行われますか?

また、「戻るボタン」がAndroid=これでユーザーを支援することもできることを理解しています。これをビューを閉じるための2番目の方法として使用する可能性がありますが、一部のタブレットは使用していません。もはや「物理的な」戻るボタンですが、あまり強調されていません。

36
CQM

簡単/愚かな方法:

  • ダミーの空のビュー(ソースのないImageViewとしましょう)を作成し、親を塗りつぶします

  • それがクリックされたら、あなたがしたいことをしてください。

RelativeLayoutになるには、XMLファイルにルートタグが必要です。 2つの要素が含まれます:ダミービュー(その位置をalign the Parent Topに設定)。もう1つは、ビューとボタンを含む元のビューです(このビューは、LinearLayoutなどにすることができます。位置をalign the Parent Topに設定することを忘れないでください)。

これがあなたに役立つことを願っています、グッドラック!

34
iTurki

ビューの四角形を見つけ、クリックイベントがビューの外にあるかどうかを検出します。

@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);
26
Kai Wang

これは古い質問ですが、onTouchイベントに基づいていない答えを出すと思いました。 RedLeaderによって提案されたように、フォーカスイベントを使用してこれを達成することも可能です。カスタムポップアップに配置された一連のボタンを表示または非表示にする必要がある場合がありました。つまり、ボタンはすべて同じViewGroupに配置されていました。これを機能させるために必要ないくつかのこと:

  1. 非表示にするビューグループには、View.setFocusableInTouchMode(true)を設定する必要があります。これは、_Android:focusableintouchmode_を使用してXMLで設定することもできます。

  2. ビュールート、つまりレイアウト全体のルート、おそらく何らかの線形レイアウトまたは相対レイアウトも、上記の#1のようにフォーカス可能である必要があります

  3. ビューグループが表示されたら、View.requestFocus()を呼び出してフォーカスを与えます。

  4. ビューグループはView.onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect)をオーバーライドするか、独自のOnFocusChangeListenerを実装してView.setOnFocusChangeListener()を使用する必要があります

  5. ユーザーがビューの外側をタップすると、フォーカスはビュールート(#2でフォーカス可能に設定したため)または本質的にフォーカス可能な別のビュー(EditTextまたは類似のもの)に転送されます。

  6. #4の方法の1つを使用してフォーカスの喪失を検出すると、フォーカスがビューグループ外の何かに転送されたことがわかり、非表示にできます。

このソリューションはすべてのシナリオで機能するとは限りませんが、私の特定のケースでは機能し、OPでも機能するように聞こえます。

26
britzl

私は外に触れたときに自分のビューを閉じる方法を探していましたが、これらの方法はどれも私のニーズにうまく適合しません。私は解決策を見つけました。誰かが興味を持っている場合に備えて、ここに投稿します。

私は私の活動のほとんどすべてを拡張する基本活動を持っています。その中に私は持っています:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (myViewIsVisible()){
            closeMyView();
        return true;
    }
    return super.dispatchTouchEvent(ev);
}

したがって、ビューが表示されている場合は閉じられ、表示されていない場合は通常のタッチイベントのように動作します。それが最善の方法かどうかはわかりませんが、私にはうまくいくようです。

11

ビューの外側をクリックしたときにビューを削除するだけでなく、クリックが通常どおりアクティビティに渡されるようにする特定の機能が必要でした。たとえば、notification_bar.xmlという別のレイアウトがあり、動的に膨張させて、必要なときに現在のアクティビティを何にでも追加する必要があります。

画面サイズのオーバーレイビューを作成して、notification_barビューの外側でクリックを受け取り、クリックでこれらのビューを両方とも削除しても、親ビュー(アクティビティのメインビュー)はまだクリックを受け取っていません。 notification_barが表示されている場合、ボタンをクリックするのに2回のクリックが必要です(1つはnotification_barビューを閉じるため、もう1つはボタンをクリックするためです)。

これを解決するには、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" ...

これにより、ビューを操作できます。ビューの外側をクリックすると、ビューを閉じると同時に、アクティビティを通常どおり操作できます。

2
byebrianwong

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

onTouchListener()を実装します。タッチの座標がビューの座標の外にあることを確認してください。

おそらくonFocus()などを使ってそれを行う方法があるでしょう-しかし、私はそれを知りません。

0
RedLeader

別のビュー(ポップアップバルーン)にアンカーされた情報ボックスを表示するカスタム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
Levinthann