You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用RxJava实现矩阵键盘输入的最近4值联动七段显示屏并超时清屏?

解决方案:用RxJava组合操作符实现动态显示屏更新

这个需求刚好是RxJava擅长处理的场景——我们可以通过组合几个核心操作符来维护按键状态、处理超时,同时动态更新显示屏。先直接上代码,再拆解逻辑:

import io.reactivex.rxjava3.core.Observable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

// 定义简单的事件类,区分按键事件和清屏事件
static class KeyEvent {
    final char character;
    KeyEvent(char c) { this.character = c; }
}

static class ClearScreenEvent {}

public void setupKeypadAndDisplay() {
    KeypadController kbc = new KeypadController();
    SevenSegmentController segment = new SevenSegmentController();

    // 第一步:处理键盘输入,防抖+去重,确保每个按键只触发一次
    Observable<KeyEvent> keyEvents = kbc.keypresses
        .debounce(250, TimeUnit.MILLISECONDS)
        .distinctUntilChanged()
        .map(KeyEvent::new);

    // 第二步:合并按键事件和超时清屏事件,超时后自动重启监听
    Observable<Object> combinedEvents = keyEvents
        .timeout(1000, TimeUnit.MILLISECONDS, Observable.just(new ClearScreenEvent()))
        .repeatWhen(completed -> completed); // 超时后重新订阅,继续接收新按键

    // 第三步:用scan维护当前显示的字符序列,处理清屏和超过4位的情况
    combinedEvents
        .scan(new ArrayList<Character>(), (currentChars, event) -> {
            if (event instanceof ClearScreenEvent) {
                // 触发清屏,返回空序列
                segment.clearScreen();
                return new ArrayList<>();
            } else if (event instanceof KeyEvent) {
                char newChar = ((KeyEvent) event).character;
                if (currentChars.size() >= 4) {
                    // 已经满4位,清屏后开始新序列
                    segment.clearScreen();
                    ArrayList<Character> newSequence = new ArrayList<>();
                    newSequence.add(newChar);
                    return newSequence;
                } else {
                    // 添加新字符到序列末尾
                    ArrayList<Character> updatedSequence = new ArrayList<>(currentChars);
                    updatedSequence.add(newChar);
                    return updatedSequence;
                }
            }
            return currentChars;
        })
        .filter(chars -> !chars.isEmpty()) // 过滤空序列,避免无效更新
        .subscribe(displayChars -> {
            // 第四步:根据当前序列更新显示屏
            // 序列顺序:[第一个输入的字符, 第二个, ..., 最新输入的字符]
            // 对应显示屏位置:长度1→位置0;长度2→位置1(旧)、0(新);以此类推
            for (int i = 0; i < displayChars.size(); i++) {
                int displayPosition = (displayChars.size() - 1) - i;
                segment.writeDigit(displayPosition, displayChars.get(i));
            }
        });
}

关键逻辑拆解

  1. 防抖与去重:
    用debounce(250, TimeUnit.MILLISECONDS)过滤短时间内的重复按键触发,再加上distinctUntilChanged()防止长按导致的重复输出,确保每个按键只产生一次有效事件。

  2. 超时清屏处理:
    通过timeout()操作符,当1000ms内没有新按键时,自动发出ClearScreenEvent。repeatWhen()用来在超时后重新订阅按键流,保证后续按键还能被监听。

  3. 状态维护(核心):
    scan()操作符是这里的关键——它会持续维护一个状态(当前显示的字符序列),每次新事件进来时更新状态并输出。我们在这里处理两种情况:

    • 收到清屏事件:清空序列并调用显示屏清屏方法;
    • 收到按键事件:如果序列已经满4位,先清屏再启动新序列;否则把新字符添加到序列末尾。
  4. 显示屏更新:
    每次拿到更新后的字符序列,我们根据序列长度计算每个字符对应的显示屏位置:比如序列是[7,6],那么7对应位置1,6对应位置0,刚好实现你要的“新字符在右侧,旧字符左移”的效果。

关于你提到的ReplaySubject

你说的ReplaySubject.createWithTimeAndSize()确实能保留最近N个且在指定时间内的事件,但它的定位是给新订阅者回放历史事件,而不是持续输出当前的状态序列。我们的需求是每次新按键进来时,都要基于当前状态更新并输出新的序列,scan()才是更适合的工具——它能持续维护状态,并且每次都输出最新的状态供订阅者处理。

如果你的需求是要持续输出最近4个2000ms内的字符(比如每个字符单独输出,但只保留符合条件的),也可以用buffer(4, 1, TimeUnit.MILLISECONDS, 2000),但这种方式没法处理“满4位清屏”的自定义逻辑,还是scan()的灵活性更高。

内容的提问来源于stack exchange,提问作者Thomas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 10:09:44