EditText, dışarıda dokunmaya net odaklanma


100

Düzenim ListView, SurfaceViewve içerir EditText. Üzerine tıkladığımda EditTextodaklanıyor ve ekran klavyesi açılıyor. Dışında bir yere tıkladığımda EditText, hala odakta (olmamalı). Sanırım OnTouchListenermizanpajdaki diğer görünümlerde 'ler ayarlayabilir ve EditTextodağını manuel olarak temizleyebilirim . Ama çok hilekâr görünüyor ...

Diğer düzen - liste görünümünde de, bazıları EditTextiçeride olan farklı türde öğelerle aynı duruma sahibim . Tıpkı yukarıda yazdığım gibi davranıyorlar.

Görev, EditTextkullanıcı onun dışındaki bir şeye dokunduğunda odağı kaybetmektir.

Burada benzer sorular gördüm, ancak herhangi bir çözüm bulamadım ...

Yanıtlar:


67

Tüm bu çözümleri denedim. edc598'ler çalışmaya en yakın olanıydı, ancak dokunma olayları Viewdüzende bulunan diğer ' lerde tetiklenmedi . Herhangi birinin bu davranışa ihtiyacı olursa, sonunda bunu yapmaya başladım:

Düzende son FrameLayoutolarak touchInterceptor adında bir (görünmez) yarattım , Viewböylece her şeyi kaplar ( düzenleme: ayrıca RelativeLayoutana düzen olarak a kullanmalı ve touchInterceptor fill_parent niteliklerini vermelisiniz ). Sonra dokunuşları kesmek ve dokunuşun üstünde olup EditTextolmadığını belirlemek için kullandım:

FrameLayout touchInterceptor = (FrameLayout)findViewById(R.id.touchInterceptor);
touchInterceptor.setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            if (mEditText.isFocused()) {
                Rect outRect = new Rect();
                mEditText.getGlobalVisibleRect(outRect);
                if (!outRect.contains((int)event.getRawX(), (int)event.getRawY())) {
                    mEditText.clearFocus();
                    InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 
                    imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
                }
            }
        }
        return false;
    }
});

Dokunma işleminin düşmesine izin vermek için yanlış döndür.

Çatlak ama benim için işe yarayan tek şey bu.


21
Aktivitenizdeki dispatchTouchEvent'i (MotionEvent ev) geçersiz kılarak, ek bir düzen eklemeden de aynı şeyi yapabilirsiniz.
pcans

clearFocus () ipucu için teşekkürler, SearchView'de de benim için çalıştı
Jimmy Ilenloa

1
Aşağıdaki @ zMan'ın cevabına ipucu vermek istiyorum. Buna dayanıyor, ancak bir görüntü yığını gerektirmiyoroverflow.com/a/28939113/969016
Boy

Ayrıca, klavyenin gizlenmediği, ancak odak temizlendiği bir durumla karşılaşırsa, önce çağırın, hideSoftInputFromWindow()sonra net odağı
Ahmet Gokdayi

234

Ken'in cevabına dayanarak, işte en modüler kopyala ve yapıştır çözümü.

XML gerekmez.

Aktivitenize koyun ve o aktivite içindeki parçalar dahil tüm EditTexts için geçerli olsun.

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        View v = getCurrentFocus();
        if ( v instanceof EditText) {
            Rect outRect = new Rect();
            v.getGlobalVisibleRect(outRect);
            if (!outRect.contains((int)event.getRawX(), (int)event.getRawY())) {
                v.clearFocus();
                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
            }
        }
    }
    return super.dispatchTouchEvent( event );
}

12
İyi görünüyor. Yine de bir sorun yaşıyorum, Tüm düzenleme metinlerim kaydırma görünümünde ve en üstteki düzenleme metninde her zaman imleç görünür durumda. Dışarıya tıkladığımda bile odak kayboluyor ve klavye kayboluyor, ancak imleç yine de üst düzenleme metninde görünüyor.
Vaibhav Gupta

