我有一个表格和一个按钮,我想在点击按钮时用表格的选定项目发出一个事件ItemsSelected。
该按钮不应该知道该表,它应该只保留为点击流。 所以这个解决方案被丢弃了:
final ETable table = ...
PublishSubject<ItemSelected> selected = PublishSubject.create();
button.addSelectionListener(new SelectionListener(){
@Override
public void widgetSelected(SelectionEvent e) {
for (TableItem item : table.getSelection()) {
selected.onNext(new ItemSelected(item));
}
}
});
我更喜欢用表格的项目选择流来组合按钮的点击流,以便在这两个元素之间保持松耦合。
由于该表允许多项选择,因此我必须首先扫描所选项目,以便发出包含所有项目的事件。类似的东西:
public static class ItemsSelected<T> {
final List<T> items = new ArrayList<T>();
}
public abstract static class ItemSelection<T> {
public abstract void apply(ItemsSelected<T> selection);
}
public static class ItemUnselected<T> extends ItemSelection<T> {
final T item;
public ItemUnselected(T item) {
this.item = item;
}
public void apply(ItemsSelected<T> selection) {
selection.items.remove(item);
}
}
public static class ItemSelected<T> extends ItemSelection<T> {
final T item;
public ItemSelected(T item) {
this.item = item;
}
public void apply(ItemsSelected<T> selection) {
selection.items.add(item);
}
}
public static class ObservableTable<T> extends Table {
private PublishSubject<ItemSelection<T>> clicks = PublishSubject.create();
public Observable<ItemsSelected<T>> selection = clicks.scan(new ItemsSelected<T>(),
new Func2<ItemsSelected<T>, ItemSelection<T>, ItemsSelected<T>>() {
@Override
public ItemsSelected<T> call(ItemsSelected<T> t1, ItemSelection<T> t2) {
// breaking events immutability
t2.apply(t1);
return t1;
}
});
public ObservableTable(Composite parent, int style) {
super(parent, style);
this.addSelectionListener(new SelectionListener() {
@SuppressWarnings("unchecked")
@Override
public void widgetSelected(SelectionEvent e) {
if (((TableItem) e.item).getChecked())
clicks.onNext(new ItemSelected<T>((T) e.item.getData()));
else
clicks.onNext(new ItemUnselected<T>((T) e.item.getData()));
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
}
});
}
}
然后,我必须将table.selection流与selectionForAction流中的button.clicks流组合在一起。我们的想法是,当发出ButtonClick时,当且仅当先前发出了一个ItemSelected时,才会发出SelectionForAction。
-------S1--U1-----S2---S3--------- table.clicks
(scan)
-------(1)--()---(2)---(2,3)------ table.selection
----O----------O-------------O---- button.clicks
(?)
-----------------------------(2,3) selectionForAction
那么,我应该使用哪种操作?
table.selection.join(button.clicks, new Func1<ItemsSelected,Observable<Long>>() {
@Override
public Observable<Long> call(ItemsSelected t) {
// it doesn't seem a good idea
return Observable.timer(1, TimeUnit.DAYS);
}
}, new Func1<ClickEvent, Observable<Long>>() {
@Override
public Observable<Long> call(ClickEvent t) {
// this makes the ClickEvent be dropped if there is no previous ItemsSelected event emitted
return Observable.timer(1, TimeUnit.MILLISECONDS);
}
}, new Func2<ItemsSelected, ClickEvent, SelectionForAction>() {
@Override
public SelectionForActioncall(ItemsSelected t1, ClickEvent t2) {
return new SelectionForAction(t1.items);
}
});
有什么想法吗?
答案 0 :(得分:0)
我发现运营商需要以非常大的时间单位(示例中为DAYS)和非常小的(MILLISECONDS)来实现 join 行为。
variant sample以另一个Observable作为采样器,我只能在发出B事件后发出事件A.
在我的示例中,click用作采样器,并且流选择会发出我感兴趣的事件。(这还需要忽略流完成时发出的最后一个事件。)
另一种可能的解决方案是使用buffer(boundary):
点击流将充当边界,我可以避开扫描运算符,因为所选项目列表由缓冲区运算符创建。但是,通过这个解决方案,我不会考虑取消选择。
所以,有了样本我已经实现了我的原始目标,但是,我对处理项目取消选择的方式以及所选项目的最终列表不满意。 在这种情况下,我需要维护所选项目的状态,以便在发生ClickEvent时对所有项目执行某些操作。
我可以订阅项目选择/取消选择并维护所选项目的列表,但是我将失去用可观察的选择组成可观察点击的可能性。
使用扫描我维护状态并保持可观察性的可组合性,但是将当前选择列表表示为事件似乎有点强迫,实际上这代表了一个新问题:如果我选择x单击项目,然后单击按钮,正在按预期发出选择的事件,但如果未选择任何项目,也未选择新项目,然后再次单击该按钮,则不会发生任何事情。因此,似乎选择不适合作为事件。