diff --git a/qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/qqface/QDQQFaceUsageFragment.kt b/qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/qqface/QDQQFaceUsageFragment.kt index d2ca7da7..8b5ef0cd 100644 --- a/qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/qqface/QDQQFaceUsageFragment.kt +++ b/qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/qqface/QDQQFaceUsageFragment.kt @@ -159,7 +159,7 @@ class QDQQFaceUsageFragment : BaseFragment() { } private fun initData() { - val textParser: TextParser = EmojiTextParser(QDQQFaceManager.getInstance()) + val textParser: TextParser = EmojiTextParser(QDQQFaceManager.getInstance()) { true } mMarqueeTypeView1!!.fadeWidth = QMUIDisplayHelper.dp2px(context, 40).toFloat() mMarqueeTypeView1!!.textParser = textParser mMarqueeTypeView1!!.text = "🙃🙃🙃🙃飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀这是一行很长很长[微笑][微笑][微笑][微笑]的文本,但是[微笑][微笑][微笑][微笑]只能单行显示" diff --git a/type/src/main/java/com/qmuiteam/qmui/type/Line.kt b/type/src/main/java/com/qmuiteam/qmui/type/Line.kt index 7c5fab28..991416e2 100644 --- a/type/src/main/java/com/qmuiteam/qmui/type/Line.kt +++ b/type/src/main/java/com/qmuiteam/qmui/type/Line.kt @@ -17,7 +17,6 @@ package com.qmuiteam.qmui.type import android.graphics.Canvas import androidx.core.util.Pools -import com.qmuiteam.qmui.type.TypeEnvironment import com.qmuiteam.qmui.type.element.BreakWordLineElement import com.qmuiteam.qmui.type.element.Element import com.qmuiteam.qmui.type.element.NextParagraphElement @@ -86,7 +85,7 @@ class Line private constructor() { } } - fun handleWordBreak(environment: TypeEnvironment?, shouldHandleWordBreak: Boolean): List? { + fun handleWordBreak(environment: TypeEnvironment, shouldHandleWordBreak: Boolean): List? { if (mElements.size == 0 || !shouldHandleWordBreak) { return null } @@ -96,7 +95,8 @@ class Line private constructor() { val back: MutableList = LinkedList() if (last.wordPart == Element.WORD_PART_WHOLE) { if (last.lineBreakType == Element.LINE_BREAK_TYPE_NOT_END || - next != null && next.lineBreakType == Element.LINE_BREAK_TYPE_NOT_START) { + next != null && next.lineBreakType == Element.LINE_BREAK_TYPE_NOT_START + ) { mElements.removeAt(lastIndex) back.add(last) } @@ -109,7 +109,7 @@ class Line private constructor() { back.add(last) mElements.removeAt(lastIndex) lastIndex-- - val min = Math.max(0, lastIndex - 30) // try 30 letter. + val min = 0.coerceAtLeast(lastIndex - environment.workBreakMaxTryLength) var find = false while (lastIndex > min) { val el = mElements[lastIndex] @@ -119,10 +119,17 @@ class Line private constructor() { } else if (el.lineBreakType == Element.LINE_BREAK_WORD_BREAK_ALLOWED) { // TODO what if environment had changed after break? the measure may be wrong val b = BreakWordLineElement() - b.measure(environment!!) - add(b) - find = true - break + b.measure(environment) + val backWidth = back.sumOf { it.measureWidth } + if (backWidth >= b.measureWidth) { + find = true + add(b) + break + } else { + back.add(0, el) + mElements.removeAt(lastIndex) + lastIndex-- + } } else { back.add(0, el) mElements.removeAt(lastIndex) @@ -139,7 +146,7 @@ class Line private constructor() { return null } for (el in back) { - contentWidth = (contentWidth -el.measureWidth).toInt() + contentWidth -= el.measureWidth } return back } @@ -171,8 +178,9 @@ class Line private constructor() { for (i in 1 until mElements.size) { val el = mElements[i] if (el.visible != Element.GONE && - (el.wordPart == Element.WORD_PART_WHOLE || - el.wordPart == Element.WORD_PART_START)) { + (el.wordPart == Element.WORD_PART_WHOLE || + el.wordPart == Element.WORD_PART_START) + ) { ret++ } } @@ -209,7 +217,8 @@ class Line private constructor() { for (i in mElements.indices) { val el = mElements[i] if (i > 0 && (el.wordPart == Element.WORD_PART_WHOLE - || el.wordPart == Element.WORD_PART_START)) { + || el.wordPart == Element.WORD_PART_START) + ) { x += addSpace mElements[i - 1].nextGapWidth = addSpace } diff --git a/type/src/main/java/com/qmuiteam/qmui/type/LineLayout.kt b/type/src/main/java/com/qmuiteam/qmui/type/LineLayout.kt index a5b479b6..5a454f5d 100644 --- a/type/src/main/java/com/qmuiteam/qmui/type/LineLayout.kt +++ b/type/src/main/java/com/qmuiteam/qmui/type/LineLayout.kt @@ -33,6 +33,7 @@ class LineLayout { var moreUnderlineColor = Color.TRANSPARENT var moreBgColor = 0 var moreUnderlineHeight = 0 + var moreTextFixAtEnd: Boolean = true var typeModel: TypeModel? = null var shouldHandleWordBreak: Boolean = true @@ -234,7 +235,12 @@ class LineLayout { } lastLine.layout(env, dropLastIfSpace, true) if (moreElement != null) { - moreElement.x = lastLine.x + lastLine.widthLimit - moreElement.measureWidth + if(moreTextFixAtEnd){ + moreElement.x = lastLine.x + lastLine.widthLimit - moreElement.measureWidth + }else{ + moreElement.x = lastLine.contentWidth - moreElement.measureWidth + } + } } diff --git a/type/src/main/java/com/qmuiteam/qmui/type/TypeEnvironment.kt b/type/src/main/java/com/qmuiteam/qmui/type/TypeEnvironment.kt index fd470ea7..ca1a9b84 100644 --- a/type/src/main/java/com/qmuiteam/qmui/type/TypeEnvironment.kt +++ b/type/src/main/java/com/qmuiteam/qmui/type/TypeEnvironment.kt @@ -72,7 +72,7 @@ class TypeEnvironment { private val mCustomProp: SparseArray = SparseArray() private val mStack = SparseArray>() - val layoutExcludeAreas = arrayListOf() + var workBreakMaxTryLength: Int = 10 var lineSpace = 0 diff --git a/type/src/main/java/com/qmuiteam/qmui/type/parser/EmojiTextParser.kt b/type/src/main/java/com/qmuiteam/qmui/type/parser/EmojiTextParser.kt index 7be390e0..86b8630c 100644 --- a/type/src/main/java/com/qmuiteam/qmui/type/parser/EmojiTextParser.kt +++ b/type/src/main/java/com/qmuiteam/qmui/type/parser/EmojiTextParser.kt @@ -22,7 +22,10 @@ import com.qmuiteam.qmui.type.element.NextParagraphElement import com.qmuiteam.qmui.type.element.TextElement import java.util.* -class EmojiTextParser(private val emojiProvider: EmojiResourceProvider) : TextParser { +class EmojiTextParser( + private val emojiProvider: EmojiResourceProvider, + private val wordBreakChecker: (c: Char) -> Boolean +) : TextParser { override fun parse(text: CharSequence?): TypeModel? { if(text == null || text.isEmpty()){ @@ -105,7 +108,7 @@ class EmojiTextParser(private val emojiProvider: EmojiResourceProvider) : TextPa i += charCount - 1 } } - ParserHelper.handleWordPart(c, last, tmp!!) + ParserHelper.handleWordPart(c, last, tmp!!, wordBreakChecker) index++ if (first == null) { first = tmp diff --git a/type/src/main/java/com/qmuiteam/qmui/type/parser/ParserHelper.kt b/type/src/main/java/com/qmuiteam/qmui/type/parser/ParserHelper.kt index b1820800..e1bf024c 100644 --- a/type/src/main/java/com/qmuiteam/qmui/type/parser/ParserHelper.kt +++ b/type/src/main/java/com/qmuiteam/qmui/type/parser/ParserHelper.kt @@ -23,16 +23,20 @@ object ParserHelper { return c in 'a'..'z' || c in 'A'..'Z' || c in '0'..'9' } - fun handleWordPart(c: Char, prev: Element?, curr: Element) { + fun handleWordPart(c: Char, prev: Element?, curr: Element, wordBreakChecker: (c: Char) -> Boolean) { if (isEnglishLetterOrNumber(c)) { if (prev == null || prev.wordPart == Element.WORD_PART_WHOLE || prev.wordPart == Element.WORD_PART_END) { curr.wordPart = Element.WORD_PART_START } else { curr.wordPart = Element.WORD_PART_MIDDLE + if(wordBreakChecker(c)){ + curr.lineBreakType = Element.LINE_BREAK_WORD_BREAK_ALLOWED + } } } else { if (prev != null && prev.wordPart == Element.WORD_PART_MIDDLE) { prev.wordPart = Element.WORD_PART_END + prev.lineBreakType = Element.LINE_BREAK_TYPE_NORMAL } curr.wordPart = Element.WORD_PART_WHOLE } diff --git a/type/src/main/java/com/qmuiteam/qmui/type/parser/PlainTextParser.kt b/type/src/main/java/com/qmuiteam/qmui/type/parser/PlainTextParser.kt index a2304e04..69a928eb 100644 --- a/type/src/main/java/com/qmuiteam/qmui/type/parser/PlainTextParser.kt +++ b/type/src/main/java/com/qmuiteam/qmui/type/parser/PlainTextParser.kt @@ -22,11 +22,15 @@ import com.qmuiteam.qmui.type.element.TextElement import com.qmuiteam.qmui.type.parser.ParserHelper.handleWordPart import java.util.* -class PlainTextParser : TextParser { +class PlainTextParser( + private val wordBreakChecker: (c: Char) -> Boolean +) : TextParser { companion object { val instance by lazy { - PlainTextParser() + PlainTextParser { + false + } } } @@ -58,7 +62,7 @@ class PlainTextParser : TextParser { tmp = TextElement(text.subSequence(i, i + charCount), index, i) i += charCount - 1 } - handleWordPart(c, last, tmp) + handleWordPart(c, last, tmp, wordBreakChecker) index++ if (first == null) { first = tmp