1
Karşılaştığım En İyi Çözüm !! Daha önce @Override public boolean onTouch (View v, MotionEvent olayı) {if (v.getId ()! = Search_box.getId ()) {if (search_box.hasFocus ()) {InputMethodManager imm = (InputMethodManager) getSystemService ( Bağlam.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow (search_box.getWindowToken (), 0); search_box.clearFocus (); } yanlış dönüş; } yanlış dönüş; }
Pradeep Kumar Kushwaha

1
One shot solution :)
karthik kolanji

15
Daha önce hiç görmediğim en iyi çözüm! Bu kodu oğluma ve torunuma iletmek için özüme kaydedeceğim.
Fan Applelin

3
Kullanıcı başka bir EditText öğesini tıklarsa klavye kapanır ve hemen yeniden açılır. Değişti bu I çözmek için MotionEvent.ACTION_DOWNiçin MotionEvent.ACTION_UPkodunuzu. Harika cevap btw!
Thanasis1101

35

EditText'in üst görünümü için şu 3 niteliğin " true " olmasına izin verin :
tıklanabilir , odaklanabilir , odaklanabilirInTouchMode .

Bir görüş odak almak istiyorsa, bu 3 koşulu karşılamalıdır.

Android.view'a bakın :

public boolean onTouchEvent(MotionEvent event) {
    ...
    if (((viewFlags & CLICKABLE) == CLICKABLE || 
        (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
        ...
        if (isFocusable() && isFocusableInTouchMode()
            && !isFocused()) {
                focusTaken = requestFocus();
        }
        ...
    }
    ...
}

Umarım yardımcı olur.



2
Odağı temizlemek için diğer odaklanabilir görünüm, odaklanmış görünümün üst öğesi olmalıdır. Odaklanılacak görünüm başka bir hiyerarşi üzerindeyse işe yaramaz.
AxeEffect

Teknik olarak, ifadeniz yanlış. Ebeveyn görüşü olmalıdır (clickable) *OR* (focusable *AND* focusableInTouchMode);)
Martin Marconcini

25

Sadece bu özellikleri en üst sıraya koyun.

android:focusableInTouchMode="true"
android:clickable="true"
android:focusable="true" 

teşekkürler dostum. Son iki saattir bu sorunla uğraşıyordum. Bu satırları üst üste eklemek işe yaradı.
Däñish Shärmà

Bilginize: Çoğu durumda işe yarasa da, yerleşim düzeninin bazı iç unsurları için çalışmıyor.
SV Madhava Reddy

17

Ken'in cevabı işe yarıyor, ancak hilekar. Pcans'ın yanıtlayanın yorumunda ima ettiği gibi, aynı şey dispatchTouchEvent ile yapılabilir. Bu çözüm, XML'yi şeffaf, sahte bir FrameLayout ile kesmek zorunda kalmadığından daha temizdir. İşte bunun neye benzediği:

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    EditText mEditText = findViewById(R.id.mEditText);
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        View v = getCurrentFocus();
        if (mEditText.isFocused()) {
            Rect outRect = new Rect();
            mEditText.getGlobalVisibleRect(outRect);
            if (!outRect.contains((int)event.getRawX(), (int)event.getRawY())) {
                mEditText.clearFocus();
                //
                // Hide keyboard
                //
                InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 
                imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
            }
        }
    }
    return super.dispatchTouchEvent(event);
}

7

Benim İçin Aşağıdakiler Çalıştı -

1. ekleme android:clickable="true"ve android:focusableInTouchMode="true"için parentLayoutbir EditTextdeyişleandroid.support.design.widget.TextInputLayout

<android.support.design.widget.TextInputLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:clickable="true"
    android:focusableInTouchMode="true">
<EditText
    android:id="@+id/employeeID"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:ems="10"
    android:inputType="number"
    android:hint="Employee ID"
    tools:layout_editor_absoluteX="-62dp"
    tools:layout_editor_absoluteY="16dp"
    android:layout_marginTop="42dp"
    android:layout_alignParentTop="true"
    android:layout_alignParentRight="true"
    android:layout_alignParentEnd="true"
    android:layout_marginRight="36dp"
    android:layout_marginEnd="36dp" />
    </android.support.design.widget.TextInputLayout>

