博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
从JEditorPane入手,分析其中的MVC模式
阅读量:6430 次
发布时间:2019-06-23

本文共 7093 字,大约阅读时间需要 23 分钟。

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
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 
void add(Class
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

你可能感兴趣的文章
Python正则表达式初识(十)附正则表达式总结
查看>>
由event target引发的关于事件流的一连串思考(一)
查看>>
JQuery教程
查看>>
java动态代理
查看>>
模拟长按事件
查看>>
三篇文章带你极速入门php(二)之迅速搭建php环境
查看>>
express express-session 小书
查看>>
长文慎入-探索Java并发编程与高并发解决方案
查看>>
Spring Boot 的 10 个核心模块
查看>>
Redis中的五种数据类型简介
查看>>
网易云瀚海一体机,云计算“全栈”航母带来了什么?
查看>>
Swoole 源码分析——Client模块之Recv
查看>>
分布式系统中处理参数配置的 4 种方案
查看>>
Spring Cloud (1)
查看>>
二叉搜索树的实现与常见用法
查看>>
APICLOUD 1.1.0 开发环境搭建
查看>>
React通用Web应用框架-概述
查看>>
服务器 ssl 配置
查看>>
【freemaker实现导出word②】代码实现导出word(包括导出list数据和导出图片到word)...
查看>>
vue vue-router vuex element-ui axios学习笔记(六)在项目中使用MongoDB完成注册
查看>>