RxJava - как вызвать подписчика на события 4 кликов в Android

В Android у меня есть текстовое представление, определенное следующим образом:

<TextView
     android:id="@+id/text1"       
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:text="@string/hello_world"
     android:textColorHighlight="#000000"
     android:textIsSelectable="true" />

Моя цель состоит в том, что после 4 кликов я хочу начать новую деятельность. Мне нужно сделать это с помощью RXJava; это требование. Или rxAndroid, rxBinding и т.д.

Моя активность выглядит так:

public class MainActivity extends Activity implements onClickListener {
    TextView tv;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv=(TextView)findViewById(R.id.text1);
        tv.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if(v.getId() == R.id.text1){

        }
    }
}

ОБНОВЛЕНИЕ: но это всего лишь стандартный способ сделать это. Должен быть способ сделать с реактивным API rxJava.

Поэтому вместо использования onClicklistener с rxBinding я читал, что мы можем сделать это:

RxView.clicks(tv).flatMap(tv -> { 
        // another observable which can throw onError. 
        return Observable.error(null); 
    }).subscribe(object -> { 
        Log.d("CLICK", "textview clicked", start activity);
    }, error -> { 
        Log.d("CLICK", "ERROR");
    });

Могу ли я как-то использовать команду в rxBinding, чтобы она выполнялась только после 4 кликов? Я не собираюсь хранить статическую переменную или использовать анонимный класс и хранить в нем переменную-член для подсчета. Для этого должен быть наблюдаемый метод.


person j2emanue    schedule 19.01.2016    source источник
comment
Вы пытаетесь добиться четырехкратного клика или всего 4 кликов с неопределенным временем между ними?   -  person skywall    schedule 19.01.2016
comment
четырехкратный щелчок, но я могу согласиться на 4 щелчка с ограничением по времени между ними. это работает так же хорошо.   -  person j2emanue    schedule 19.01.2016


Ответы (6)


Вы можете создать наблюдаемое значение, которое будет испускать каждый раз, когда нажимается кнопка, затем подсчитать общее количество испускаемых им значений и использовать фильтр, чтобы ваш подписчик видел его только после того, как оно было испущено четыре раза.

Observable<View> buttonObservable = ViewObservable.clicks(initiateButton, false);
buttonObservable.count()
        .filter(count -> (count >= 4))
        .subscribe(object -> {
            //Your code here
        });