2. Activity sınıfında geçersiz kılma dispatchTouchEventve hideKeyboard()fonksiyon ekleme

@Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            View view = getCurrentFocus();
            if (view != null && view instanceof EditText) {
                Rect r = new Rect();
                view.getGlobalVisibleRect(r);
                int rawX = (int)ev.getRawX();
                int rawY = (int)ev.getRawY();
                if (!r.contains(rawX, rawY)) {
                    view.clearFocus();
                }
            }
        }
        return super.dispatchTouchEvent(ev);
    }

    public void hideKeyboard(View view) {
        InputMethodManager inputMethodManager =(InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE);
        inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
    }

3. ekleme setOnFocusChangeListenerEditText için

EmployeeId.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (!hasFocus) {
                    hideKeyboard(v);
                }
            }
        });

5

Muhtemelen bu sorunun cevabını zaten bulmuşsunuzdur, ancak bunu nasıl çözeceğime bakıyordum ve hala aradığımı tam olarak bulamıyorum, bu yüzden buraya koyacağımı düşündüm.

Yaptığım şey şuydu (bu çok genelleştirilmiş, amaç size nasıl devam edeceğiniz konusunda bir fikir vermektir, tüm kodu kopyalayıp yapıştırmak işe yaramayacaktır O: D):

Öncelikle, programınızda istediğiniz düzenleme metni ve diğer görünümleri tek bir görünümle sarın. Benim durumumda, her şeyi sarmak için bir LinearLayout kullandım.

<LinearLayout 
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/mainLinearLayout">
 <EditText
  android:id="@+id/editText"/>
 <ImageView
  android:id="@+id/imageView"/>
 <TextView
  android:id="@+id/textView"/>
 </LinearLayout>

Daha sonra kodunuzda, ana Doğrusal Yerleşiminize bir Dokunma Dinleyici ayarlamanız gerekir.

final EditText searchEditText = (EditText) findViewById(R.id.editText);
mainLinearLayout.setOnTouchListener(new View.OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            // TODO Auto-generated method stub
            if(searchEditText.isFocused()){
                if(event.getY() >= 72){
                    //Will only enter this if the EditText already has focus
                    //And if a touch event happens outside of the EditText
                    //Which in my case is at the top of my layout
                    //and 72 pixels long
                    searchEditText.clearFocus();
                    InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
                    imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
                }
            }
            Toast.makeText(getBaseContext(), "Clicked", Toast.LENGTH_SHORT).show();
            return false;
        }
    });

Umarım bu bazı insanlara yardımcı olur. Ya da en azından problemlerini çözmeye başlamalarına yardımcı olur.


Evet, ben de benzer çözüme geldim. Daha sonra, android görünüm sisteminin bir alt kümesini
opengl'de

Bu gerçekten çok güzel bir örnek. Bunun gibi bir kod arıyordum ama tek bulabildiğim karmaşık çözümlerdi. Birkaç çok satırlı EditText'e sahip olduğum için her iki XY koordinatını daha kesin olarak kullandım. Teşekkürler!
Sumitk

3

Bunun ebeveyninin iki özelliğini basitçe şu şekilde tanımlayın EditText:

android:clickable="true"
android:focusableInTouchMode="true"

Dolayısıyla, kullanıcı EditTextalanın dışına dokunduğunda odak kaldırılacaktır çünkü odak üst görünüme aktarılacaktır.


2

Bir ListViewdizi EditTextgörüşüm var. Senaryo, bir veya daha fazla satırdaki metni düzenledikten sonra "bitir" adlı bir düğmeye tıklamamız gerektiğini söylüyor. İçerideki görünümü kullandım onFocusChanged, ancak bitir'e tıkladıktan sonra veriler kaydedilmiyor. Sorun ekleyerek çözüldüEditTextlistView

listView.clearFocus();

içeride onClickListener"bitiş" düğmesini ve veriler için başarıyla kaydedildi.


