Jface TreeViewer分类 - 更新
从在jface treeviewer中进行排序行当我(如该页面上的问题)时,我的比较器不起作用,我只是想按标签提供商进行排序。
我的treeViewer
在我的应用程序中使用的一些通用功能有些扩展。然后在3个派生范围内将其扩展,但是从本质上讲,它是抽象类的示例是因为它不是直接实例化的,并且Eclipse插件代码的其余部分必须为各种视图零件选择具体实现。但是,我需要在所有这些功能中分类功能,因此这可能是应该去的地方。
我匿名化了下面的代码,但本质上是treeViewer
的抽象包装器,在所有派生中都有3列。第一列(索引0)始终是某种类型的树,因此节点是可扩展的,并且在第二列和第三列中产生更明显的数据行(索引1和2)。这些列仅包含文本数据。
因此,我希望实现的是一个可排序的控件,其中单击带有索引0的标头列将清除任何排序并在最初加载数据时渲染数据,而单击任何其他标头的单击都将执行以下操作:
- 分类(UP)如果 则已经不是排序列
- 如果重复单击相同的列,
转换方向,这是我尝试过的,从本文顶部的链接中提到的viewercomparator类开始:
public abstract class MyTreeViewer extends TreeViewer {
public static final int ACTIVE_TOTAL_COLUMN_WIDTH = 120;
public static final int FIRST_COLUMN_WIDTH = 180;
private static final String SPECIAL_FIELD = "specialField";
private TreeColumn sortColumn = null;
final RMService rmService;
final PService pService;
public MyTreeViewer(Composite parent) {
this(parent, SWT.V_SCROLL | SWT.H_SCROLL | SWT.FULL_SELECTION);
}
public MyTreeViewer(Composite parent, int style) {
super(parent, style);
this.rmService = UIActivator.getDefault().getInjector().getInstance(RMService.class);
this.pService = UIActivator.getDefault().getInjector().getInstance(PService.class);
this.setUseHashlookup(true);
this.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
if (!event.getSelection().isEmpty()) {
Tree tree = getTree();
if (tree != null) {
List<MyTreeItem> treeItems = Lists.newArrayList();
for (int i = 0; i < tree.getSelectionCount(); i++) {
TreeItem item = tree.getSelection()[i];
Object obj = item.getData();
if (obj instanceof MyTreeItem) {
treeItems.add((MyTreeItem) obj);
}
}
handleSelectionChanged(treeItems);
}
}
}
});
this.setComparator(new ViewerComparator());
}
protected abstract void handleSelectionChanged(Collection<MyTreeItem> treeItemList);
public void initTree(List<ViewField> fields, IMemento memento) {
TableLayout tableLayout = new TableLayout();
Tree tree = getTree();
for (ViewField field : fields) {
TreeViewerColumn column = new TreeViewerColumn(this, SWT.NONE);
if (SystemPropertiesLoader.OPERATING_SYSTEM_NAME.equalsIgnoreCase(IConstants.LINUX_OS)) {
column.getColumn().setResizable(false);
column.getColumn().setMoveable(false);
} else {
column.getColumn().setResizable(true);
column.getColumn().setMoveable(true);
}
column.getColumn().setData(SPECIAL_FIELD, field);
column.getColumn().setText(field.getFieldName());
tableLayout.addColumnData(new ColumnPixelData(field.getWidth(), false));
column.getColumn().addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent selectionEvent) {
if (selectionEvent.getSource() instanceof TreeColumn) {
TreeColumn column = (TreeColumn)selectionEvent.getSource();
Tree tree = getTree();
if (column.getText().equalsIgnoreCase("") && sortColumn != null) {
// file column clicked - use it to clear any sort order
sortColumn = null;
tree.setSortColumn(sortColumn);
tree.setSortDirection(0);
refresh();
} else {
sortColumn = column;
tree.setSortColumn(sortColumn);
tree.setSortDirection(invertSortDirection(tree.getSortDirection()));
refresh();
}
}
}
@Override
public void widgetDefaultSelected(SelectionEvent selectionEvent) {
// not currently used, but required as part of implementation of SelectionListener interface
}
});
}
tree.setLayout(tableLayout);
tree.setLinesVisible(false);
tree.setHeaderVisible(true);
tree.layout(true);
// prevent expanding/collapsing tree item on dbl click
tree.addListener(SWT.MeasureItem, new Listener() {
@Override
public void handleEvent(Event event) {
// nothing
}
});
}
private int invertSortDirection(int sortDirection) {
if (sortDirection != SWT.UP && sortDirection != SWT.DOWN) {
return SWT.UP;
} else if (sortDirection == SWT.UP) {
return SWT.DOWN;
}
return SWT.UP;
}
@Override
public void refresh() {
super.refresh();
}
@Override
public void refresh(boolean updateLabels) {
super.refresh(updateLabels);
}
}
我继承了此代码,所以有一些奇特的东西我不会在不知道它不会在质量检查中产生回归的固定错误,例如,可以实现防止在双击上扩展/折叠树项目的粗略方法。实际上,到目前为止,我修改的此特定代码的唯一部分是插入addSelectionListener
用于处理列标头单击的闭合以及invertSortDirection
方法。
当我运行此操作并单击标头时会发生什么。我看到UI Caret指示列索引1或2上的排序方向,但我看不到数据排序。单击列索引0的标题将清除排序列和顺序。如果分类数据,我希望观众在请求任何列排序之前将UI中的UI刷新为原始加载状态。
在先前的问题上(在顶部链接),我解释说,如果需要按标签进行排序,我应该只添加this.setComparator(new ViewerComparator());
line。如果我必须编写扩展viewComparator
的类,我不知道我会覆盖或更改。
上面代码中没有一个派生的类在列上实现侦听器。我可以跟踪代码,上述处理标头单击的代码确实执行。
因此,我是否需要扩展viewComparator
,如果我这样做,我应该更改它以获得所需的行为?
(我也可能可以取消treecolumn stortcolumn
字段,因为这棵树本身“记住”了
。 /strong>
我的目的是显示上面定义的这个通用查看器的派生类别,但是在我检查了这一点之后,很明显,对于当前的问题而言,很少有使用这种情况。
我发现了我认为的是为什么我从我继承了该项目的一个前任中没有出现这种类型的“吸烟枪”。这是标签提供商:
public class MyCustomLabelProvider extends LabelProvider implements ITableLabelProvider {
final IDecoratorManager decorator;
public MyCustomLabelProvider() {
decorator = PlatformUI.getWorkbench().getDecoratorManager();
}
@Override
public Image getImage(Object element) {
return super.getImage(element);
}
@Override
public String getText(Object element) {
return null;
}
@Override
public Image getColumnImage(Object element, int columnIndex) {
if (element instanceof MyTreeItem) {
if (columnIndex == 0) {
final MyTreeItem item = (MyTreeItem)element;
switch (item.getType()) {
// snipped cases returning different images
}
}
}
return null;
}
@Override
public String getColumnText(Object element, int columnIndex) {
if (element instanceof MyTreeItem) {
return showColumns((MyTreeItem) element, columnIndex);
}
return null;
}
private String showColumns(MyTreeItem element, int columnIndex) {
if (columnIndex == 0) {
return element.getName();
}
if (columnIndex == 1) {
return String.valueOf(element.getCustomProperty1());
}
if (columnIndex == 2) {
return String.valueOf(element.getCustomProperty2());
}
return "";
}
}
通过跟踪viewercomparator
代码,该程序最终调用getText
总是返回null
。
viewercomparator
仅尝试抓住标签文本,因此上述为null
,它对空字符串进行了修改。然后,它使用Java String 进行比较
方法。由于两者都是“”
,因此没有比较结果可以交换元素顺序。
我想知道将getText
方法更改为以某种方式获取单击列的原始列索引,并具有逻辑,以确定从我的基础数据对象中读取用于填充一行的基础数据对象。对我来说,这种转化为不起作用,因为我使用的基础对象具有用于填充3列中2个的非弦乐属性。
用户Greg-449在评论中指出,我需要扩展和覆盖viewercomparator
制作自己的版本,但是直到我到达这么远,或者直到他说...
标准ViewerComparator仅支持一列
...目前尚不清楚为什么。最初链接的帖子没有澄清。或至少在撰写本文时。
在他提到的那一刻,我没有指出问题已解决,只是我以为我已经找到了这个问题。我只是从viewercomparator
,跟踪运行代码开始,并找到了现有比较器无法重新排序项目的原因,假设一旦我更新代码,那可能是它的终结。
我会进一步访问Greg-449所说的话,因为即使您有一列,默认viewercomparator
也不会支持比较基础数据对象的属性不是一个元素的元素Java字符串。您需要为此实施自己的比较器。因此很清楚,为什么Greg -449建议在我一开始说我有3列 - 但是我仍然感到困惑,因为我只是以为我在我想可以分类的2列上有文本数据,即使该文本实际上是转换了从int
。我只是以为我阅读的链接帖子可能很合适,因为它提到了对文本进行排序,而没有提及单列或Java字符串数据类型的限制。
因此,继续前进,我现在已经按照自己的意愿来工作。更重要的是,我不必更改getText
方法,因为这是由默认的view> viewercomparator
专门调用的,我找到了一种不需要的方法。我将在答案中发布这些。
Linking in from Sort rows in JFace Treeviewer where I asked @greg-449 why my comparator doesn't work when (like the question on that page) I just wanted to sort by the label provider.
My TreeViewer
is somewhat extended with some generic features used in my application. There are then at 3 derivations extending it, but essentially the example below of it being an abstract class is because it's not intended to be directly instantiated, and the rest of the Eclipse plugin code must choose a concrete implementation for various view parts. However, I need sorting functionality in all of them, so this is probably where it should go.
I anonymized the code below, but it is in essence the abstract wrapper around TreeViewer
which in all derivations has 3 columns. The first column (index 0) is always a tree of some type, so the nodes are expandable and this produces more visible rows of data in the 2nd and 3rd columns (indexes 1 and 2). These columns contain text data only.
As such, what I hope to achieve is a sortable control, where clicking header column with index 0 will clear any sorting and render the data as it was initially loaded, while clicking any other headers will do the following:
- Sort Ascending (UP) if it was NOT already the sort column
- Invert sort direction if same sort column is repeatedly clicked
Here's what I have tried, starting with the ViewerComparator class mentioned in the link at he top of this post:
public abstract class MyTreeViewer extends TreeViewer {
public static final int ACTIVE_TOTAL_COLUMN_WIDTH = 120;
public static final int FIRST_COLUMN_WIDTH = 180;
private static final String SPECIAL_FIELD = "specialField";
private TreeColumn sortColumn = null;
final RMService rmService;
final PService pService;
public MyTreeViewer(Composite parent) {
this(parent, SWT.V_SCROLL | SWT.H_SCROLL | SWT.FULL_SELECTION);
}
public MyTreeViewer(Composite parent, int style) {
super(parent, style);
this.rmService = UIActivator.getDefault().getInjector().getInstance(RMService.class);
this.pService = UIActivator.getDefault().getInjector().getInstance(PService.class);
this.setUseHashlookup(true);
this.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
if (!event.getSelection().isEmpty()) {
Tree tree = getTree();
if (tree != null) {
List<MyTreeItem> treeItems = Lists.newArrayList();
for (int i = 0; i < tree.getSelectionCount(); i++) {
TreeItem item = tree.getSelection()[i];
Object obj = item.getData();
if (obj instanceof MyTreeItem) {
treeItems.add((MyTreeItem) obj);
}
}
handleSelectionChanged(treeItems);
}
}
}
});
this.setComparator(new ViewerComparator());
}
protected abstract void handleSelectionChanged(Collection<MyTreeItem> treeItemList);
public void initTree(List<ViewField> fields, IMemento memento) {
TableLayout tableLayout = new TableLayout();
Tree tree = getTree();
for (ViewField field : fields) {
TreeViewerColumn column = new TreeViewerColumn(this, SWT.NONE);
if (SystemPropertiesLoader.OPERATING_SYSTEM_NAME.equalsIgnoreCase(IConstants.LINUX_OS)) {
column.getColumn().setResizable(false);
column.getColumn().setMoveable(false);
} else {
column.getColumn().setResizable(true);
column.getColumn().setMoveable(true);
}
column.getColumn().setData(SPECIAL_FIELD, field);
column.getColumn().setText(field.getFieldName());
tableLayout.addColumnData(new ColumnPixelData(field.getWidth(), false));
column.getColumn().addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent selectionEvent) {
if (selectionEvent.getSource() instanceof TreeColumn) {
TreeColumn column = (TreeColumn)selectionEvent.getSource();
Tree tree = getTree();
if (column.getText().equalsIgnoreCase("") && sortColumn != null) {
// file column clicked - use it to clear any sort order
sortColumn = null;
tree.setSortColumn(sortColumn);
tree.setSortDirection(0);
refresh();
} else {
sortColumn = column;
tree.setSortColumn(sortColumn);
tree.setSortDirection(invertSortDirection(tree.getSortDirection()));
refresh();
}
}
}
@Override
public void widgetDefaultSelected(SelectionEvent selectionEvent) {
// not currently used, but required as part of implementation of SelectionListener interface
}
});
}
tree.setLayout(tableLayout);
tree.setLinesVisible(false);
tree.setHeaderVisible(true);
tree.layout(true);
// prevent expanding/collapsing tree item on dbl click
tree.addListener(SWT.MeasureItem, new Listener() {
@Override
public void handleEvent(Event event) {
// nothing
}
});
}
private int invertSortDirection(int sortDirection) {
if (sortDirection != SWT.UP && sortDirection != SWT.DOWN) {
return SWT.UP;
} else if (sortDirection == SWT.UP) {
return SWT.DOWN;
}
return SWT.UP;
}
@Override
public void refresh() {
super.refresh();
}
@Override
public void refresh(boolean updateLabels) {
super.refresh(updateLabels);
}
}
I inherited this code, so there are some peculiar things that fixed bugs that I won't touch without knowing it won't produce a regression in QA, such as the crude way preventing expanding/collapsing tree item on double-click is implemented. In fact, the only part of this particular code I amended thus far is the insertion of the addSelectionListener
closure for handling column header clicks, and the invertSortDirection
method.
What happens when I run this and click on the headers is as I expect. I see the UI caret indicating the sort direction on column index 1 or 2, but I do not see the data sorted. Clicking the header of column index 0 will clear the sort column and the order. If the data was sorted, I'd like the viewer to refresh in the UI to its original loaded state before any column ordering is requested.
On the prior question (linked at top), I interpreted that if sorting by label text was required, I should just add the this.setComparator(new ViewerComparator());
line. I've no idea what I would override or change if I have to write a class that extends ViewComparator
.
None of the derived classes from the above code implement a listener on a column. I can trace the code, and the above code for handling header clicks does execute.
So, do I need to extend ViewComparator
and what should I be changing in it to get the desired behaviour, if I do?
(I can also probably do away with the TreeColumn sortColumn
field since the tree itself 'remembers' this. The Google Guice injected services are used by derivations of this abstract tree viewer)
UPDATE 1:
My intention was to show a derived class of this generic viewer defined above, but after I examined this, it was clear it shows little of use for the current issue of why the sort does not occur.
I had found what I thought was the 'smoking gun' of why the sort does not occur in the custom label provider itself from one of my predecessors that I've inherited the project from. Here's the label provider:
public class MyCustomLabelProvider extends LabelProvider implements ITableLabelProvider {
final IDecoratorManager decorator;
public MyCustomLabelProvider() {
decorator = PlatformUI.getWorkbench().getDecoratorManager();
}
@Override
public Image getImage(Object element) {
return super.getImage(element);
}
@Override
public String getText(Object element) {
return null;
}
@Override
public Image getColumnImage(Object element, int columnIndex) {
if (element instanceof MyTreeItem) {
if (columnIndex == 0) {
final MyTreeItem item = (MyTreeItem)element;
switch (item.getType()) {
// snipped cases returning different images
}
}
}
return null;
}
@Override
public String getColumnText(Object element, int columnIndex) {
if (element instanceof MyTreeItem) {
return showColumns((MyTreeItem) element, columnIndex);
}
return null;
}
private String showColumns(MyTreeItem element, int columnIndex) {
if (columnIndex == 0) {
return element.getName();
}
if (columnIndex == 1) {
return String.valueOf(element.getCustomProperty1());
}
if (columnIndex == 2) {
return String.valueOf(element.getCustomProperty2());
}
return "";
}
}
Via tracing the ViewerComparator
code, the program eventually calls getText
which always returning null
.
The ViewerComparator
transpires only to attempt to grab the label text, which due to the above is null
, which it amends to an empty String. It then uses the Java String compareTo
method for the comparison. Since both are ""
, there is no comparison result to signal the elements order needs to be swapped.
I wondered about changing the getText
method to somehow obtain the original column index of the clicked column and to have logic in it to determine which property to read from my underlying data object used to populate a row in the viewer. For me, this transpired to not work because the underlying object I used has non-String properties that are used to populate 2 of the 3 columns.
User greg-449 had indicated in comments I would need to extend and override the ViewerComparator
to make my own version, but until I got this far, or until he stated ...
The standard ViewerComparator only supports one column
... it had not been clear why. The originally linked post doesn't have that clarification. Or at least, not at time of writing this.
At the point he mentioned that, I had not indicated the issue as resolved, just that I thought I had found the issue. I had simply started with the ViewerComparator
, traced running code, and found the reason the existing comparator could not re-order the items, assuming that would likely be the end of it once I update code.
I would go further to what greg-449 said, in that even if you have a single column, the default ViewerComparator
will not support comparing elements where the property of the underlying data object is not a Java String. You need to implement your own comparator for that. Thus is became clear why greg-449 suggested that at the outset of me stating I had 3 columns - but I remained confused because I simply thought I had text data on the 2 columns I wanted to be sortable, even if that text is actually converted from an int
. I just thought the linked post I read was probably apt because it mentioned sorting on text, without mentioning the limitation of a single column, or Java String data types.
So moving on, I have now got my code working as I wanted. More importantly, I did not have to change the getText
method, since this was called specifically by the default ViewerComparator
and I found an approach to follow where this wasn't needed. I shall be posting these in the answer.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
因此,除了我在问题中的更新之外……
我试图找到GREG-449引用的其他比较器的示例,但是如果我尝试导入它,则在我的环境中不可用。
然后,我搜索了编写自定义比较器并找到Vogella帖子的方法:
https://www.vogella.com/教程/eclipsejfacetable/acrate.html#-sort-content-of-collumns
我注意到上面提到的列索引问题是通过在Vogella代码中的每个列中创建处理程序来处理的。我主要是复制它的,对其执行比较的方式有所更改以满足我的要求:
可以看出,我们有一个标准
比较
函数,该函数返回负数,匹配时为零或正面数字,指示哪个命令应在。或按照标准Javadoc所述:就在我的版本中,
比较
是从我的数据对象中使用int
值。除此之外,如果用户单击了违约列的列标题为列索引0,我希望它清除任何形式并返回到最初加载的格式。
为此,我只是更改了我最初在
myTreeViewer
中使用的selectionListener
(请参阅有问题的第二个代码列表)每列使用以下vogella示例(再次调整):if(index == 0)
在上面的检查中检查时,请在我的其他列标头上清除我的排序点击。由于列索引与如何设置列有关,因此不会在UI中具有可移动列的影响。您可以在没有有害效果的情况下更改可见的列排序。从我学到的知识中,这只是比较工具性元素并且需要在自定义比较器中覆盖的元素的逻辑。除此之外,唯一要识别的是,您希望通过实现
selectapter
接口并将其添加到列中,同时也能够跟踪列索引,从而希望将比较器专门连接到每一列。 Vogella链接确实显示了这一点。基本上,您会在比较器上看到setColumn
方法,并且在selectionAdapter
中使用此列来设置要在比较>中测试的列索引< /代码>。
So in addition to my updates in the question...
I tried to find the example of a different comparator that greg-449 referenced, but the specific package was not available in my environment if I tried to import it.
I then searched for a means to writing a custom comparator and found a Vogella post:
https://www.vogella.com/tutorials/EclipseJFaceTable/article.html#sort-content-of-table-columns
I noticed that the column index issue I mentioned above was handled by creating a handler per column in the Vogella code. I mostly copied it, with slight changes to how it performs the comparison to meet my requirements:
As can be seen, we have a standard
compare
function that returns a negative number, zero when matching, or a positive number, that indicates which order two items should be in. Or as the standard JavaDoc states:Just in my version, the
compare
is usingint
values from my data object instead.In addition to this, if the user clicked the column header of the column that by default is column index 0, I wanted it to clear any sort and return to the format the viewer was initially loaded as.
To achieve this, I simply changed the
SelectionListener
I originally used inMyTreeViewer
(see second code listing in question) for each column, and made it use the following from the Vogella example (again tweaked):The
if (index == 0)
check in the above, clears the sorting for me when true, otherwise sorts on the whichever other column header has been clicked. Since the column index pertains to how the columns were set up, they are not affected by having movable columns in the UI. You can change the visible column ordering without deleterious effects.From what I've learnt, it's only the logic for comparing elements that is instrumental and would need overriding in a custom comparator. Outside of this, the only thing to recognize is you then want the comparator specifically wired to each column, by implementing the
SelectionAdapter
interface and adding this to the column, while also being able to track the column indexes. The Vogella link does show this. Basically you will see on the comparator that there exists asetColumn
method, and this is used in theSelectionAdapter
to set the column index to be tested in thecompare
.