(九)IntelliJ 插件开发—— List and Tree Controls(列表和树状)

官方文档

https://www.jetbrains.org/intellij/sdk/docs/user_interface_components/lists_and_trees.html

Github

https://github.com/kungyutucheng/my_gradle_plugin

运行环境

macOS 10.14.5
IntelliJ idea 2019.2.4

图形素材来源

https://www.iconfont.cn/collections/detail?spm=a313x.7781069.1998910419.de12df413&cid=16957

定义

相比JList,JBList出了拥有JList相关功能,还具备以下几个优势:

  • 元素内容长度超过组件宽度时,可以以tooltip的形式展示完整内容
  • 没有元素时,可以通过调用getEmptyText().setText()来设置默认展示文本
  • 可以通过调用setPaintBusy()展示一个icon在组件右上角,以显示当前有后台运行任务

相比JTree,Tree则多了拖拉等功能

JBList

效果

初始效果
查找模式
AddAction
ExtraAction

Demo

ListAction

package com.kungyu.jblist;

import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import org.jetbrains.annotations.NotNull;

/**
 * @author wengyongcheng
 * @since 2020/3/5 11:23 下午
 */
public class ListAction extends AnAction {

    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        new ListDialogWrapper().showAndGet();
    }
}

注册Action

<action id="com.kungyu.jblist.ListAction" class="com.kungyu.jblist.ListAction"
                text="ListAction" description="ListAction">
    <add-to-group group-id="ToolsMenu" anchor="after" relative-to-action="com.kungyu.file.chooser.FileChooserAction"/>
</action>

ListDialogWrapper

package com.kungyu.jblist;

import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.ValidationInfo;
import com.intellij.ui.ColoredListCellRenderer;
import com.intellij.ui.ListSpeedSearch;
import com.intellij.ui.ToolbarDecorator;
import com.intellij.ui.components.JBList;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.awt.*;

/**
 * @author wengyongcheng
 * @since 2020/3/6 11:55 下午
 */
public class ListDialogWrapper extends DialogWrapper {

    private JBList<Integer> list;

    private DefaultListModel<Integer> defaultListModel;

    public ListDialogWrapper(){
        super(true);
        init();
        setTitle("JBList");
    }

    @Nullable
    @Override
    protected JComponent createCenterPanel() {

        defaultListModel = new DefaultListModel<>();
        for (int i = 0;i < 10; i++) {
            defaultListModel.addElement(i);
        }
        list = new JBList<>(defaultListModel);

        // 修饰每一行的元素
        ColoredListCellRenderer<Integer> coloredListCellRenderer = new ColoredListCellRenderer<Integer>() {
            @Override
            protected void customizeCellRenderer(@NotNull JList<? extends Integer> list, Integer value, int index, boolean selected, boolean hasFocus) {
                append(value + "-suffix");
            }
        };
        list.setCellRenderer(coloredListCellRenderer);

        // 触发快速查找
        new ListSpeedSearch<>(list);

        // 增加工具栏(新增按钮、删除按钮、上移按钮、下移按钮)
        ToolbarDecorator decorator = ToolbarDecorator.createDecorator(list);
        // 新增元素动作
        decorator.setAddAction(actionButton -> addAction());
        // 自定义按钮
        decorator.addExtraAction(new ExtraButtonAction(defaultListModel,list));

        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());
        panel.add(decorator.createPanel(), BorderLayout.CENTER);
        return panel;
    }

    private void addAction() {
        String newItem = Messages.showInputDialog("Input A Item", "Add", Messages.getInformationIcon());
        if (StringUtils.isNotBlank(newItem)) {
            defaultListModel.addElement(Integer.valueOf(newItem));
        }
    }


    @Nullable
    @Override
    protected ValidationInfo doValidate() {
        Integer value = list.getSelectedValue();
        if (value != null) {
            Messages.showMessageDialog(value + "",value + "", Messages.getInformationIcon());
        }
        return null;
    }
}

ExtraButtonAction

package com.kungyu.jblist;

import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.IconLoader;
import com.intellij.ui.AnActionButton;
import com.intellij.ui.components.JBList;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;

/**
 * @author wengyongcheng
 * @since 2020/3/7 9:38 下午
 */
public class ExtraButtonAction extends AnActionButton {

    private JBList list;

    private DefaultListModel<Integer> model;