Teşekkürler. Zamanımı kazandınız, bu senaryoyu geri dönüşüm görünümündeydim ve düğmeye tıkladıktan sonra son editText değerini kaybediyordum, çünkü son editText odağı değişmedi ve bu yüzden onFocusChanged çağrılmadı. Son editText'in, dışarıdayken odağı kaybetmesini, aynı düğmeyi tıklatıp çözümünüzü bulmasını sağlayacak bir çözüm bulmak istiyorum. Harika çalıştı!
Reyhane Farshbaf

2

Gerçekten kullanımı daha sağlam yol olduğunu düşünüyorum getLocationOnScreendaha getGlobalVisibleRect. Çünkü bir sorunla karşılaşıyorum. Bazı Edittext içeren ajustpanve aktivitede ayarlanmış bir Listview vardır . Ben bulmak getGlobalVisibleRectİçinde scrollY ama event.getRawY dahil gibi görünüyor ekrana tarafından her zaman bir değer döndürür. Aşağıdaki kod iyi çalışıyor.

public boolean dispatchTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        View v = getCurrentFocus();
        if ( v instanceof EditText) {
            if (!isPointInsideView(event.getRawX(), event.getRawY(), v)) {
                Log.i(TAG, "!isPointInsideView");

                Log.i(TAG, "dispatchTouchEvent clearFocus");
                v.clearFocus();
                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
            }
        }
    }
    return super.dispatchTouchEvent( event );
}

/**
 * Determines if given points are inside view
 * @param x - x coordinate of point
 * @param y - y coordinate of point
 * @param view - view object to compare
 * @return true if the points are within view bounds, false otherwise
 */
private boolean isPointInsideView(float x, float y, View view) {
    int location[] = new int[2];
    view.getLocationOnScreen(location);
    int viewX = location[0];
    int viewY = location[1];

    Log.i(TAG, "location x: " + location[0] + ", y: " + location[1]);

    Log.i(TAG, "location xWidth: " + (viewX + view.getWidth()) + ", yHeight: " + (viewY + view.getHeight()));

    // point is inside view bounds
    return ((x > viewX && x < (viewX + view.getWidth())) &&
            (y > viewY && y < (viewY + view.getHeight())));
}

çok teşekkür ederim! RecyclerView vb. Gibi tüm düzen senaryoları için çalıştı. Başka bir çözümü denedim ama bu hepsine uyuyor! :) İyi iş!
Woppi

1

Diğer görünüme dokunulduğunda odağı kaybetmek için, her iki görünüm de view.focusableInTouchMode (true) olarak ayarlanmalıdır.

Ancak dokunma modunda odakların kullanılması tavsiye edilmiyor gibi görünüyor. Lütfen buraya bir göz atın: http://android-developers.blogspot.com/2008/12/touch-mode.html


Evet, bunu okudum ama istediğim tam olarak bu değil. Odaklanmak için diğer görüşe ihtiyacım yok. Şu anda alternatif bir yol görmesem de ... Belki, kullanıcı odaklanılamayan çocuğa tıkladığında odağı yakalamak için bir şekilde kök görünümü yapabilir mi?
note173

Bildiğim kadarıyla, odak bir sınıf tarafından yönetilemez, android tarafından yönetiliyor ... fikir yok :(
forumercio

0

En iyi yol varsayılan yöntemi kullanmaktır clearFocus()

Kodları nasıl çözeceğinizi biliyorsunuz onTouchListenerdeğil mi?

Sadece ara EditText.clearFocus(). Odaklanmayı netleştirecek last EditText.


0

@Pcans'ın önerdiği gibi, bunu dispatchTouchEvent(MotionEvent event)etkinliğinizde yapabilirsiniz.

Burada dokunma koordinatlarını alıyoruz ve bunları görüntülemek için karşılaştırıyoruz. Dokunma bir görünümün dışında gerçekleştirilirse, bir şeyler yapın.

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        View yourView = (View) findViewById(R.id.view_id);
        if (yourView != null && yourView.getVisibility() == View.VISIBLE) {
            // touch coordinates
            int touchX = (int) event.getX();
            int touchY = (int) event.getY();
            // get your view coordinates
            final int[] viewLocation = new int[2];
            yourView.getLocationOnScreen(viewLocation);

            // The left coordinate of the view
            int viewX1 = viewLocation[0];
            // The right coordinate of the view
            int viewX2 = viewLocation[0] + yourView.getWidth();
            // The top coordinate of the view
            int viewY1 = viewLocation[1];
            // The bottom coordinate of the view
            int viewY2 = viewLocation[1] + yourView.getHeight();

            if (!((touchX >= viewX1 && touchX <= viewX2) && (touchY >= viewY1 && touchY <= viewY2))) {

                Do what you want...

                // If you don't want allow touch outside (for example, only hide keyboard or dismiss popup) 
                return false;
            }
        }
    }
    return super.dispatchTouchEvent(event);
}

