|
@@ -4,7 +4,6 @@ import android.content.Context
|
|
|
import android.graphics.Canvas
|
|
import android.graphics.Canvas
|
|
|
import android.graphics.Paint
|
|
import android.graphics.Paint
|
|
|
import android.graphics.RectF
|
|
import android.graphics.RectF
|
|
|
-import android.text.StaticLayout
|
|
|
|
|
import android.text.TextPaint
|
|
import android.text.TextPaint
|
|
|
import android.util.AttributeSet
|
|
import android.util.AttributeSet
|
|
|
import android.view.View
|
|
import android.view.View
|
|
@@ -16,10 +15,7 @@ import io.legado.app.help.config.AppConfig
|
|
|
import io.legado.app.help.config.ReadBookConfig
|
|
import io.legado.app.help.config.ReadBookConfig
|
|
|
import io.legado.app.lib.theme.accentColor
|
|
import io.legado.app.lib.theme.accentColor
|
|
|
import io.legado.app.model.ReadBook
|
|
import io.legado.app.model.ReadBook
|
|
|
-import io.legado.app.ui.book.read.page.entities.TextColumn
|
|
|
|
|
-import io.legado.app.ui.book.read.page.entities.TextLine
|
|
|
|
|
-import io.legado.app.ui.book.read.page.entities.TextPage
|
|
|
|
|
-import io.legado.app.ui.book.read.page.entities.TextPos
|
|
|
|
|
|
|
+import io.legado.app.ui.book.read.page.entities.*
|
|
|
import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
|
import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
|
|
import io.legado.app.ui.book.read.page.provider.ImageProvider
|
|
import io.legado.app.ui.book.read.page.provider.ImageProvider
|
|
|
import io.legado.app.ui.book.read.page.provider.TextPageFactory
|
|
import io.legado.app.ui.book.read.page.provider.TextPageFactory
|
|
@@ -168,86 +164,19 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
reviewCountPaint.textSize = textPaint.textSize * 0.6F
|
|
reviewCountPaint.textSize = textPaint.textSize * 0.6F
|
|
|
reviewCountPaint.color = textColor
|
|
reviewCountPaint.color = textColor
|
|
|
textLine.columns.forEach {
|
|
textLine.columns.forEach {
|
|
|
- when (it.style) {
|
|
|
|
|
- 0 -> {
|
|
|
|
|
|
|
+ when (it) {
|
|
|
|
|
+ is TextColumn -> {
|
|
|
textPaint.color = textColor
|
|
textPaint.color = textColor
|
|
|
if (it.isSearchResult) {
|
|
if (it.isSearchResult) {
|
|
|
textPaint.color = context.accentColor
|
|
textPaint.color = context.accentColor
|
|
|
}
|
|
}
|
|
|
canvas.drawText(it.charData, it.start, lineBase, textPaint)
|
|
canvas.drawText(it.charData, it.start, lineBase, textPaint)
|
|
|
|
|
+ if (it.selected) {
|
|
|
|
|
+ canvas.drawRect(it.start, lineTop, it.end, lineBottom, selectedPaint)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- 1 -> drawImage(canvas, textPage, textLine, it, lineTop, lineBottom)
|
|
|
|
|
- 2 -> {
|
|
|
|
|
- if (textLine.reviewCount <= 0) return@forEach
|
|
|
|
|
- canvas.drawLine(
|
|
|
|
|
- it.start,
|
|
|
|
|
- lineBase - textPaint.textSize * 2 / 5,
|
|
|
|
|
- it.start + textPaint.textSize / 6,
|
|
|
|
|
- lineBase - textPaint.textSize / 4,
|
|
|
|
|
- linePaint
|
|
|
|
|
- )
|
|
|
|
|
- canvas.drawLine(
|
|
|
|
|
- it.start,
|
|
|
|
|
- lineBase - textPaint.textSize * 0.38F,
|
|
|
|
|
- it.start + textPaint.textSize / 6,
|
|
|
|
|
- lineBase - textPaint.textSize * 0.55F,
|
|
|
|
|
- linePaint
|
|
|
|
|
- )
|
|
|
|
|
- canvas.drawLine(
|
|
|
|
|
- it.start + textPaint.textSize / 6,
|
|
|
|
|
- lineBase - textPaint.textSize / 4,
|
|
|
|
|
- it.start + textPaint.textSize / 6,
|
|
|
|
|
- lineBase,
|
|
|
|
|
- linePaint
|
|
|
|
|
- )
|
|
|
|
|
- canvas.drawLine(
|
|
|
|
|
- it.start + textPaint.textSize / 6,
|
|
|
|
|
- lineBase - textPaint.textSize * 0.55F,
|
|
|
|
|
- it.start + textPaint.textSize / 6,
|
|
|
|
|
- lineBase - textPaint.textSize * 0.8F,
|
|
|
|
|
- linePaint
|
|
|
|
|
- )
|
|
|
|
|
- canvas.drawLine(
|
|
|
|
|
- it.start + textPaint.textSize / 6,
|
|
|
|
|
- lineBase,
|
|
|
|
|
- it.start + textPaint.textSize * 1.6F,
|
|
|
|
|
- lineBase,
|
|
|
|
|
- linePaint
|
|
|
|
|
- )
|
|
|
|
|
- canvas.drawLine(
|
|
|
|
|
- it.start + textPaint.textSize / 6,
|
|
|
|
|
- lineBase - textPaint.textSize * 0.8F,
|
|
|
|
|
- it.start + textPaint.textSize * 1.6F,
|
|
|
|
|
- lineBase - textPaint.textSize * 0.8F,
|
|
|
|
|
- linePaint
|
|
|
|
|
- )
|
|
|
|
|
- canvas.drawLine(
|
|
|
|
|
- it.start + textPaint.textSize * 1.6F,
|
|
|
|
|
- lineBase - textPaint.textSize * 0.8F,
|
|
|
|
|
- it.start + textPaint.textSize * 1.6F,
|
|
|
|
|
- lineBase,
|
|
|
|
|
- linePaint
|
|
|
|
|
- )
|
|
|
|
|
- if (textLine.reviewCount < 100) canvas.drawText(
|
|
|
|
|
- textLine.reviewCount.toString(),
|
|
|
|
|
- it.start + textPaint.textSize * 0.87F -
|
|
|
|
|
- StaticLayout.getDesiredWidth(
|
|
|
|
|
- textLine.reviewCount.toString(),
|
|
|
|
|
- reviewCountPaint
|
|
|
|
|
- ) / 2,
|
|
|
|
|
- lineBase - textPaint.textSize / 6,
|
|
|
|
|
- reviewCountPaint
|
|
|
|
|
- )
|
|
|
|
|
- else canvas.drawText(
|
|
|
|
|
- "99+",
|
|
|
|
|
- it.start + textPaint.textSize * 0.35F,
|
|
|
|
|
- lineBase - textPaint.textSize / 6,
|
|
|
|
|
- reviewCountPaint
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- if (it.selected) {
|
|
|
|
|
- canvas.drawRect(it.start, lineTop, it.end, lineBottom, selectedPaint)
|
|
|
|
|
|
|
+ is ImageColumn -> drawImage(canvas, textPage, textLine, it, lineTop, lineBottom)
|
|
|
|
|
+ is ReviewColumn -> it.drawToCanvas(canvas, linePaint, reviewCountPaint, lineBase)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -260,7 +189,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
canvas: Canvas,
|
|
canvas: Canvas,
|
|
|
textPage: TextPage,
|
|
textPage: TextPage,
|
|
|
textLine: TextLine,
|
|
textLine: TextLine,
|
|
|
- textColumn: TextColumn,
|
|
|
|
|
|
|
+ column: ImageColumn,
|
|
|
lineTop: Float,
|
|
lineTop: Float,
|
|
|
lineBottom: Float
|
|
lineBottom: Float
|
|
|
) {
|
|
) {
|
|
@@ -278,7 +207,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
isVisible &&
|
|
isVisible &&
|
|
|
!cacheIncreased &&
|
|
!cacheIncreased &&
|
|
|
ImageProvider.isTriggerRecycled() &&
|
|
ImageProvider.isTriggerRecycled() &&
|
|
|
- !ImageProvider.isImageAlive(book, textColumn.charData)
|
|
|
|
|
|
|
+ !ImageProvider.isImageAlive(book, column.src)
|
|
|
) {
|
|
) {
|
|
|
val newSize = ImageProvider.bitmapLruCache.maxSize() + increaseSize
|
|
val newSize = ImageProvider.bitmapLruCache.maxSize() + increaseSize
|
|
|
if (newSize < maxCacheSize) {
|
|
if (newSize < maxCacheSize) {
|
|
@@ -290,8 +219,8 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
}
|
|
}
|
|
|
val bitmap = ImageProvider.getImage(
|
|
val bitmap = ImageProvider.getImage(
|
|
|
book,
|
|
book,
|
|
|
- textColumn.charData,
|
|
|
|
|
- (textColumn.end - textColumn.start).toInt(),
|
|
|
|
|
|
|
+ column.src,
|
|
|
|
|
+ (column.end - column.start).toInt(),
|
|
|
(lineBottom - lineTop).toInt()
|
|
(lineBottom - lineTop).toInt()
|
|
|
) {
|
|
) {
|
|
|
if (!drawVisibleImageOnly && isVisible) {
|
|
if (!drawVisibleImageOnly && isVisible) {
|
|
@@ -301,12 +230,12 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
} ?: return
|
|
} ?: return
|
|
|
|
|
|
|
|
val rectF = if (textLine.isImage) {
|
|
val rectF = if (textLine.isImage) {
|
|
|
- RectF(textColumn.start, lineTop, textColumn.end, lineBottom)
|
|
|
|
|
|
|
+ RectF(column.start, lineTop, column.end, lineBottom)
|
|
|
} else {
|
|
} else {
|
|
|
/*以宽度为基准保持图片的原始比例叠加,当div为负数时,允许高度比字符更高*/
|
|
/*以宽度为基准保持图片的原始比例叠加,当div为负数时,允许高度比字符更高*/
|
|
|
- val h = (textColumn.end - textColumn.start) / bitmap.width * bitmap.height
|
|
|
|
|
|
|
+ val h = (column.end - column.start) / bitmap.width * bitmap.height
|
|
|
val div = (lineBottom - lineTop - h) / 2
|
|
val div = (lineBottom - lineTop - h) / 2
|
|
|
- RectF(textColumn.start, lineTop + div, textColumn.end, lineBottom - div)
|
|
|
|
|
|
|
+ RectF(column.start, lineTop + div, column.end, lineBottom - div)
|
|
|
}
|
|
}
|
|
|
kotlin.runCatching {
|
|
kotlin.runCatching {
|
|
|
canvas.drawBitmap(bitmap, null, rectF, imagePaint)
|
|
canvas.drawBitmap(bitmap, null, rectF, imagePaint)
|
|
@@ -360,15 +289,15 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
y: Float,
|
|
y: Float,
|
|
|
select: (textPos: TextPos) -> Unit,
|
|
select: (textPos: TextPos) -> Unit,
|
|
|
) {
|
|
) {
|
|
|
- touch(x, y) { _, textPos, _, _, textColumn ->
|
|
|
|
|
- if (textColumn.style == 2) return@touch
|
|
|
|
|
- if (textColumn.style == 1) {
|
|
|
|
|
- callBack.onImageLongPress(x, y, textColumn.charData)
|
|
|
|
|
- } else {
|
|
|
|
|
- if (!selectAble) return@touch
|
|
|
|
|
- textColumn.selected = true
|
|
|
|
|
- invalidate()
|
|
|
|
|
- select(textPos)
|
|
|
|
|
|
|
+ touch(x, y) { _, textPos, _, _, column ->
|
|
|
|
|
+ when (column) {
|
|
|
|
|
+ is ImageColumn -> callBack.onImageLongPress(x, y, column.src)
|
|
|
|
|
+ is TextColumn -> {
|
|
|
|
|
+ if (!selectAble) return@touch
|
|
|
|
|
+ column.selected = true
|
|
|
|
|
+ invalidate()
|
|
|
|
|
+ select(textPos)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -379,8 +308,8 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
*/
|
|
*/
|
|
|
fun click(x: Float, y: Float): Boolean {
|
|
fun click(x: Float, y: Float): Boolean {
|
|
|
var handled = false
|
|
var handled = false
|
|
|
- touch(x, y) { _, textPos, textPage, textLine, textColumn ->
|
|
|
|
|
- if (textColumn.style == 2) {
|
|
|
|
|
|
|
+ touch(x, y) { _, textPos, textPage, textLine, column ->
|
|
|
|
|
+ if (column is ReviewColumn) {
|
|
|
context.toastOnUi("Button Pressed!")
|
|
context.toastOnUi("Button Pressed!")
|
|
|
handled = true
|
|
handled = true
|
|
|
}
|
|
}
|
|
@@ -396,11 +325,12 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
y: Float,
|
|
y: Float,
|
|
|
select: (textPos: TextPos) -> Unit,
|
|
select: (textPos: TextPos) -> Unit,
|
|
|
) {
|
|
) {
|
|
|
- touch(x, y) { _, textPos, _, _, textColumn ->
|
|
|
|
|
- if (textColumn.style == 2) return@touch
|
|
|
|
|
- textColumn.selected = true
|
|
|
|
|
- invalidate()
|
|
|
|
|
- select(textPos)
|
|
|
|
|
|
|
+ touch(x, y) { _, textPos, _, _, column ->
|
|
|
|
|
+ if (column is TextColumn) {
|
|
|
|
|
+ column.selected = true
|
|
|
|
|
+ invalidate()
|
|
|
|
|
+ select(textPos)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -450,7 +380,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
textPos: TextPos,
|
|
textPos: TextPos,
|
|
|
textPage: TextPage,
|
|
textPage: TextPage,
|
|
|
textLine: TextLine,
|
|
textLine: TextLine,
|
|
|
- textColumn: TextColumn
|
|
|
|
|
|
|
+ column: BaseColumn
|
|
|
) -> Unit
|
|
) -> Unit
|
|
|
) {
|
|
) {
|
|
|
if (!visibleRect.contains(x, y)) return
|
|
if (!visibleRect.contains(x, y)) return
|
|
@@ -518,13 +448,14 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
textPos.relativePagePos = relativePos
|
|
textPos.relativePagePos = relativePos
|
|
|
for ((lineIndex, textLine) in relativePage(relativePos).lines.withIndex()) {
|
|
for ((lineIndex, textLine) in relativePage(relativePos).lines.withIndex()) {
|
|
|
textPos.lineIndex = lineIndex
|
|
textPos.lineIndex = lineIndex
|
|
|
- for ((charIndex, textColumn) in textLine.columns.withIndex()) {
|
|
|
|
|
|
|
+ for ((charIndex, column) in textLine.columns.withIndex()) {
|
|
|
textPos.charIndex = charIndex
|
|
textPos.charIndex = charIndex
|
|
|
- if (textColumn.style == 2) continue
|
|
|
|
|
- textColumn.selected =
|
|
|
|
|
- textPos.compare(selectStart) >= 0 && textPos.compare(selectEnd) <= 0
|
|
|
|
|
- textColumn.isSearchResult =
|
|
|
|
|
- textColumn.selected && callBack.isSelectingSearchResult
|
|
|
|
|
|
|
+ if (column is TextColumn) {
|
|
|
|
|
+ column.selected =
|
|
|
|
|
+ textPos.compare(selectStart) >= 0 && textPos.compare(selectEnd) <= 0
|
|
|
|
|
+ column.isSearchResult =
|
|
|
|
|
+ column.selected && callBack.isSelectingSearchResult
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -544,8 +475,10 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
for (relativePos in 0..last) {
|
|
for (relativePos in 0..last) {
|
|
|
relativePage(relativePos).lines.forEach { textLine ->
|
|
relativePage(relativePos).lines.forEach { textLine ->
|
|
|
textLine.columns.forEach {
|
|
textLine.columns.forEach {
|
|
|
- it.selected = false
|
|
|
|
|
- if (fromSearchExit) it.isSearchResult = false
|
|
|
|
|
|
|
+ if (it is TextColumn) {
|
|
|
|
|
+ it.selected = false
|
|
|
|
|
+ if (fromSearchExit) it.isSearchResult = false
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -561,12 +494,14 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
textPos.relativePagePos = relativePos
|
|
textPos.relativePagePos = relativePos
|
|
|
textPage.lines.forEachIndexed { lineIndex, textLine ->
|
|
textPage.lines.forEachIndexed { lineIndex, textLine ->
|
|
|
textPos.lineIndex = lineIndex
|
|
textPos.lineIndex = lineIndex
|
|
|
- textLine.columns.forEachIndexed { charIndex, textColumn ->
|
|
|
|
|
|
|
+ textLine.columns.forEachIndexed { charIndex, column ->
|
|
|
textPos.charIndex = charIndex
|
|
textPos.charIndex = charIndex
|
|
|
val compareStart = textPos.compare(selectStart)
|
|
val compareStart = textPos.compare(selectStart)
|
|
|
val compareEnd = textPos.compare(selectEnd)
|
|
val compareEnd = textPos.compare(selectEnd)
|
|
|
if (compareStart >= 0 && compareEnd <= 0) {
|
|
if (compareStart >= 0 && compareEnd <= 0) {
|
|
|
- builder.append(textColumn.charData)
|
|
|
|
|
|
|
+ if (column is TextColumn) {
|
|
|
|
|
+ builder.append(column.charData)
|
|
|
|
|
+ }
|
|
|
if (
|
|
if (
|
|
|
textLine.isParagraphEnd
|
|
textLine.isParagraphEnd
|
|
|
&& charIndex == textLine.charSize - 1
|
|
&& charIndex == textLine.charSize - 1
|