Кнопка слева в выделенном состоянии с TouchListener и clickListener - PullRequest
1 голос
/ 10 ноября 2019

У меня проблема с тем, что моя Кнопка остается в подсвеченном состоянии, после выполнения следующих действий:

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

    }
}

Что касается приведенного выше кода, при его использовании я ожидаю, что нажатие кнопки будетобрабатываются касанием и возвращая «true», обработка должна останавливаться на touchListener.

Но это не тот случай. Кнопка остается в подсвеченном состоянии, даже если щелчок вызывается.

Что я получаю:

Test - calling onClick
Test - Performing click

с другой стороны, если я использую следующий код,нажата кнопка, то же самое печатается, но кнопка не застревает в подсвеченном состоянии:

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

    }
}

Я немного запутался относительно того, что представляет собой цепочка респондента на событие касания. Я предполагаю, что это:

1) TouchListener

2) ClickListener

3) ParentViews

Может ли кто-нибудь это также подтвердить?

Ответы [ 3 ]

2 голосов
/ 13 ноября 2019

Такие настройки не требуют программных изменений. Вы можете сделать это просто в xml файлах. Прежде всего, полностью удалите метод setOnTouchListener, который вы указали в onCreate. Затем определите цвет селектора в каталоге res/color, как показано ниже. (если каталог не существует, создайте его)

res / color / 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>

Теперь установите для него значение app:backgroundTintатрибут:

<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" />

Визуальный результат:

enter image description here

РЕДАКТИРОВАНИЕ: (для решения проблемы сенсорного события)

С общей точки зрения поток сенсорного события начинается с Activity, а затем направляется вниз к макету (изродительский для дочерних макетов), а затем к представлениям. (Поток LTR на следующем рисунке)

enter image description here

Когда событие касания достигает целевого представления, представление может обработать событие, а затем принять решение передать егопредыдущие макеты / действия или нет (возвращая false из true в onTouch метод). (RTL-поток на рисунке выше)

Теперь давайте взглянем на исходный код View , чтобы получить более глубокое понимание потоков сенсорных событий. Посмотрев на реализацию dispatchTouchEvent, мы увидим, что если вы установите OnTouchListener для представления, а затем вернете true в его методе onTouch, onTouchEvent представления не будет вызвано.

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

Теперь рассмотрим метод onTouchEvent, где действие события равно MotionEvent.ACTION_UP. Мы видим, что там происходит действие «выполнить щелчок». Таким образом, возвращая true в OnTouchListener onTouch и, следовательно, не вызывая onTouchEvent, вы не вызываете OnClickListener * onClick.

. вызов onTouchEvent, который относится к нажатому состоянию, и вы упомянули вопрос. Как видно из приведенного ниже блока кода, есть экземпляр UnsetPressedState, который вызывает 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);
    }
}

Что касается приведенных выше описаний, вы можете изменить код, вызвав setPressed(false) самостоятельно, чтобы изменить состояние рисования, в котором действие события равно 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;
    }
});
0 голосов
/ 12 ноября 2019

Вы возитесь с событиями touch и focus. Давайте начнем с понимания поведения с тем же цветом. По умолчанию Selector назначено в качестве фона для Button в Android. Так что, просто меняя цвет фона, make остается статичным (цвет не изменится). Но это не родное поведение.

Selector может выглядеть так.

<?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>

Как вы можете видеть выше, есть состояние focused и состояние pressed. Установив onTouchListener, вы будете обрабатывать сенсорные события, которые не имеют ничего общего с focus.

Selector кнопки должен заменить событие focus на touch во время события нажатия кнопки. Но в первой части вашего кода вы перехватили события для touch (возвращая true из обратного вызова). Изменение цвета не может продолжаться дальше и замерзает тем же цветом. И вот почему второй вариант (без перехвата) работает нормально, и это ваше замешательство.

ОБНОВЛЕНИЕ

Все, что вам нужно сделать, это изменить поведение и цвет для Selector. Напримериспользуя следующий фон для Button. И удалить onTouchListener из вашей реализации вообще.

<?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>
0 голосов
/ 12 ноября 2019

если вы назначите фон кнопке, он не изменит цвет при нажатии.

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

и установит его в качестве фона для вашей кнопки

android:background="@color/myColor"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...