2

LineBreakMeasurerを使用して複数行のテキストを描画する方法については数千の記事がありますが、\ n(テキストの特定の位置に新しい行を強制する場合だけでなく、右-または左-マージンが終了します)。

秘密はBreakIteratorにあるようですが、\nを処理する実装が見つかりませんでした。

4

5 に答える 5

4

LineBreakMeasurer's(LBM' s)メソッドの代わりにnextLayout(float)、オーバーロードされたLBM.nextLayout(float, int, boolean)メソッドを使用します。LBMこれにより、返されるに含まれるテキストを制限できますTextLayout。あなたの場合、次の改行を超えないように指示します。

このコードスニペットはあなたにアイデアを与えるはずです。最初LBM.nextOffsetに、どの文字インデックスが次のレイアウトの終わりになるかを「ピーク」するために使用します。次に、そのオフセットまで文字列の内容を繰り返し処理して、改行文字が見つかるかどうかを確認します。そうした場合、その見つかった制限を、改行を超えないようnextLayout(float, int, boolean)に指示する2番目の引数として使用します。LBM

int next = lineMeasurer.nextOffset(formatWidth);
int limit = next;
if (limit < totalLength) {
   for (int i = lineMeasurer.getPosition(); i < next; ++i) {
      char c = string.charAt(i);
      if (c == '\n') {
         limit = i;
         break;
      }
   }
}

TextLayout layout = lineMeasurer.nextLayout(formatWidth, limit, false);

参考文献

http://java.sun.com/developer/onlineTraining/Media/2DText/style.html#layout http://java.sun.com/developer/onlineTraining/Media/2DText/Code/LineBreakSample.java

于 2010-11-21T05:22:16.220 に答える
3

このコードは改行の問題にうまく機能することがわかりました。これを取得するためのテンプレートとしてatdixonを使用しました。

while (measurer.getPosition() < paragraph.getEndIndex()) {
   next = measurer.nextOffset(wrappingWidth);
   limit = next;
   charat = tested.indexOf('\n',measurer.getPosition()+1);
   if(next > (charat - measurer.getPosition()) && charat != -1){
      limit = charat - measurer.getPosition();
   }
   layout = measurer.nextLayout(wrappingWidth, measurer.getPosition()+limit, false);
   // Do the rest of your layout and pen work.
}
于 2012-03-21T21:37:42.077 に答える
2

最初にテキストをトークン化し、次にLineBreakMeasureCodeを各トークンに適用します。

于 2009-07-13T15:41:41.387 に答える
1

アーロンのコードは常に正しく機能するとは限らないので、ここで私のために機能しているいくつかの微調整されたコードがあります:

int next = measurer.nextOffset(width);
int limit = next;
if (limit <= text.length()) {
  for (int i = measurer.getPosition(); i < next; ++i) {
    char c = text.charAt(i);
    if (c == '\n') {
      limit = i + 1;
      break;
    }
  }
}
TextLayout textLayout = measurer.nextLayout(width, limit, false);

AttributedStringからのテキストが必要な場合は、事前にこれを行うことができます

AttributedCharacterIterator iterator = attributedString.getIterator();
StringBuilder stringBuilder = new StringBuilder(iterator.getEndIndex());
while (iterator.getIndex() < iterator.getEndIndex()) {
  stringBuilder.append(iterator.current());
  iterator.next();
}
String text = stringBuilder.toString();
于 2013-06-04T10:51:12.490 に答える
0

トピックは非常に古いものですが、私はこの問題を自分で抱えており、解決する必要がありました。かなりの調査の結果、「JTextArea」をラップする単一のクラスで機能するソリューションを思いつきました。

コードはKotlinにあり、それが私が使用しているものです。うまくいけば、それはまだ役立つでしょう。

package [your package name]

import java.awt.Font
import java.awt.FontMetrics
import java.awt.Insets
import java.awt.font.LineBreakMeasurer
import java.awt.font.TextAttribute
import java.text.AttributedString
import java.text.BreakIterator
import javax.swing.JTextArea

