TouchListener ve clickListener ile vurgulanan durumda sol düğme


10

Aşağıdakileri yaptıktan sonra, düğmemin vurgulanmış durumda kalmasıyla ilgili bir sorun yaşıyorum:

public class MainActivity extends AppCompatActivity {

    @SuppressLint("ClickableViewAccessibility")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        AppCompatButton button = (AppCompatButton) findViewById(R.id.mybutton);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("Test", "calling onClick");
            }
        });
        button.setOnTouchListener(new View.OnTouchListener() {

            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN: {
                        v.getBackground().setColorFilter(0xe0f47521,PorterDuff.Mode.SRC_ATOP);
                        v.invalidate();
                        break;
                    }
                    case MotionEvent.ACTION_UP: {
                        v.getBackground().clearColorFilter();
                        v.invalidate();
                        v.performClick();
                        Log.d("Test", "Performing click");
                        return true;
                    }
                }
                return false;
            }
        });

    }
}

Yukarıdaki kod ile ilgili olarak, bunu kullanırken, dokunma tarafından ele alınacak düğme tıklamasını bekliyorum ve "true" döndürerek, işleme touchListener'da durmalıdır.

Ancak durum böyle değil. Tıklama çağrılsa bile düğme vurgulanmış durumda kalır.

Ne olsun:

Test - calling onClick
Test - Performing click

Öte yandan, aşağıdaki kodu kullanıyorsam, düğme tıklanır, aynı yazdırılır, ancak düğme vurgulanan durumda takılı kalmaz:

public class MainActivity extends AppCompatActivity {

    @SuppressLint("ClickableViewAccessibility")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        AppCompatButton button = (AppCompatButton) findViewById(R.id.mybutton);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("Test", "calling onClick");
            }
        });
        button.setOnTouchListener(new View.OnTouchListener() {

            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN: {
                        v.getBackground().setColorFilter(0xe0f47521,PorterDuff.Mode.SRC_ATOP);
                        v.invalidate();
                        break;
                    }
                    case MotionEvent.ACTION_UP: {
                        v.getBackground().clearColorFilter();
                        v.invalidate();
                        // v.performClick();
                        Log.d("Test", "Performing click");
                        return false;
                    }
                }
                return false;
            }
        });

    }
}

Touch event'in yanıtlayıcı zincirinin ne olduğu konusunda biraz kafam karıştı. Benim tahminim şudur:

1) Dokunmatik Liste

2) ClickListener

3) ParentViews

Birisi de bunu onaylayabilir mi?


Gerçekte ne yapmak istiyorsunuz, sadece basarken dokunarak mı yoksa renk değiştirerek mi?
Haider Saleem

Temas halinde bir mantık çalıştırmak ve daha sonra performClick'i çağırmak ve düğmenin rengini değiştirmemek istiyorum.
Whitebear

@Whitebear Lütfen aşağıdaki cevabı kontrol edin. Belki daha fazla bilgi ekleyebilirim.
GensaGames

Bu , dokunma olaylarının akışını anlamanıza yardımcı olabilir. Ne olmasını istediğiniz belli değil. Bir tıklama işleyici ister ve gerçekleştirme tıklaması mı yapmak istiyorsunuz? Düğmenin başlangıç ​​renginden renk filtresi tarafından ayarlanan duruma, ardından başlangıç ​​rengine dönmesini ister misiniz?
Cheticamp

Ne demek istediğimi açıklayayım. Düğmede bir touchListener ve bir clickListener var. Dokunma, tıklamanın önceliğine göre önceliklidir ve olayı ele alırsa true değerini döndürür; başka bir deyişle, başka hiç kimse tarafından ele alınmamalıdır. Bu, dokunma ile yaptığım, tıklamayı işleyen ve doğru dönen şeydir, ancak onclick dinleyici çağrılmasına ve akış doğru bir şekilde yapılmasına rağmen düğme hala vurgulanmış olarak kalır.
Whitebear

Yanıtlar:


10

Bu tür özelleştirmelerin programlı olarak değiştirilmesi gerekmez. Bunu sadece xmldosyalarda yapabilirsiniz. Her şeyden önce, setOnTouchListenersağladığınız yöntemi onCreatetamamen silin . Ardından, res/colordizinde aşağıdaki gibi bir seçici renk tanımlayın . (dizin mevcut değilse oluşturun)

res / renk / button_tint_color.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="#e0f47521" android:state_pressed="true" />
    <item android:color="?attr/colorButtonNormal" android:state_pressed="false" />
</selector>

Şimdi düğmenin app:backgroundTintözelliğine ayarlayın:

<androidx.appcompat.widget.AppCompatButton
    android:id="@+id/mybutton"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Button"
    app:backgroundTint="@color/button_tint_color" />


