22.5 Views


In our discussion of how Swing represents styled text, we haven't mentioned how it is actually drawn on the screen. That's where the View classes come in. They form most of the V part of the MVC architecture for text components and are responsible for rendering the text.

The way this works is a tree of View objects is created from the Document's Element tree. (Examples of this are shown in Figure 22-11.) For DefaultStyledDocuments in a JTextPane, the View tree is modeled closely on the structure of the Element tree, with almost a one-to-one mapping from Elements to Views. A PlainDocument in a JTextArea is handled more simply, with a single View object that paints the entire Element tree.

Figure 22-11. View trees created from Element trees
figs/swng2.2211.gif

Notice that the View trees have a root View above what could be considered the "natural" root. This was done to ease the implementation of the other View classes, which can now all assume that they have a non-null parent in the View tree. So that each child doesn't have to register as a DocumentListener, the root View also takes care of dispatching DocumentEvents to its children. The actual type of the root View is a package-private inner class. (You are free to create a View tree with any root you like, so this does not always need to be the case. The implementations of the TextUI.getRootView( ) method that Swing provides do return a package-private View class.)

Creation of the View tree is often handled by the text component's TextUI, but if its EditorKit returns a non-null value from its getViewFactory( ) method, this task is delegated to the factory. This gives the more complex text components (such as JEditorPane, discussed in Chapter 23) a powerful tool for customizing their appearance.

22.5.1 The ViewFactory Interface

Implementations of the ViewFactory interface determine the kind of View object that needs to be created for each Element passed in to the factory. We'll see more of the ViewFactory interface in the next chapter.

22.5.1.1 Method
public View create(Element elem)

Return a View for the given Element.

22.5.2 The View Class

This abstract class is the base class for a score of View types (see Figure 22-12) and provides basic functionality for drawing part of a Document's content. Before we cover the properties and methods of View, we should note a few potentially confusing details.

22.5.2.1 Float coordinates

The View classes follow the lead of the 2D API and use float values to specify x and y coordinates. It is usually safe to convert these to ints if you prefer.

22.5.2.2 Shape versus Rectangle

Many View methods take arguments of type java.awt.Shape. The Shape interface is implemented by many classes that represent geometric shapes, including java.awt.Rectangle. Theoretically, a View class can take advantage of nonrectangular rendering areas, but the existing View classes treat Shape objects as Rectangles. You can convert a Shape into a Rectangle (int coordinates) by calling someShape.getBounds( ), or you can convert a Shape into a Rectangle2D (float coordinates) by calling someShape.getBounds2D( ).

22.5.2.3 Span and allocation

The term span refers to a distance (typically a float) in a particular direction while allocation refers to the area (a Shape) in which a View is drawn. These terms are used frequently in the View source code and documentation, so we'll use them too.

22.5.2.4 Axis

An axis value should be either X_AXIS or Y_AXIS (int constants). View has several indexed properties that expect the index to be one of these two values. (The exception is the view property. getView(n) requests the nth child of the View.)

22.5.2.5 Bias

Many View methods have a parameter of type Position.Bias (see the previous discussion). There are only two values for an object of this type: Bias.Forward and Bias.Backward. The idea is that the addition of a Bias refines a Document offset somewhat. Without a Bias, if a user clicks to an offset of 5, then you know the click was either on the right half of the 4th character or on the left half of the 5th. The Bias parameter distinguishes between the two cases.

That said, Swing's handling of Position.Bias is spotty. Many method implementations simply ignore the Bias parameter, and many callers blindly pass in Bias.Forward. Should you decide to do the same, you would be in good company.

22.5.2.6 Properties

The View class defines the properties shown in Table 22-34.

Table 22-34. View properties

Property

Data type

get

is

set

Default value

alignmenti

float

·

 
 

0.5

attributes

AttributeSet

·

 
 

From element

container

Container

·

 
 

From parent

document

Document

·

 
 

From element

element

Element

·

 
 

From constructor

endOffset

int

·

 
 

From element

graphics1.3

Graphics

·

 
   

maximumSpani

float

·

 
 

(resizeWeight<=0 ? preferredSpan : Integer.MAX_VALUE)

minimumSpani

float

·

 
 

(resizeWeight<=0 ? preferredSpan : 0)

parent

View

·

 

·

null

preferredSpani

float

·

 
 

Abstract

resizeWeighti

int

·

 
 