    public ExtraButtonAction(DefaultListModel<Integer> model,JBList list) {
        super("Extra", IconLoader.getIcon("/icons/edit.svg"));
        this.model = model;
        this.list = list;
    }


    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        int index = list.getSelectedIndex();
        String newValue = Messages.showInputDialog(model.get(index) + "", "Edit", Messages.getInformationIcon());
        if (StringUtils.isNotBlank(newValue)) {
            model.add(index, Integer.valueOf(newValue));
        }
    }
}

其中,数据集合还可以使用SortedListModel等,也可以继承AbstractListModel来自定义,若是使用自定义数据集合,新增元素时需触发以下方法

    /**
     * <code>AbstractListModel</code> subclasses must call this method
     * <b>after</b>
     * one or more elements are added to the model.  The new elements
     * are specified by a closed interval index0, index1 -- the enpoints
     * are included.  Note that
     * index0 need not be less than or equal to index1.
     *
     * @param source the <code>ListModel</code> that changed, typically "this"
     * @param index0 one end of the new interval
     * @param index1 the other end of the new interval
     * @see EventListenerList
     * @see DefaultListModel
     */
    protected void fireIntervalAdded(Object source, int index0, int index1)
    {
        Object[] listeners = listenerList.getListenerList();
        ListDataEvent e = null;

        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == ListDataListener.class) {
                if (e == null) {
                    e = new ListDataEvent(source, ListDataEvent.INTERVAL_ADDED, index0, index1);
                }
                ((ListDataListener)listeners[i+1]).intervalAdded(e);
            }
        }
    }

SortedListModel为例,其添加元素的方法是这样的:

  private void add(int index, T item) {
    myItems.add(index, item);
    fireIntervalAdded(this, index, index);
  }

Tree

效果

Tree

Demo

TreeAction

package com.kungyu.tree;

import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import org.jetbrains.annotations.NotNull;

/**
 * @author wengyongcheng
 * @since 2020/3/7 10:21 下午
 */
public class TreeAction extends AnAction {
    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        new TreeDialogWrapper().showAndGet();
    }
}

注册Action

<action id="com.kungyu.tree.TreeAction" class="com.kungyu.tree.TreeAction"
                text="TreeAction" description="TreeAction">
    <add-to-group group-id="ToolsMenu" anchor="after" relative-to-action="com.kungyu.jblist.ListAction"/>
</action>

TreeDialogWrapper

package com.kungyu.tree;

import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.ui.ToolbarDecorator;
import com.intellij.ui.TreeSpeedSearch;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.ui.EditableTreeModel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.util.Collection;

/**
 * @author wengyongcheng
 * @since 2020/3/7 10:22 下午
 */
public class TreeDialogWrapper extends DialogWrapper {

    private Tree tree;

    private DefaultTreeModel model;

    public TreeDialogWrapper() {
        super(true);
        init();
        setTitle("Tree");
    }
    @Nullable
    @Override
    protected JComponent createCenterPanel() {
        // 创建节点
        DefaultMutableTreeNode child1Leaf1 = new DefaultMutableTreeNode();
        child1Leaf1.setUserObject("child1Leaf1");

        DefaultMutableTreeNode child1 = new DefaultMutableTreeNode();
        child1.add(child1Leaf1);
        child1.setUserObject("child1");

        DefaultMutableTreeNode child1Leaf2 = new DefaultMutableTreeNode();
        child1Leaf2.setUserObject("child1Leaf2");

        DefaultMutableTreeNode child2 = new DefaultMutableTreeNode();
        child2.add(child1Leaf2);
        child2.setUserObject("child2");

        DefaultMutableTreeNode root = new DefaultMutableTreeNode();
        root.setUserObject("root");
        root.add(child1);
        root.add(child2);

        // 创建数据模型
        model = new DefaultTreeModel(root);
        tree = new Tree(model);
        tree.setDragEnabled(true);
        tree.setExpandableItemsEnabled(true);

        // 快速查找
        new TreeSpeedSearch(tree);

        // 自定义样式
        ColoredTreeCellRenderer coloredTreeCellRenderer = new ColoredTreeCellRenderer() {
            @Override
            public void customizeCellRenderer(@NotNull JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
                append(value + "-suffix");

            }
        };
        tree.setCellRenderer(coloredTreeCellRenderer);

        // 工具栏
        ToolbarDecorator toolbarDecorator = ToolbarDecorator.createDecorator(tree);
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());
        panel.add(toolbarDecorator.createPanel(), BorderLayout.CENTER);
        return panel;
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容