|
|
|
@ -4,7 +4,6 @@ import android.content.Context |
|
|
|
|
import android.graphics.Canvas |
|
|
|
|
import android.graphics.Paint |
|
|
|
|
import android.graphics.RectF |
|
|
|
|
import android.text.StaticLayout |
|
|
|
|
import android.text.TextPaint |
|
|
|
|
import android.util.AttributeSet |
|
|
|
|
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.lib.theme.accentColor |
|
|
|
|
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.ImageProvider |
|
|
|
|
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.color = textColor |
|
|
|
|
textLine.columns.forEach { |
|
|
|
|
when (it.style) { |
|
|
|
|
0 -> { |
|
|
|
|
when (it) { |
|
|
|
|
is TextColumn -> { |
|
|
|
|
textPaint.color = textColor |
|
|
|
|
if (it.isSearchResult) { |
|
|
|
|
textPaint.color = context.accentColor |
|
|
|
|
} |
|
|
|
|
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, |
|
|
|
|
textPage: TextPage, |
|
|
|
|
textLine: TextLine, |
|
|
|
|
textColumn: TextColumn, |
|
|
|
|
column: ImageColumn, |
|
|
|
|
lineTop: Float, |
|
|
|
|
lineBottom: Float |
|
|
|
|
) { |
|
|
|
@ -278,7 +207,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at |
|
|
|
|
isVisible && |
|
|
|
|
!cacheIncreased && |
|
|
|
|
ImageProvider.isTriggerRecycled() && |
|
|
|
|
!ImageProvider.isImageAlive(book, textColumn.charData) |
|
|
|
|
!ImageProvider.isImageAlive(book, column.src) |
|
|
|
|
) { |
|
|
|
|
val newSize = ImageProvider.bitmapLruCache.maxSize() + increaseSize |
|
|
|
|
if (newSize < maxCacheSize) { |
|
|
|
@ -290,8 +219,8 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at |
|
|
|
|
} |
|
|
|
|
val bitmap = ImageProvider.getImage( |
|
|
|
|
book, |
|
|
|
|
textColumn.charData, |
|
|
|
|
(textColumn.end - textColumn.start).toInt(), |
|
|
|
|
column.src, |
|
|
|
|
(column.end - column.start).toInt(), |
|
|
|
|
(lineBottom - lineTop).toInt() |
|
|
|
|
) { |
|
|
|
|
if (!drawVisibleImageOnly && isVisible) { |
|
|
|
@ -301,12 +230,12 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at |
|
|
|
|
} ?: return |
|
|
|
|
|
|
|
|
|
val rectF = if (textLine.isImage) { |
|
|
|
|
RectF(textColumn.start, lineTop, textColumn.end, lineBottom) |
|
|
|
|
RectF(column.start, lineTop, column.end, lineBottom) |
|
|
|
|
} else { |
|
|
|
|
/*以宽度为基准保持图片的原始比例叠加,当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 |
|
|
|
|
RectF(textColumn.start, lineTop + div, textColumn.end, lineBottom - div) |
|
|
|
|
RectF(column.start, lineTop + div, column.end, lineBottom - div) |
|
|
|
|
} |
|
|
|
|
kotlin.runCatching { |
|
|
|
|
canvas.drawBitmap(bitmap, null, rectF, imagePaint) |
|
|
|
@ -360,15 +289,15 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at |
|
|
|
|
y: Float, |
|
|
|
|
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 { |
|
|
|
|
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!") |
|
|
|
|
handled = true |
|
|
|
|
} |
|
|
|
@ -396,11 +325,12 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at |
|
|
|
|
y: Float, |
|
|
|
|
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, |
|
|
|
|
textPage: TextPage, |
|
|
|
|
textLine: TextLine, |
|
|
|
|
textColumn: TextColumn |
|
|
|
|
column: BaseColumn |
|
|
|
|
) -> Unit |
|
|
|
|
) { |
|
|
|
|
if (!visibleRect.contains(x, y)) return |
|
|
|
@ -518,13 +448,14 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at |
|
|
|
|
textPos.relativePagePos = relativePos |
|
|
|
|
for ((lineIndex, textLine) in relativePage(relativePos).lines.withIndex()) { |
|
|
|
|
textPos.lineIndex = lineIndex |
|
|
|
|
for ((charIndex, textColumn) in textLine.columns.withIndex()) { |
|
|
|
|
for ((charIndex, column) in textLine.columns.withIndex()) { |
|
|
|
|
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) { |
|
|
|
|
relativePage(relativePos).lines.forEach { textLine -> |
|
|
|
|
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 |
|
|
|
|
textPage.lines.forEachIndexed { lineIndex, textLine -> |
|
|
|
|
textPos.lineIndex = lineIndex |
|
|
|
|
textLine.columns.forEachIndexed { charIndex, textColumn -> |
|
|
|
|
textLine.columns.forEachIndexed { charIndex, column -> |
|
|
|
|
textPos.charIndex = charIndex |
|
|
|
|
val compareStart = textPos.compare(selectStart) |
|
|
|
|
val compareEnd = textPos.compare(selectEnd) |
|
|
|
|
if (compareStart >= 0 && compareEnd <= 0) { |
|
|
|
|
builder.append(textColumn.charData) |
|
|
|
|
if (column is TextColumn) { |
|
|
|
|
builder.append(column.charData) |
|
|
|
|
} |
|
|
|
|
if ( |
|
|
|
|
textLine.isParagraphEnd |
|
|
|
|
&& charIndex == textLine.charSize - 1 |
|
|
|
|