person TheoKanning    schedule 19.01.2016
comment
Метод Observable.count просто возвращает количество раз, когда Observable выпускал элемент. Это часть RxJava - person TheoKanning; 19.01.2016
comment
count нельзя использовать здесь, потому что он будет испускать элемент только после того, как его наблюдаемая эмиссия завершена (rxmarbles.com/#count) наблюдаемый щелчок, с другой стороны, бесконечен - person ndori; 10.02.2016

Вы можете добиться чего-то вроде этого:

Observable.create((Observable.OnSubscribe<Void>) subscriber -> {
    if (!subscriber.isUnsubscribed()) {
        button.setOnClickListener(v -> subscriber.onNext(null));
    }
}).buffer(1000, TimeUnit.MILLISECONDS)
        .filter(clicks -> clicks.size() == 4)
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(clicks -> {
            Log.d("CLICK", "CLICK");
        });
  • buffer() - собирать клики для окна 1с
  • filter() - фильтровать все эмитированные буферизованные элементы, где количество элементов в буфере не равно 4 (отключить двойные, тройные, пятерные клики)

Самое сложное — указать «окно» буферного времени. Одной секунды иногда бывает недостаточно, но в других случаях пользователю приходится ждать несколько миллисекунд.

person skywall    schedule 19.01.2016
comment
Что такое create((Observable.OnSubscribe‹Void›? Я использую rxjava2 - person Pablo Cegarra; 01.06.2017
comment
@PabloCegarra Этот пример основан на RxJava1. Метод create() используется, когда вам нужно создать наблюдаемое от какого-то провайдера (в данном случае OnClickListener). Observable.OnSubscribe<Void> - это просто определение типа подписчика. В RxJava2 вы должны использовать Flowable. - person skywall; 01.06.2017
comment
Не могли бы вы обновить свой пост до rxjava2, пожалуйста? Я проголосую за вас, если это сработает, теперь я использую rxbinding только для этого - person Pablo Cegarra; 01.06.2017
comment
@PabloCegarra Пожалуйста, создайте новый вопрос и укажите там свои требования. Кто-нибудь взгляните на это. - person skywall; 01.06.2017

Для обнаружения нескольких кликов с разумным временем ожидания (например, 4 клика в течение 1000 мс) хорошо работает следующее:

    final int numClicks = 4;
    final int timeout = 1000;
    disposables.add(RxView.clicks(tv)
            .buffer(timeout, TimeUnit.MILLISECONDS, numClicks)
            .filter(list -> list.size() == numClicks)
            .subscribe(this::doThing));

В этом примере buffer будет либо генерировать 4 события, либо генерировать менее 4 событий каждые 1000 мс, что будет проигнорировано из-за filter

person Peter    schedule 14.11.2018
comment
это довольно хорошо, спасибо, но разве это не то же самое, что и ответ Зиги? - person j2emanue; 15.11.2018
comment
Сначала я попробовал это, и я не мог заставить его работать. Даже если бы это сработало, этот ответ имел для меня пару преимуществ: 1. вы можете сделать это в одну строку; 2. версия buffer, которая включает в себя тайм-аут, а также numClicks, отлично работает для сценария с несколькими кликами. - person Peter; 15.11.2018
comment
@Peter Это будет ждать секунды, прежде чем дать результат. Что делать, если я хочу получить результат, как только 4 клика будут сделаны в течение 1 секунды? - person Rishabh Jain; 23.05.2019
comment
@RishabhJain ты уверен? согласно документации для buffer, элементы будут создаваться, когда будет достигнуто либо timespan (время ожидания в приведенном выше примере), либо count (numClicks выше) - поэтому, когда в приведенном выше примере получено 4 клика, наблюдатель должен вызываться независимо от сколько времени прошло: reactivex.io/documentation/operators/buffer.html - person Peter; 24.05.2019

Ответ @skywall является наиболее полным, однако, если вас не волнуют временные интервалы, самым простым ответом будет использование пропустить

Observable<Void> fourClicksView = RxView.clicks(tv).skip(3); 
fourClicksView.subscribe(...);
person ndori    schedule 10.02.2016
comment
Это зависит от того, нужно ли вам именно 4 клика и не больше. Вопрос был сформулирован ровно как четверной щелчок. - person skywall; 01.06.2017
comment
Вопрос требует 4, конечно, его можно изменить по желанию, если вам нужно только одно событие, вы также можете использовать first() или take(1) впоследствии - person ndori; 03.06.2017

Используйте оператор буфера и устранения дребезга.

val clickStream = RxView.clicks(view)
    clickStream.buffer(clickStream.debounce(250, TimeUnit.MILLISECONDS))
            .map { it.size }
            .filter { it == 4 }
            .subscribe({
            })
person Zygi    schedule 13.09.2017
comment
RxView.clicks(view).share() — rxbinding работает только с последним слушателем, подробнее здесь github. com/JakeWharton/RxBinding/issues/146 - person yaroslav; 24.03.2019

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

RxView.clicks(tv)
    .map(e -> SystemClock.elapsedRealtime())
    .buffer(NUM_CLICKS, 1) //Grab every sub-sequence of however many clicks
    .filter(times -> times.size() == NUM_CLICKS) //Last values will be truncated
    .filter(times -> times.get(NUM_CLICKS - 1) - times.get(0) < ALLOWED_TIME) //check times
    .subscribe(ignore -> startActivity());
person nick allen    schedule 12.09.2017