0

startOffset

int

·

 
 

From element

viewi

View

·

 
 

null

viewCount

int

·

 
 

0

viewFactory

ViewFactory

·

 
 

From parent

visible

boolean

 

·

 

true

iindexed, 1.3since 1.3

The properties are defined as follows:

attributes

This property defines the AttributeSet used by the View to render an Element. By default, the Element's attributes are used. However, View objects should access the attributes through the getAttributes( ) accessor to allow View-specific attributes to be added to the Element's attributes or to allow the attributes to be converted in some way by the View subclasses.

container

This property is the Container ultimately responsible for this View. This View uses the container's coordinate system as its own, without performing any translation for child Views. We refer to this coordinate system as view coordinates.

element

This property is the Element this View is responsible for rendering. The document, startOffset , and endOffset properties are taken directly from the element property.

viewCount

This property defines the number of children this View has, and the indexed view property gives access to specific children. Typically, only subclasses of CompositeView (plus root Views) have any children.

viewFactory

This property is the factory that was used to create this View and can be used to create child View objects.

graphics

This property is a java.awt.Graphics object that can be used to determine font characteristics. (The paint( ) method is passed a Graphics object for drawing, so it doesn't need to use this one.)

visible

This property reflects whether the View should be drawn on the screen..

alignment

This property specifies the desired alignment for the View for each axis. 0 indicates origin alignment, 1 indicates alignment to the full span away from the origin, and 0.5 (the default) indicates centered alignment.

The other four properties help determine the minimum, maximum, and preferred size of the component using the View. preferredSpan reflects the View's preferred size along a particular axis. resizeWeight is used to determine the minimum and maximum size of the View. A value of 0 (or less) indicates that the view should not be resized. The minimumSpan and maximumSpan of a View with a resizeWeight of 0 are equal to the preferredSpan. Otherwise (if resizeWeight > 0), the minimumSpan is considered to be 0 while the maximumSpan is Integer.MAX_VALUE.

22.5.2.7 Constructor
public View(Element elem)

Instantiate a View for the given Element. Since the View class is abstract, this is called only from the subclass constructors.

22.5.2.8 Constants

View defines the constants shown in Table 22-35. The first four are used in the getBreakWeight( ) method, which is described later. The other two apply to the three properties that are indexed based on an axis.

Table 22-35. View constants

Name

Data type

Description

BadBreakWeight

int

The View should not be broken into fragments for formatting purposes.

ExcellentBreakWeight

int

The View supports splitting, and this is a very good place to break.

ForcedBreakWeight

int

The View supports splitting, and it must be broken here to be displayed properly.

GoodBreakWeight

int

The View supports splitting, but there is probably a better break location.

X_AXIS

int

Used to specify the x-axis (another name for SwingConstants.HORIZONTAL).

Y_AXIS

int

Used to specify the y-axis (another name for SwingConstants.VERTICAL).

22.5.2.9 Abstract methods

These three key methods must be implemented by all concrete subclasses of View:

public abstract void paint(Graphics g, Shape allocation)

Lay out and paint the View within the bounds of the given Shape with the given Graphics. The allocation may be different than the last time the View was painted, so the View must be prepared to reorganize its layout appropriately. For efficiency, there is usually no clipping region set on the Graphics object. It's the View's responsibility to stay within the allocation (or explicitly call g.setClip(allocation)). Also, you can't be sure that the Graphics object has any other settings (foreground color, background color, etc.).

public abstract Shape modelToView(int offset, Shape allocation, Position.Bias b) throws BadLocationException

Convert from a Document offset to view coordinates. The return value should be a small Rectangle (or another Shape) that designates the region in which the character at that offset is drawn. Be prepared for the Position.Bias argument to be null, in which case it should be treated as Position.Bias.Forward.

public abstract int viewToModel(float x, float y, Shape allocation, Position.Bias biasReturn[])

Convert from view coordinates to a Document offset. The return value should be the Document offset closest to point (x, y). You should set the value of biasReturn[0] to Bias.Forward or Bias.Backward to indicate whether the given point is closer to the next character or the previous one. (If you're less scrupulous and always set it to Bias.Forward, you won't be alone, but set it correctly if you can.)

In the method descriptions that follow, we describe the functions of the methods for a generic subclass of View. These functions are not what the methods usually do in the View class itself. The View class does provide implementations, but they are often empty or minimal (i.e., they always return null or always return this).

22.5.2.10 Translation methods

These methods (with the addition of two of the above abstract methods) translate between Document offsets and view coordinates. Views use the same coordinate system as the text component in which they are drawn.

public Shape modelToView(int p0, Position.Bias b0,int p1, Position.Bias b1, Shape allocation) throws BadLocationException

Like the abstract modelToView( ) method, but returns a Shape that binds a range of characters instead of a single character. (p0 and p1 are Document offsets.)

public int getNextVisualPositionFrom(int offset, PositionBias bias, Shape a, int direction, Position.Bias[] biasReturn) throws BadLocationException

This method determines where the caret goes when the user presses one of the arrow keys. The direction parameter is one of SwingConstants.NORTH, SOUTH, EAST, or WEST. The input and output are both Document offsets, so handling EAST and WEST is easy. NORTH and SOUTH might need to be converted into view coordinates and then reconverted. The View class has a good implementation that tries to respect magicCaretPosition (if any) and calls Utilities.getPostionAbove( ) and getPositionBelow( ) for NORTH and SOUTH. You should set the value of biasReturn[0] to Bias.Forward or Bias.Backward as appropriate.

public int getViewIndex(int offset, Shape allocation) throws BadLocationException

Return the index of the child View that corresponds to the given Document offset, or -1 if no such child exists. This method was introduced in SDK 1.3.

public int getViewIndex(float x, float y, Shape allocation) throws BadLocationException

Return the index of the child View that corresponds to the given point, or -1 if no such child exists. This method was introduced in SDK 1.4.

public String getToolTipText(float x, float y, Shape allocation)

Return the tooltip for the given point, or null if there is none. This method was introduced in SDK 1.4 to support the display of ALT text from HTML IMG tags in JEditorPane.

22.5.2.11 Break methods
public View breakView(int axis, int offset, float pos, float length)

Return a new View for painting part of this View's content. The new View should start at the given offset into the Document, which can be anywhere in the range defined by the View's startOffset and endOffset properties. Ideally, the new View should have a span of length along the given axis. The pos argument designates the starting position (along the same axis) for the new View's allocation. Most Views ignore this argument except for things such as calculating the positions of tab stops. Views that support breaking typically implement this by calling the createFragment( ) method after determining an offset for the far side of the break. Views that don't support breaking return themselves unbroken (via return this).

Suppose we're trying to draw lines of text that are 400 pixels wide and we've already drawn 280 pixels worth of text on the current line. We could call breakView(X_AXIS, offset, 280, 120) in an attempt to fill out the line. The View returned may or may not have the horizontal span we asked for.

public int getBreakWeight(int axis, float pos, float length)

Return a "score" indicating how appropriate it would be to break this View into a piece with the given length along the given axis (from the start of the View, i.e., from the Document offset designated by its startOffset property). The return value should be one of the constants from Table 22-35. The pos argument designates the starting position (along the same axis) of the View's allocation. Most Views ignore this argument except for things such as calculating the positions of tab stops.

public View createFragment(int p0, int p1)

Return a new View for painting part of this View's content. The new View should cover the Document from offset p0 to offset p1, which are in the range defined by the View's startOffset and endOffset properties. Views that don't support breaking return themselves unbroken (via return this).

22.5.2.12 Tree management methods

These methods (introduced in SDK 1.3) allow the structure of the View tree to be modified. In Views that don't support children, these methods do nothing.

public void insert(int index, View v)

Insert a child View at the given index.

public void append(View v)

Add a child View to the end; equivalent to insert(getViewCount( ), v).

public void remove(int index)

Remove the child View at the given index.

public void removeAll( )

Remove all of this View's children.

public void replace(int index, int length, View[] views)

Replace some child Views (starting at index) with some other Views. The length argument may be 0, resulting in a pure insertion. The views array may be null, resulting in a pure deletion.

22.5.2.13 Layout methods

The way that Views are laid out is based on how Components are laid out, except that each axis is handled separately. Both View and Component have preferredSize, minimumSize, and maximumSize properties. View's setSize( ) method resembles a combination of Component's doLayout( ) and setSize( ) methods. View's preferenceChanged( ) method is analogous to invalidate( ) in Component.

public void setSize(float width, float height)

Set the size of the View and lay out any children. This method may be called with the same arguments as the previous call to ensure that the layout is up-to-date.

public void preferenceChanged(View child, boolean width, boolean height)

A child View calls this method to indicate to its parent that its preferredSpan has changed, identifying itself and the axis (one or both) of the change. Any cached size information for the child should be discarded, and preferenceChanged( ) should be called on this View's parent (grandparent of the child). A preferenceChanged( ) on the root View triggers a revalidate( ) on the text component.

public Shape getChildAllocation(int index, Shape a)

The parameters are the Shape that is the allocation for this View (the parent) and a child index, and the Shape returned is the allocation for the child. (The child's allocation should be entirely inside the parent's.) This is an important method because many of the View methods require an allocation argument. So you need the value returned by this method to call them.

22.5.2.14 Update methods

If every View object registers as a DocumentListener, there is a flurry of redundant activity each time the document changes. To ameliorate this, only the root View registers itself and uses these methods to forward DocumentEvents down the tree. A child View is notified only if the event applies to that child.

public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f )
public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f )
public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f )

