Pour ceux qui ont besoin d'une explication plus spécifique de la façon particulière dont HTMLEditorKit gère les listes, tout se résume au balisage généré. Je vais essayer de le garder aussi simple que possible. Revenons un peu et parlons de documents HTML dans Swing.
Il s'avère que Swing s'appuie sur les paragraphes pour faire le positionnement et la navigation du curseur. Par exemple, chaque fois que vous écrivez dans une nouvelle ligne, un nouveau pagraphe est généré. Même la vue correspondante du document dépend de la présence de paragraphes aux bons endroits. Il doit toujours y avoir un paragraphe dans le document. Sinon, des choses étranges commencent à se produire.
Alors, que se passe-t-il si le document est complètement vide? Sûrement, il n'y a pas besoin d'un paragraphe là. Eh bien, incroyablement, même dans ce cas, il y a un paragraphe. C'est l'un des effets de ce que la documentation appelle p-implicite ou le paragraphe implicite. Le code HTML généré pour un document vierge est:
<html>
<head></head>
<body>
<p style="margin-top: 0">
</p>
</body>
</html>
Expectedly, lorsque vous insérez une liste, il est placé à l'intérieur paragraphe:
<html>
<head></head>
<body>
<p style="margin-top: 0">
<ul>
<li>
</li>
</ul>
</p>
</body>
</html>
... ce qui est bien sûr une balise invalide (pas seulement parce qu'il n'y a pas de titre dans la tête). Mais attends! Ça devient plus intéressant.Une fois la liste insérée, le "pointeur interne" du document, pour ainsi dire, reste après la balise de fermeture </ul>
. Par conséquent, si vous tapez « Bonjour », il sera placé en dehors de la liste:
<html>
<head></head>
<body>
<p style="margin-top: 0">
<ul>
<li>
</li>
</ul>
Hello
</p>
</body>
</html>
C'est pourquoi que « Bonjour » apparaît vers la droite par rapport à la balle insérée. Maintenant, comme Stephane mentionné dans la question, setText(getText())
résout par magie le problème. En effet, la définition manuelle du contenu de l'instance JTextPane déclenche l'analyseur, qui à son tour place le "pointeur interne" là où il devrait être; dans la liste. Maintenant, lorsque vous tapez "Bonjour", il apparaîtra beaucoup plus près de la balle. Je dis beaucoup plus parce qu'il ya encore quelque chose ne va pas sur le HTML:
<html>
<head></head>
<body>
<p style="margin-top: 0">
<ul>
<li>
Hello
</li>
</ul>
</p>
</body>
</html>
avis il n'y a pas de paragraphe enfermant le nouveau texte dans la liste. C'est pourquoi le texte n'apparaîtra pas juste à côté de la balle.
Comment allez-vous tout cela? Eh bien, c'est le truc délicat dont parlait Stéphane. Vous seriez confronté à une combinaison de bugs (tels que this one), des problèmes non documentés (comme this one) et le comportement par défaut comme nous l'avons vu. La solution la plus simple consiste à utiliser l'une des solutions de la liste de Stephane. Je suis d'accord Shef est le meilleur de tous, mais n'a pas eu beaucoup d'activité depuis 2009 (!). Personnellement, j'ai trouvé Stanislav's website incroyablement utile pour toutes les choses EditorKit.
Vous pouvez également jeter un oeil à ADAPRO: un éditeur d'assistance open-source assez stable dans lequel j'étais fortement impliqué. Les fonctions d'assistance sont boguées mais la fonctionnalité d'édition principale a été testée minutieusement. Le code suivant provient de ce projet. Il nécessite la classe ElementWriter de SHEF'snet.atlanticbb.tantlinger.ui.text package.
//HTML representation of an empty paragraph
private static final String sEmptyParagraph = "<p style=\"margin-top: 0\"></p>";
/**
* Translates into HTML a given element of the document model.
* @param element Element to serialise to a HTML string
* @param out Serialiser to HTML string
* @return HTML string "equivalent" to given element
*/
static String extractHTML (Element element, StringWriter out) {
ElementWriter writer = new ElementWriter (out, element);
try {
writer.write();
} catch (IOException e) {
System.out.println ("Error encountered when serialising element: " +e);
e.printStackTrace();
} catch (BadLocationException e) {
System.out.println ("Error encountered when extracting HTML at the element's position: " +e);
e.printStackTrace();
}
return out.toString();
}
/**
* Determines if the parent element of the current paragraph element is one of a number provided as a list
* of tag names. If so, it returns the parent element.
* @param document Document model of the text
* @param iCaretPos Caret's current position
* @param sTags Possible parent tags
* @return Parent element
*/
static Element getNearestParent (HTMLDocument document, int iCaretPos, String sTags) {
Element root;
root = document.getParagraphElement (iCaretPos);
do {
root = root.getParentElement();
} while (sTags.indexOf (root.getName()) == -1);
return root;
}
/**
* Inserts all HTML tags required to build an ordered/unordered list at the caret's current position.
* If the aim is instead to turn the numbered/bulleted paragraphs into plain ones, it takes care of
* deleting the necessary tags.
* @param sTypeList Type of list to build: "ul" or "ol".
* @param textArea Editable area containing text.
*/
static void insertList (String sTypeList, JTextPane textArea) {
boolean bOnlyListSelected; //selection includes a list exclusively
int iStartIndex, iEndIndex, //element indexes included in selection
iStartSel, iEndSel, //starting and ending offset of selected text
iItemNo, //total number of list items
i;
String sHTML, //HTML code of text represented by a given element
sHTMLBlock, //HTML code block to be inserted into document model
sRest; //part of the text remaining unselected after the selected block
HTML.Tag tag; //current list tag
HTMLDocument document; //data model underlying the typed text
Element root, //root element of the document model tree
section; //element representing a block of text
SimpleAttributeSet attribIns; //backup of current input attributes
//Fetches the current document
document = (HTMLDocument) textArea.getDocument();
//Finds the topmost parent element of the current paragraph (effectively, is the list inside a table?)
root = getNearestParent (document, textArea.getCaretPosition(), "td body");
//Range of elements included in the selection
iStartSel = textArea.getSelectionStart();
iEndSel = textArea.getSelectionEnd();
iStartIndex = root.getElementIndex (iStartSel);
iEndIndex = root.getElementIndex (iEndSel);
//HTML-related initialisations
sHTML = "";
sHTMLBlock = "";
tag = null;
//Checks if selection is comprised of just list items
i = iStartIndex;
bOnlyListSelected = true;
do {
tag = HTML.getTag (root.getElement(i).getName());
//Is it a list tag?
if ((tag == null) || ((!tag.equals (HTML.Tag.OL)) && (!tag.equals (HTML.Tag.UL))))
bOnlyListSelected = false;
i++;
} while (bOnlyListSelected && (i <= iEndIndex));
//Back up current input attributes
attribIns = new SimpleAttributeSet (textArea.getInputAttributes());
try {
//At some point in the selection there is no previous list...
if (!bOnlyListSelected) {
//Inserts <LI> tags for every text block
for (i = iStartIndex; i <= iEndIndex; i++) {
section = root.getElement(i);
tag = HTML.getTag (section.getName());
//Retrieves current HTML
sHTML = extractHTML (section, new StringWriter());
//If it is non-listed text, reconstitute the paragraph
if (tag == null)
sHTML = "<p style=\"margin-top: 0;\">" +sHTML+ "</p>";
//Text in a list already => no nesting (delete <UL>/<OL> tags)
if (sHTML.indexOf("<li>") != -1) {
sHTML = sHTML.substring (sHTML.indexOf("<li>"), sHTML.length());
sHTML = sHTML.substring (0, sHTML.lastIndexOf("</li>") + 5);
//Non-listed text => add <LI> tags
} else sHTML = "<li>" +sHTML+ "</li>";
sHTMLBlock = sHTMLBlock + sHTML;
}
sHTMLBlock = "<"+sTypeList+">" +sHTMLBlock.trim()+ "</"+sTypeList+">";
//Gets the text coming after caret or end of selection
sRest = textArea.getText (iEndSel, document.getLength() - iEndSel);
//Adds an empty paragraph at the end of the list if the latter coincides with the end of the document
//or if the rest of the document is empty. This is to avoid a glitch in the editor kit's write() method.
//http://java-sl.com/tip_html_kit_last_empty_par.html
if ((root.getElement(iEndIndex).getEndOffset() == root.getEndOffset()) ||
sRest.replaceAll ("[\\p{Z}\\s]", "").trim().isEmpty())
sHTMLBlock = sHTMLBlock + sEmptyParagraph;
//Removes the remaining old non-listed text block and saves resulting HTML string to document model
document.setOuterHTML (root.getElement(iEndIndex), sHTMLBlock);
if (iEndIndex > iStartIndex)
document.remove (root.getElement(iStartIndex).getStartOffset(),
root.getElement(iEndIndex - 1).getEndOffset() -
root.getElement(iStartIndex).getStartOffset());
//Selection just includes list items
} else {
//Works out the list's length in terms of element indexes
root = root.getElement (root.getElementIndex (iStartSel));
iItemNo = root.getElementCount();
iStartIndex = root.getElementIndex (textArea.getSelectionStart());
iEndIndex = root.getElementIndex (textArea.getSelectionEnd());
//For everery <LI> block, remove the <LI> tag
for (i = iStartIndex; i <= iEndIndex; i++) {
sHTML = extractHTML (root.getElement(i), new StringWriter());
sHTML = sHTML.substring(sHTML.indexOf("<li>") + 4, sHTML.length());
sHTML = sHTML.substring(0, sHTML.lastIndexOf("</li>"));
sHTMLBlock = sHTMLBlock + sHTML;
}
//List selected partially? => divide list
if (iItemNo > (iEndIndex - iStartIndex + 1)) {
//Saves HTML string to document model
((HTMLEditorKit) textArea.getEditorKit()).insertHTML (document, root.getElement(iEndIndex).getEndOffset(),
sHTMLBlock, 3, 0, HTML.Tag.P);
//Removes the old block
document.remove (root.getElement(iStartIndex).getStartOffset(),
root.getElement(iEndIndex).getEndOffset() -
root.getElement(iStartIndex).getStartOffset());
//Removes the list tag associated with the block
} else document.setOuterHTML (root, sHTMLBlock.trim());
}
} catch (Exception eTexto) {
System.out.println ("Problemas al eliminar/insertar texto: " +eTexto);
eTexto.printStackTrace();
}
//Recover selection. Previous operations displace the cursor and thus selection highlight is lost
textArea.setSelectionStart (iStartSel);
textArea.setSelectionEnd (iEndSel);
//If only one list item has been created and is the first one, copy all previous style information to the list
if ((!bOnlyListSelected) && (iStartSel == iEndSel)) {
textArea.setCharacterAttributes (attribIns, false);
}
}
exemple de code lié par @StanislavL se fait dans le document (modèle pour JTextComponents) – mKorbel
Mais je ne fais pas des insertions dans le document ou setText, je suis juste essayer d'utiliser la fonctionnalité Java intégrée. Et cela fonctionne, la source dans le document est correcte. C'est juste que le JTextPane n'est pas rendu correctement à moins que je ne réinitialise le texte ... –
Pour une meilleure aide plus tôt, postez un [SSCCE] (http://sscce.org/). –