Görsel Sonuç:

resim açıklamasını buraya girin



DÜZENLENDİ: (dokunma etkinliği sorununu gidermek için)

Genel bir bakış açısından, dokunma etkinliğinin Activityakışı, öğeden başlar , sonra düzene (üst öğeden alt düzenlere) ve sonra görünümlere doğru akar. (Aşağıdaki resimde LTR akışı)

resim açıklamasını buraya girin

Dokunmatik etkinliği hedef ekrana ulaşır, görünüm olayı daha sonra, önceki düzenleri / etkinlik ya da (döndürme için geçmesi karar işleyebilir falseve trueiçinde onTouchyöntemi). (Yukarıdaki resimde RTL akışı)

Şimdi dokunma olayı akışları hakkında daha derin bir fikir edinmek için Görünüm'ün kaynak koduna bakalım . Uygulanmasında bir göz alarak dispatchTouchEvent, size bir ayarlarsanız de görürdün OnTouchListenersonra görünümüne ve dönüş trueonun içinde onTouchyöntemin, onTouchEventgörüş denilen edilmeyecektir.

public boolean dispatchTouchEvent(MotionEvent event) {
    // removed lines for conciseness...
    boolean result = false;    
    // removed lines for conciseness...
    if (onFilterTouchEventForSecurity(event)) {
        // removed lines for conciseness...
        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnTouchListener != null
                && (mViewFlags & ENABLED_MASK) == ENABLED
                && li.mOnTouchListener.onTouch(this, event)) { // <== right here!
            result = true;
        }
        if (!result && onTouchEvent(event)) {
            result = true;
        }
    }
    // removed lines for conciseness...
    return result;
}

Şimdi, onTouchEventolay eyleminin olduğu yönteme bakın MotionEvent.ACTION_UP. Tıklama gerçekleştir eyleminin orada gerçekleştiğini görüyoruz. Yani, dönen trueiçinde OnTouchListener'ın onTouchve dolayısıyla çağırarak değil onTouchEvent, aramıyor neden OnClickListeners' onClick.

onTouchEventBasılı durumla ilgili olan ve soruda bahsettiğiniz, çağrılmama ile ilgili başka bir sorun var . Aşağıdaki kod bloğunda görebildiğimiz gibi, çalıştığında UnsetPressedStatebu çağrıların bir örneği vardır . Arama yapmamanın sonucu , görünümün basılı durumda takılıp kalması ve çekilebilir durumunun değişmemesidir. setPressed(false)setPressed(false)

public boolean onTouchEvent(MotionEvent event) {
    // removed lines for conciseness...
    if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
        switch (action) {
            case MotionEvent.ACTION_UP:
                // removed lines for conciseness...
                if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                    // removed lines for conciseness...
                    if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                        // removed lines for conciseness...
                        if (!focusTaken) {
                            // Use a Runnable and post this rather than calling
                            // performClick directly. This lets other visual state
                            // of the view update before click actions start.
                            if (mPerformClick == null) {
                                mPerformClick = new PerformClick();
                            }
                            if (!post(mPerformClick)) {
                                performClickInternal();
                            }
                        }
                    }
                    if (mUnsetPressedState == null) {
                        mUnsetPressedState = new UnsetPressedState();
                    }
                    if (prepressed) {
                        postDelayed(mUnsetPressedState,
                                ViewConfiguration.getPressedStateDuration());
                    } else if (!post(mUnsetPressedState)) {
                        // If the post failed, unpress right now
                        mUnsetPressedState.run();
                    }
                    // removed lines for conciseness...
                }
                // removed lines for conciseness...
                break;
            // removed lines for conciseness...
        }
        return true;
    }
    return false;
}

UnsetPressedState :

private final class UnsetPressedState implements Runnable {
    @Override
    public void run() {
        setPressed(false);
    }
}


Yukarıdaki açıklamalarla ilgili olarak setPressed(false), olay eyleminin bulunduğu çekilebilir durumu değiştirmek için kendinizi çağırarak kodu değiştirebilirsiniz MotionEvent.ACTION_UP:

button.setOnTouchListener(new View.OnTouchListener() {

    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                v.getBackground().setColorFilter(0xe0f47521,PorterDuff.Mode.SRC_ATOP);
                v.invalidate();
                break;
            }
            case MotionEvent.ACTION_UP: {
                v.getBackground().clearColorFilter();
                // v.invalidate();
                v.setPressed(false);
                v.performClick();
                Log.d("Test", "Performing click");
                return true;
            }
        }
        return false;
    }
});