class TextAreaLineCounter(
    private val textArea: JTextArea
) {

    private val font: Font
        get() = textArea.font
    private val fontMetrics: FontMetrics
        get() = textArea.getFontMetrics(font)
    private val insets: Insets
        get() = textArea.insets
    private val formatWidth: Float
        get() = (textArea.width - insets.left - insets.right).toFloat()

    fun countLines(): Int {
        return countLinesInParagraphs(
            textRaw = textArea.text,
            font = font,
            fontMetrics = fontMetrics,
            formatWidth = formatWidth
        )
    }

    private fun countLinesInParagraphs(
        textRaw: String,
        font: Font,
        fontMetrics: FontMetrics,
        formatWidth: Float
    ): Int {
        val paragraphs: List<String> = textRaw.split("\n")
        val lineCount = paragraphs.fold(0) { acc: Int, sentence: String ->
            val newCount = acc + countLinesInSentence(sentence, font, fontMetrics, formatWidth)
            newCount
        }
        return lineCount
    }

    private fun countLinesInSentence(
        textRaw: String,
        font: Font,
        fontMetrics: FontMetrics,
        formatWidth: Float
    ): Int {
        val text = AttributedString(textRaw)
        text.addAttribute(TextAttribute.FONT, font)
        val frc = fontMetrics.fontRenderContext
        val charIt = text.iterator
        val lineMeasurer = LineBreakMeasurer(
            charIt,
            BreakIterator.getWordInstance(),
            frc
        )
        lineMeasurer.position = charIt.beginIndex
        var noLines = 0
        while (lineMeasurer.position < charIt.endIndex) {
            lineMeasurer.nextLayout(formatWidth)
            noLines++
        }
        return noLines
    }
}

また、ラインカウンターをテストできるGUIアプリケーションも役立つ場合があります。

package [your package name]

import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.awt.*
import java.awt.event.ComponentAdapter
import java.awt.event.ComponentEvent
import javax.swing.JFrame
import javax.swing.JPanel
import javax.swing.JTextArea
import javax.swing.SwingUtilities

class MainJTextArea(
    private val l: Logger
): JPanel(GridBagLayout()) {

    init {
        val inputStr = "Lorem ipsum dolor sit amet, consectetur adipisicing\n elit, sed do eiusmo," +
                " Lorem ipsum \ndolor sit amet, consectetur adipisicing elit, sed do eiusmo," +
                " Lorem ipsum dolor sit amet, \nconsectetur adipisicing elit, sed do eiusmo," +
                " Lorem ipsum dolor sit amet, \nconsectetur adipisicing elit, sed do eiusmo"

        val textArea = drawTextArea(
            text = inputStr,
            fontSize = 12.0
        )
        val textAreaLineCounter = TextAreaLineCounter(textArea)

        // Add Components to this panel.
        val c = GridBagConstraints().apply {
            gridwidth = GridBagConstraints.REMAINDER
            fill = GridBagConstraints.BOTH
            weightx = 1.0
            weighty = 1.0
        }
        add(textArea, c)
        addComponentListener(object : ComponentAdapter() {
            override fun componentResized(e: ComponentEvent?) {
                super.componentResized(e)
                l.debug("Line count: ${textAreaLineCounter.countLines()}")
            }
        })
    }

    private fun drawTextArea(
        text: String,
        fontSize: Double = 12.0
    ): JTextArea {
        val textArea = JTextArea(text)
        textArea.size = Dimension(width, height)
        textArea.foreground = Color.BLACK
        textArea.background = Color(0, 0, 0, 0)
        textArea.font = Font(null, Font.LAYOUT_LEFT_TO_RIGHT, fontSize.toInt())
        textArea.lineWrap = true
        textArea.wrapStyleWord = true
        return textArea
    }

    companion object {
        @JvmStatic
        fun main(args: Array<String>) {
            val logger = LoggerFactory.getLogger(MainJTextArea::class.java)!!
            SwingUtilities.invokeLater {
                val frame = JFrame("JTextAreaLineCountDemo").apply {
                    preferredSize = Dimension(400, 360)
                    defaultCloseOperation = JFrame.EXIT_ON_CLOSE
                    add(MainJTextArea(logger))
                    pack()
                }
                frame.isVisible = true
            }
        }
    }
}