Ayrıca, etkinliğinizin düzeni çalışma sırasında değişmezse görünüm varlığını ve görünürlüğünü kontrol etmek gerekmez (örneğin, düzende parçalar eklemeyin veya görünümleri değiştirmeyin / kaldırmazsanız). Ancak, özel bağlam menüsünü kapatmak (veya benzer bir şey yapmak) istiyorsanız (örneğin, öğenin taşma menüsünü kullanırken Google Play Store'da olduğu gibi), görünüm varlığını kontrol etmeniz gerekir. Aksi takdirde bir alacaksınızNullPointerException .


0

Bu basit kod pasajı istediğinizi yapar

GestureDetector gestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onSingleTapConfirmed(MotionEvent e) {
                KeyboardUtil.hideKeyboard(getActivity());
                return true;
            }
        });
mScrollView.setOnTouchListener((v, e) -> gestureDetector.onTouchEvent(e));

0

Kotlin'de

hidekeyboard () bir Kotlin Uzantısıdır

fun Activity.hideKeyboard() {
    hideKeyboard(currentFocus ?: View(this))
}

Aktivitede dispatchTouchEvent ekleyin

override fun dispatchTouchEvent(event: MotionEvent): Boolean {
    if (event.action == MotionEvent.ACTION_DOWN) {
        val v: View? = currentFocus
        if (v is EditText) {
            val outRect = Rect()
            v.getGlobalVisibleRect(outRect)
            if (!outRect.contains(event.rawX.toInt(), event.rawY.toInt())) {
                v.clearFocus()
                hideKeyboard()
            }
        }
    }
    return super.dispatchTouchEvent(event)
}

Bu özellikleri en üstteki ana alana ekleyin

android:focusableInTouchMode="true"
android:focusable="true"

0

Bu, zMan'ın koduna dayalı sürümüm. Sonraki görünüm de bir düzenleme metni ise klavyeyi gizlemeyecektir. Kullanıcı sadece ekranı kaydırırsa klavyeyi de gizlemeyecektir.

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        downX = (int) event.getRawX();
    }

    if (event.getAction() == MotionEvent.ACTION_UP) {
        View v = getCurrentFocus();
        if (v instanceof EditText) {
            int x = (int) event.getRawX();
            int y = (int) event.getRawY();
            //Was it a scroll - If skip all
            if (Math.abs(downX - x) > 5) {
                return super.dispatchTouchEvent(event);
            }
            final int reducePx = 25;
            Rect outRect = new Rect();
            v.getGlobalVisibleRect(outRect);
            //Bounding box is to big, reduce it just a little bit
            outRect.inset(reducePx, reducePx);
            if (!outRect.contains(x, y)) {
                v.clearFocus();
                boolean touchTargetIsEditText = false;
                //Check if another editText has been touched
                for (View vi : v.getRootView().getTouchables()) {
                    if (vi instanceof EditText) {
                        Rect clickedViewRect = new Rect();
                        vi.getGlobalVisibleRect(clickedViewRect);
                        //Bounding box is to big, reduce it just a little bit
                        clickedViewRect.inset(reducePx, reducePx);
                        if (clickedViewRect.contains(x, y)) {
                            touchTargetIsEditText = true;
                            break;
                        }
                    }
                }
                if (!touchTargetIsEditText) {
                    InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                    imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
                }
            }
        }
    }
    return super.dispatchTouchEvent(event);
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.