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 (Enumeratione = 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 synchronizedvoid 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方法“画”到计算机界面。