These methods have the same names and purposes as those defined in the DocumentListener interface, but have extra arguments to pass the View its current allocation and a ViewFactory that can be used to rebuild its children if necessary.

22.5.3 The View Classes

Swing provides an almost overwhelming number of View classes. Figure 22-12 shows them and the relationships between them. (Note that the View types used by JTextArea and JTextPane are depicted in Figure 22-11.)

Figure 22-12. View class diagram
figs/swng2.2212.gif

It's not worth discussing every View class in detail, but here's a brief description of each:

PlainView

Displays one or more nonwrapped lines of text in a single font and color.

FieldView

A subclass of PlainView used by JTextField. It knows how to perform alignment on a single line of text.

PasswordView

A subclass of FieldView used by JPasswordField.

GlyphView

Displays a block of styled text, but all in the same style (such as the text represented by a leaf in the Element tree). This View supports breaking, so the text may wrap across several lines.

LabelView

A subclass of GlyphView that doesn't behave any differently except that it caches character attributes to speed up painting. (It has nothing to do with JLabel.)

ComponentView

Knows how to display embedded Components.

IconView

Knows how to display embedded Icons.

CompositeView

An abstract class customized to handle children. There is no requirement that all Views that use child Views must extend this class, but they do in practice.

BoxView

A subclass of CompositeView that tiles its child Views along an axis (as when paragraphs are tiled down a page, for example).

