当前位置: 首页 > news >正文

从JEditorPane入手,分析其中的MVC模式

JEditorPane的包级关系

java.lang.Object
    java.awt.Component
        java.awt.Container
            javax.swing.JComponent
                javax.swing.text.JTextComponent
                    javax.swing.JEditorPane
                        javax.swing.JTextPane
复制代码

JEditorPane的介绍

它是用于编辑各种内容的文本组件。该组件使用EditorKit的EditorKit来完成其行为。它有效地转化为适当的文本编辑器,用于提供给他们的内容。 编辑器在任何给定时间绑定的内容类型由当前EditorKit EditorKit确定。 如果内容设置为新的URL,则其类型用于确定应用于加载内容的EditorKit 。

model

这里的model就是document

 private Document initializeModel(EditorKit kit, URL page) {
        Document doc = kit.createDefaultDocument();
        if (pageProperties != null) {
            // transfer properties discovered in stream to the
            // document property collection.
            for (Enumeration<String> e = pageProperties.keys(); e.hasMoreElements() ;) {
                String key = e.nextElement();
                doc.putProperty(key, pageProperties.get(key));
            }
            pageProperties.clear();
        }
        if (doc.getProperty(Document.StreamDescriptionProperty) == null) {
            doc.putProperty(Document.StreamDescriptionProperty, page);
        }
        return doc;
    }
复制代码

创建了一个初始化model,然后在对page中的属性添加进model,现在就是一个有内容的Document了。

view

public View create(Element elem) {
            Document doc = elem.getDocument();
            Object i18nFlag
                = doc.getProperty("i18n"/*AbstractDocument.I18NProperty*/);
            if ((i18nFlag != null) && i18nFlag.equals(Boolean.TRUE)) {
                // build a view that support bidi
                return createI18N(elem);
            } else {
                return new WrappedPlainView(elem);
            }
        }

        View createI18N(Element elem) {
            String kind = elem.getName();
            if (kind != null) {
                if (kind.equals(AbstractDocument.ContentElementName)) {
                    return new PlainParagraph(elem);
                } else if (kind.equals(AbstractDocument.ParagraphElementName)){
                    return new BoxView(elem, View.Y_AXIS);
                }
            }
            return null;
        }

复制代码

根据前面创建的document的结构形成相应视图框架

model to view

这里是默认继承父类JTextComponent的modelToView()来实现

public Rectangle modelToView(int pos) throws BadLocationException {
    return getUI().modelToView(this, pos);
}                                                                               
复制代码

将model中的给定位置转换为view坐标系中的位置。pos必须是正数。

首先先看几个继承关系

java.lang.Object
    javax.swing.plaf.ComponentUI
        javax.swing.plaf.TextUI
            javax.swing.plaf.basic.BasicTextUI
复制代码
javax.swing.JComponent
    javax.swing.text.JTextComponent
复制代码

JTextComponent中的两个方法

 public void setUI(TextUI ui) {
    super.setUI(ui);
}
复制代码
public TextUI getUI() { return (TextUI)ui; }
复制代码

这里的TextUI是ComponentUI类型,也就是这里的modelToView依然是一个继承方法,具体实现在BasicTextUi中,代码如下

public Rectangle modelToView(JTextComponent tc, int pos, Position.Bias bias) throws BadLocationException {
        Document doc = editor.getDocument();
        if (doc instanceof AbstractDocument) {
            ((AbstractDocument)doc).readLock();
        }
        try {
            Rectangle alloc = getVisibleEditorRect();
            if (alloc != null) {
                rootView.setSize(alloc.width, alloc.height);
                Shape s = rootView.modelToView(pos, alloc, bias);
                if (s != null) {
                  return s.getBounds();
                }
            }
        } finally {
            if (doc instanceof AbstractDocument) {
                ((AbstractDocument)doc).readUnlock();
            }
        }
        return null;
    }
复制代码

这里的getVisibleEditorRect()其实是初始化一个rectangle,具体代码如下

protected Rectangle getVisibleEditorRect() {
        Rectangle alloc = editor.getBounds();
        if ((alloc.width > 0) && (alloc.height > 0)) {
            alloc.x = alloc.y = 0;
            Insets insets = editor.getInsets();
            alloc.x += insets.left;
            alloc.y += insets.top;
            alloc.width -= insets.left + insets.right;
            alloc.height -= insets.top + insets.bottom;
            return alloc;
        }
        return null;
    }
复制代码

BasicTextUI中的modelToView()

public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
            if (view != null) {
                return view.modelToView(pos, a, b);
            }
            return null;
        }
复制代码

这里的rootView是继承自View,View中的modelToView()

public abstract Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException;
复制代码

controller

在JEditorPane中

 public synchronized void addHyperlinkListener(HyperlinkListener listener) {
    listenerList.add(HyperlinkListener.class, listener);
}
复制代码

这里的listenerList是JEditorPane中继承JComponent得到的

JComponent中

protected EventListenerList listenerList = new EventListenerList();
复制代码

至于EventListenerList

