0

私は持っている:

JTextPane jtextPane = new JTextPane();
jtextPane.setEditorKit(new HTMLEditorKit());
...

その後、アクションが次のようになるように、順序付けられていないリスト ボタンをツールバーに追加しようとしました。

Action insertBulletAction = 
        HTMLEditorKit.InsertHTMLTextAction ("Bullets", "<ul><li> </li></ul>", 
                                            HTML.Tag.P, HTML.Tag.UL);
JButton insertBulletJButton = new JButton(insertBulletAction);

生成された html のダンプを取ると、これには適切なコードが含まれます。ただし、以下に示すように、妥当なものにさえ近くないため、レンダリングは非常に悪くなります。

生成された弾丸のスナップショット

しかし、もしそうなら:

jtextPane.setText(jtextPane.getText());
jtextPane.repaint();

その後、すべてが順調です。しかし、両方の行を実行しないと、どちらも単独では機能しません。jtextPane を表示する前にテキストを設定することで、機能させることもできます。

setText(getText())これは本当に奇妙で、なぜ a の後に aを実行しなければならないのか理解できませんrepaint()

PS: これは、次の質問と非常によく似ています: How to implement bullet points in a JTextPane? そして、正しくレンダリングされないことを除いて機能します。HTMLEditorKit と RTFEditorKit に関係があるかどうかはわかりませんが、何か原因でレンダリングが失敗します。以下のhtmlソースコードは完璧です...

PS2:このリンクも非常に便利ですが、解決策も示されていません。

更新: 要求された完全なコードは次のとおりですが、他にはあまりありません...

public static void main(String[] args)
{
    JFrame jframe = new JFrame();
    jframe.setSize(800, 600);
    jframe.setVisible(true);

    JTextPane jtextPane = new JTextPane();
    jtextPane.setEditorKit(new HTMLEditorKit());

    Action insertBulletAction = new HTMLEditorKit.InsertHTMLTextAction ("Bullets", 
                                    "<ul><li> </li></ul>", HTML.Tag.P, HTML.Tag.UL);
    JButton insertBulletJButton = new JButton(insertBulletAction);
    insertBulletJButton.setRequestFocusEnabled(false);

    jframe.setLayout(new BorderLayout());
    jframe.add(new JScrollPane(jtextPane));
    jframe.add(insertBulletJButton, BorderLayout.SOUTH);
}
4

2 に答える 2

2

答えは実際には非常に複雑です。基本的に、InsertHtmlActionそれだけでは十分ではありません。作業リスト アクションに到達するには、多くの作業とロジックが必要です。かなりのロジックが必要です!したがって、間違いなく Action クラスをオーバーライドする必要があります。基本的に、のパラメーターはInsertHtmlAction、html コードのどこにいるかによって変わります。

そうは言っても、何が関係しているのかをよりよく理解するために、いくつかのオープンソース ソリューションを調査しました。その後、何時間もかけて (そして事前に何時間も費やして)、最終的に必要なものを十分に理解することができました。しかし、それはかなり複雑です。ここで書くには複雑すぎます。概念を説明するだけでも、本の 1 章が必要です。それでも、私はまだいくつかの詳細について曖昧です (私はまだ取り組んでいます)。

人々がこのためにコンポーネントを販売する理由が理解できました!

ほとんどのオープン ソース ソリューションは、リストをうまく処理できないことがわかりました。それらは通常ある程度は機能しますが、ほとんどの場合、明らかなバグがあります。それか、リストの最も基本的なケース以外は実際には何も処理しません。

以下は、すべてをよりよく理解するためにシステムがどのように機能するかを理解するために調べたシステムのリストです。残念ながら、ドキュメントが不足しているか、理解しにくいことがわかったので、これらのプロジェクトを見るのが何よりも役に立ちました。

最も役立つ

  • Shef - 最も役に立ちました。
  • ekit - まともだが多くのバグがあり、最適なコード構成ではない
  • MetaphaseEditor - ekit に類似

ある程度役立つ (より複雑、バグがある、関連性が低いなど)

  • OOoBean - 試してみましたが、必要なものが多すぎました (したがって、複雑すぎました)。本当に良さそうに見えますが、時間を投資する必要があります。
  • JXHTMLEdit - 関心があるようです