WrappedPlainView

A subclass of BoxView that displays one or more paragraphs of text in a single font and color. Each paragraph is managed by a child View (an instance of a package-private inner class) and is wrapped if necessary.

FlowView

An abstract subclass of BoxView that defines a "flow strategy" for laying out children amid constraints.

ParagraphView

An subclass of FlowView that knows how to display an indented paragraph of text that may have a mix of styles.

ZoneView

A subclass of BoxView that attempts to be memory-efficient by deferring the creation of child Views until they are needed. This can be useful for displaying (parts of) very large Element trees, but this class is unused in Swing.

AsyncBoxView

Behaves like BoxView but performs layout on a low-priority thread in an attempt to free up the GUI thread for other things. The thread and pending layout tasks are managed by the LayoutQueue class.

TableView

An abstract class in which each child View represents a row, and each grandchild View represents a cell.

In addition to these, there are several View classes in the javax.swing.text.html package that are used by JEditorPane to display HTML. These are listed in Table 22-36. (JEditorPane is the topic of the next chapter.)

Table 22-36. View classes in the javax.swing.text.html package

View class

Used for

BlockView

<blockquote>, <pre>, <center>, <div>, <li>, <dl>, <dd>, parent View for others

FormView

<input>, <select>, <textarea> (HTML forms)

ImageView

<img>

InlineView

Inline text

ListView

<ul>, <ol>, <dir>, <menu>

ObjectView

Component specified in <object> tag

ParagraphView

<p> (including implied <p> tags), <dt>, <h1>, <h2>, <h3>, <h4>, <h5>, <h6>

As of SDK 1.4, ObjectView supports an interesting feature. If you include a tag like this:

<object classid='javax.swing.JButton'>     <param name='text' value='push me'>     <param name='toolTipText' value='go ahead and push me...'> </object>

in your HTML, ObjectView instantiates the Object and (if it is a Component, including JComponent) renders it with the rest of the HTML. This works for any Object type, but (at least for now) the <param> tag works only for properties of type String.