单个实例可用于使用列表来容纳所有实例的所有侦听器(所有类型的所有类型)。 使用EventListenerList的类的责任是提供类型安全的API(最好符合JavaBean规范)以及将事件通知方法分配给列表中适当的事件侦听器的方法。 这个类提供的主要好处是它在没有听众的情况下相对便宜,并且在一个地方提供了事件监听器列表的序列化,以及一定程度的MT安全性(正确使用时)。

public synchronized <T extends EventListener> void add(Class<T> t, T l) {
        if (l==null) {
            // In an ideal world, we would do an assertion here
            // to help developers know they are probably doing
            // something wrong
            return;
        }
        if (!t.isInstance(l)) {
            throw new IllegalArgumentException("Listener " + l +
                                         " is not of type " + t);
        }
        if (listenerList == NULL_ARRAY) {
            // if this is the first listener added,
            // initialize the lists
            listenerList = new Object[] { t, l };
        } else {
            // Otherwise copy the array and add the new listener
            int i = listenerList.length;
            Object[] tmp = new Object[i+2];
            System.arraycopy(listenerList, 0, tmp, 0, i);

            tmp[i] = t;
            tmp[i+1] = l;

            listenerList = tmp;
        }
    }
复制代码

但是仅仅有listener是不够的,还需要有“fire”方法,下面是一个例子

 EventListenerList listenerList = new EventListenerList();
 FooEvent fooEvent = null;

 public void addFooListener(FooListener l) {
     listenerList.add(FooListener.class, l);
 }

 public void removeFooListener(FooListener l) {
     listenerList.remove(FooListener.class, l);
 }


 // Notify all listeners that have registered interest for
 // notification on this event type.  The event instance
 // is lazily created using the parameters passed into
 // the fire method.
 
 protected void fireFooXXX() {
     // Guaranteed to return a non-null array
     Object[] listeners = listenerList.getListenerList();
     // Process the listeners last to first, notifying
     // those that are interested in this event
     for (int i = listeners.length-2; i>=0; i-=2) {
         if (listeners[i]==FooListener.class) {
             // Lazily create the event:
             if (fooEvent == null)
                 fooEvent = new FooEvent(this);
             ((FooListener)listeners[i+1]).fooXXX(fooEvent);
         }
     }
 } 
复制代码

fire这个方法一般在实现在EventListenerList没有,但是在Component中有体现,这点可能是为了减少代码的耦合度。

到此简单的MVC模式介绍完了。

下一个问题是如何将Shape通过paint方法“画”到计算机界面。

转载于:https://juejin.im/post/5cde4b3b51882525be132a7f

相关文章:

  • mmc控制台,访问不了目标主机
  • 网易有道——招聘
  • 使用IntelliJ IDEA 配置Maven
  • 【BZOJ】1082: [SCOI2005]栅栏(二分+dfs)
  • magento Mage custom helper not found
  • 通过递归组合多维数组!
  • 95th percentile concentration
  • 用oracle查询当前数据库中的所有表
  • Node图文教程(第四章:express)
  • ArcGIS查找空洞多边形
  • ARC066E/AtCoder2273 Addition and Subtraction Hard
  • 源代码管理工具 ——Github的介绍与简要教程
  • 关于MFLAGS与MAKEFLAGS
  • Linux下的nexus数据迁移
  • windows下安装memcached
  • ----------
  • [分享]iOS开发-关于在xcode中引用文件夹右边出现问号的解决办法
  • Android开源项目规范总结
  • FastReport在线报表设计器工作原理
  • HTTP那些事
  • idea + plantuml 画流程图
  • Java读取Properties文件的六种方法
  • NSTimer学习笔记
  • python_bomb----数据类型总结
  • Quartz初级教程
  • SAP云平台运行环境Cloud Foundry和Neo的区别
  • vue2.0一起在懵逼的海洋里越陷越深(四)
  • windows下如何用phpstorm同步测试服务器
  • 关于extract.autodesk.io的一些说明
  • 关于字符编码你应该知道的事情
  • 汉诺塔算法
  • 前端相关框架总和
  • 融云开发漫谈:你是否了解Go语言并发编程的第一要义?
  • 如何胜任知名企业的商业数据分析师?
  • 使用SAX解析XML
  • 为什么要用IPython/Jupyter?
  • 用Python写一份独特的元宵节祝福
  • 06-01 点餐小程序前台界面搭建
  • 好程序员web前端教程分享CSS不同元素margin的计算 ...
  • #WEB前端(HTML属性)
  • (Arcgis)Python编程批量将HDF5文件转换为TIFF格式并应用地理转换和投影信息
  • (LeetCode 49)Anagrams
  • (九)One-Wire总线-DS18B20
  • (十五)使用Nexus创建Maven私服
  • (四) Graphivz 颜色选择
  • (续)使用Django搭建一个完整的项目(Centos7+Nginx)
  • (转)Android中使用ormlite实现持久化(一)--HelloOrmLite
  • (转)C#开发微信门户及应用(1)--开始使用微信接口
  • (转)从零实现3D图像引擎:(8)参数化直线与3D平面函数库
  • .Family_物联网
  • .NET 5.0正式发布,有什么功能特性(翻译)
  • .Net IOC框架入门之一 Unity
  • .NET 服务 ServiceController
  • .NET 简介:跨平台、开源、高性能的开发平台
  • @RequestMapping处理请求异常