追加のリンク

  • JWebEngine - 主にレンダリング用
  • Joeffice - 興味深いですが、それはすべてビデオであり、まだ準備が十分ではありませんでした.
  • リッチテキスト- コメントはありません。ざっと見ただけです。
  • JRichTextEditor - コメントもありません。同じことです。

有料

  • JWord - とても面白そうですが、私がやっていることの予算を超えていました。
于 2013-07-10T10:30:34.397 に答える
1

リストを処理する HTMLEditorKit の独特な方法について、より具体的な説明が必要な人のために説明すると、すべては生成されるマークアップに帰着します。できるだけシンプルにしようと思います。少し巻き戻して、Swing での HTML ドキュメントについて話しましょう。

Swing は、カーソルの配置とナビゲーションを行うために段落に依存していることがわかりました。たとえば、新しい行に書き込むたびに、新しいページが生成されます。ドキュメントの対応するビューでさえ、適切な場所に段落が存在するかどうかによって異なります。ドキュメントには必ず段落が必要です。そうしないと、奇妙なことが起こり始めます。

では、ドキュメントが完全に空白の場合はどうなるでしょうか? 確かに、そこに段落は必要ありません。まあ、信じられないことに、その場合でも段落があります。これは、ドキュメンテーションがp-impliedまたはImplied paragraphと呼んでいるものの効果の 1 つです。空白のドキュメントに対して生成される HTML は次のとおりです。

<html>
  <head></head>
  <body>
    <p style="margin-top: 0">

    </p>
  </body>
</html>

予想どおり、リストを挿入すると、段落内に配置されます。

<html>
  <head></head>
  <body>
    <p style="margin-top: 0">
      <ul>
        <li>

        </li>
      </ul>
    </p>
  </body>
</html>

...もちろん、これは無効なマークアップです (頭の中にタイトルがないという理由だけではありません)。ちょっと待って!もっと面白くなります。リストが挿入されると、ドキュメントの「内部ポインター」は、いわば終了</ul>タグの後に留まります。したがって、「Hello」と入力すると、リストの外に配置されます。

<html>
  <head></head>
  <body>
    <p style="margin-top: 0">
      <ul>
        <li>

        </li>
      </ul>
      Hello
    </p>
  </body>
</html>

これが、挿入された箇条書きに対して「Hello」がかなり右に表示される理由です。さて、ステファンが質問で述べたように、setText(getText())魔法のように問題を解決します。これは、JTextPane インスタンスのコンテンツを手動で設定するとパーサーがトリガーされ、パーサーが「内部ポインター」をあるべき場所に配置するためです。リスト内。これで「Hello」と入力すると、箇条書きの近くに表示されます。HTML にはまだ正しくない点があるため、もっと詳しく言います。

<html>
  <head></head>
  <body>
    <p style="margin-top: 0">
      <ul>
        <li>
          Hello
        </li>
      </ul>      
    </p>
  </body>
</html>

リストには、新しいテキストを囲む段落がないことに注意してください。そのため、テキストは箇条書きのすぐ隣に表示されません。

このすべてについてどうやって行くのですか?まあ、それはステファンが話していたトリッキーなビットです. これまで見てきたように、バグ (このようなもの)、文書化されていない不具合 (このようなもの)、およびデフォルトの動作の組み合わせに直面することになります。最も簡単な方法は、Stephane のリストにあるソリューションの 1 つを使用することです。Shefが最高であることに同意しますが、2009 年以降はあまり活動していません (!)。個人的には、Stanislav の Web サイトが EditorKit のあらゆる点で非常に役立つことがわかりました。

ADAPROもご覧ください。私が深く関わった、非常に安定したオープンソースの支援エディターです。支援機能にはバグがありますが、コアの編集機能は徹底的にテストされています。次のコードは、そのプロジェクトからのものです。SHEF のnet.atlanticbb.tantlinger.ui.textパッケージのElementWriter クラスが必要です。

    //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); 
        }        
}
于 2013-11-02T01:27:27.970 に答える