22.5.4 The TabExpander Interface

TabExpander is a simple interface implemented by View classes that know how to expand tabs.

22.5.4.1 Method
public abstract float nextTabStop( float x, int tabOffset)

Return the next tab stop position after the specified position. Values are expressed in points. For example, a View with tab stops every 80 points, starting at 0, would return 240.0 for an input of 227.0. The second parameter is the Document offset of the tab, which can be used to look up the kind of tab when multiple tab types are supported.

22.5.5 The TabableView Interface

This interface is implemented by View classes that need to take tab settings into account when calculating their preferredSpan. In general, a View instance that implements this interface functions correctly with respect to tabs only if its parent View implements the TabExpander interface.

22.5.5.1 Methods
public abstract float getTabbedSpan( float x, TabExpander e)

Return a value indicating the span of the View, starting at the specified point and using the supplied TabExpander to expand tabs.

public abstract float getPartialSpan(int p0, int p1)

Return a value indicating the span needed to cover the range delimited by the specified document offsets. Implementations may assume that there are no tabs in the given range.

22.5.6 The Utilities Class

This class defines a number of static methods used by the text package. Some of these methods are key to the View classes. Many of the method signatures clash with the rest of the Swing text API. For example, parameters in the view coordinate system are declared to be ints, not floats.

22.5.6.1 Public static methods
public static final int getWordStart(JTextComponent c, int offset) throws BadLocationException
public static final int getWordEnd(JTextComponent c, int offset) throws BadLocationException

Return the Document offset at the beginning (or one position past the end) of the word at the specified Document offset.

public static final int getPreviousWord(JTextComponent c, int offset) throws BadLocationException
public static final int getNextWord(JTextComponent c, int offset) throws BadLocationException

Return the Document offset at the beginning of the word that precedes or follows the specified offset.

public static final int getRowStart(JTextComponent c, int offset) throws BadLocationException
public static final int getRowEnd(JTextComponent c, int offset) throws BadLocationException

Return the Document offset corresponding to the start or end of the row containing the specified offset.

public static final int getPositionAbove(JTextComponent c, int offset, int x) throws BadLocationException
public static int getPositionBelow(JTextComponent c, int offset, int x) throws BadLocationException

Return the document position closest to the given x view coordinate in the row above or below the row containing the specified offset.

public static final Element getParagraphElement(JTextComponent c, int offset)

Return the paragraph Element corresponding to the specified Document offset. If the component's model is a StyledDocument, it delegates to the model's getParagraphElement( ) method. Otherwise, the Document's defaultRootElement is queried for the child containing the given offset.

public static final int getBreakLocation(Segment s, FontMetrics metrics, int x0, int x, TabExpander e, int startOffset)

Try to find a suitable line-break location by looking for whitespace in the supplied Segment. x0 and x (both in view coordinates) are the start and intended end of the span. The return value is a Document offset. The TabExpander may be null, but then tabs are counted as spaces.

public static final int drawTabbedText(Segment s, int x, int y, Graphics g, TabExpander e, int startOffset)

Draw the Segment's text, starting at startOffset, to the supplied Graphics at the specified point (x, y) in view coordinates. The TabExpander may be null, but then tabs are counted as spaces. The return value is the x-coordinate at the end of the drawn text.

public static final int getTabbedTextWidth(Segment s, FontMetrics metrics, int x, TabExpander e, int startOffset)

Return the width (in view coordinates) of the Segment's text, starting at startOffset, with tabs expanded. The x argument is the starting position of the text in view coordinates. The TabExpander may be null, but then tabs are counted as spaces.

public static final int getTabbedTextOffset(Segment s, FontMetrics metrics, int x0, int x, TabExpander e, int startOffset)

Return the offset into the given Segment corresponding to the view coordinate x. The x0 coordinate specifies the View location of the start of the text. The TabExpander may be null, but then tabs are counted as spaces.

public static final int getTabbedTextOffset(Segment s, FontMetrics metrics, int x0, int x, TabExpander e, int startOffset, boolean round)

This is the same as the previous method if round is true. If round is false, when x falls between two characters, it returns the offset of the left one even if the right one is closer.



Java Swing
Graphic Java 2: Mastering the Jfc, By Geary, 3Rd Edition, Volume 2: Swing
ISBN: 0130796670
EAN: 2147483647
Year: 2001
Pages: 289
Authors: David Geary

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net