Arkadaşım için aradığım şey bu değil. Yukarıda açıkladığım her iki durumda da davranıştaki değişikliği anlamaya çalışıyorum. Ayrıntılı olarak inceleyebileceğim bir şey varsa, lütfen bana bildirin. Dokunmatik işleyiciyi veya tıklama işleyicisini silmek istemiyorum. Lütfen yukarıdaki cevaba yönelik yorumuma göz atın.
Whitebear

@Whitebear: Cevabı güncelledim. Lütfen bir bak, ahbap.
aminografi

Bu iyi ve ayrıntılı bir cevap ve ben bunu kabul ediyorum. Değiştirmek için birkaç işaret: Now, look at the onTouchEvent method where the event action is MotionEvent.ACTION_UP. We see that perform-click action happens there. So, returning true in the OnTouchListener's onTouch and consequently not calling the onTouchEvent, causes not calling the OnClickListener's onClick.Benim durumumda onClick çağrılır. mUnsetPressedState, false olarak ayarlanmadan önce null olup olmadığını kontrol eder ve ayrıca önceden basılmışsa runnable'ın çalışacağından emin değildir. Yanlış olarak ayarlanması gerektiğini nasıl anladığınızı tam olarak anlamıyorum
Whitebear

onClickAradığınız çünkü denir v.performClick();. Lütfen yukarıdaki MotionEvent.ACTION_UPbölümdeki kodu tekrar kontrol edin , null setPressed(false)olsun mUnsetPressedStateya da olmasın prepressed, doğru olsun ya da olmasın yine de çağrılır . Fark çağıran yolunda olduğunu setPressed(false)yoluyla olabileceğini post/ postDelayedveya doğrudan.
aminografi

2

Etrafta karışıklık touchve focusolaylar var. Aynı renkteki davranışı anlamakla başlayalım. Varsayılan olarak, Android'de Selectorarka plan olarak atanır Button. Yani arka plan rengini değiştirmek, yapmak statiktir (renk değişmez). Ama bu yerel bir davranış değil.

Selector buna benzeyebilir.

<?xml version="1.0" encoding="utf-8"?> 
  <selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:state_focused="true"
        android:state_pressed="true"
        android:drawable="@drawable/bgalt" />

    <item
        android:state_focused="false"
        android:state_pressed="true"
        android:drawable="@drawable/bgalt" />

    <item android:drawable="@drawable/bgnorm" />
</selector>

Yukarıda da görebileceğiniz gibi, devlet focusedve devlet vardır pressed. Ayarlayarak onTouchListener, ilgisi olmayan dokunma olaylarını yöneteceksiniz focus.

Selectordüğmesinin tıklama olayı sırasında focusolayla değiştirilmelidir touch. Ancak kodunuzun ilk bölümünde touch(geri aramadan true döndürme) için olayları durdurdunuz . Renk değişimi daha fazla ilerleyemez ve aynı renkle donar. Bu yüzden ikinci varyant (müdahale olmadan) iyi çalışıyor ve bu sizin karışıklığınız.

GÜNCELLEME

Yapmanız gereken tek şey davranışını ve rengini değiştirmek Selector. Örn. için bir sonraki arka planı kullanarak Button. VEonTouchListener hiç uygulamanızdan kaldırın .

<?xml version="1.0" encoding="utf-8"?> 
  <selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:state_pressed="true"
        android:drawable="@color/color_pressed" />

    <item android:drawable="@color/color_normal" />
</selector>

O zaman iyi çalışacak ilk örneği nasıl değiştirirdiniz?
Whitebear

Dokunmatik dinleyiciyi uygulamamdan kaldırmak istemiyorum. dokunmatik işleyiciyi kullanarak belirli bir görünüme tıklamaları yakalamak ve daha sonra bunları kendim ele almak (performClick kullanarak) ve diğer işleyicilere başka işlem yapılmadığını söylemek için dokunmatik dinleyiciden true değerini döndürmek istiyorum. Günlükler örneğimde iyi yazdırılmış, ancak düğme hala vurgulanmış durumda.
Whitebear

@Whitebear Sorularınızda bundan bahsetmediniz. Herhangi bir şekilde, istediğiniz kadar onTouchListeners kullanabilirsiniz . Sadece olayı tüketmene gerek yok return true.
GensaGames

@Whitebear VEYA Seçiciyi kaldırın ve düğmesiyle ham rengi ayarlayın backgroundColor.
GensaGames

çocuklar, hala orijinal yazıda yazdıklarıma hitap etmiyorsun ..
Whitebear

0

düğmeye bir arka plan atarsanız, tıklandığında rengi değiştirmez.

 <color name="myColor">#000000</color>

ve düğmenize backgrount olarak ayarlayın

android:background="@color/myColor"

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.