アップデート

さらに調査したところ、電卓にはまだ問題があり、少しカスタマイズする必要があることに気付きました。そこで、計算メカニズムを改善して、内部で構成されたテキスト区切りを詳細に提供できるようにしました。

このメカニズムはほとんどの場合機能します。JTextArea検出されなかった空の行で折り返されるケースがいくつかあることに気づきました。したがって、自己責任でコードを使用してください。

/**
 * Parses text to fit in [TextProvider.formatWidth] and wraps whenever needed
 */
class TextAreaLineCounter(
    private val textProvider: TextProvider
) {

    private val formatWidth: Float
        get() = textProvider.formatWidth

    fun parseParagraph(
        font: Font,
        fontMetrics: FontMetrics
    ): WrappedParagraph {
        return countLinesInParagraphs(
            textRaw = textProvider.text,
            font = font,
            fontMetrics = fontMetrics,
            formatWidth = formatWidth
        )
    }

    /**
     * Counts lines in [JTextArea]
     * Includes line breaks ('\n')
     */
    private fun countLinesInParagraphs(
        textRaw: String,
        font: Font,
        fontMetrics: FontMetrics,
        formatWidth: Float
    ): WrappedParagraph {
        val paragraphsAsString: List<String> = textRaw.split("\n")
        val sentences = paragraphsAsString.map { paragraph ->
            countLinesInSentence(paragraph, font, fontMetrics, formatWidth)
        }
        return WrappedParagraph(sentences = sentences)
    }

    /**
     * Counts lines in wrapped [JTextArea]
     * Does not include line breaks.
     */
    private fun countLinesInSentence(
        textRaw: String,
        font: Font,
        fontMetrics: FontMetrics,
        formatWidth: Float
    ): Sentence {
        if (textRaw.isEmpty()) {
            return Sentence(
                wraps = listOf(
                    SentenceWrap(
                        wrapIndex = -1,
                        wrapText = textRaw
                    )
                )
            )
        }
        val text = AttributedString(textRaw)
        text.addAttribute(TextAttribute.FONT, font)
        val frc = fontMetrics.fontRenderContext
        val charIt = text.iterator
        val words = mutableListOf<SentenceWrap>()
        val lineMeasurer = LineBreakMeasurer(
            charIt,
            BreakIterator.getLineInstance(),
            frc
        )
        lineMeasurer.position = charIt.beginIndex
        var posBegin = 0
        var posEnd = lineMeasurer.position
        var noLines = 0
        do {
            lineMeasurer.nextLayout(formatWidth)
            posBegin = posEnd
            posEnd = lineMeasurer.position
            words.add(
                SentenceWrap(
                    wrapIndex = noLines,
                    wrapText = textRaw.substring(posBegin, posEnd)
                )
            )
            noLines++
        } while (posEnd < charIt.endIndex)
        return Sentence(words)
    }

    /**
     * Holds wrapped [Sentence]s that break during 'wrap text' or text break symbols
     */
    data class WrappedParagraph(
        val sentences: List<Sentence>
    ) {
        fun lineCount(): Int {
            val sentenceCount = sentences.fold(0) { currentCount: Int, sentence: Sentence ->
                val newCount = currentCount + sentence.lineCount()
                newCount
            }
            return sentenceCount
        }
    }

    /**
     * Sentence contains text pieces which are broken by 'wrapText'
     */
    data class Sentence(
        val wraps: List<SentenceWrap>
    ) {
        fun lineCount(): Int = wraps.size
    }

    /**
     * Entity for holding a wrapped text snippet
     */
    data class SentenceWrap(
        val wrapIndex: Int,
        val wrapText: String
    )

    interface TextProvider {
        val text: String
        val formatWidth: Float
    }

    companion object {
        val l = LoggerFactory.getLogger(TextAreaLineCounter::class.java)!!
    }
}
于 2022-03-01T10:15:47.163 に答える