划线逻辑

pull/1033/head
康凡 4 years ago
parent fa7f69c5ef
commit 259fe55870
  1. 4
      app/build.gradle
  2. 16
      app/src/main/java/io/legado/app/data/dao/BookIdealDao.kt
  3. 23
      app/src/main/java/io/legado/app/data/dao/BookIdealDetailDao.kt
  4. 4
      app/src/main/java/io/legado/app/data/dao/BookLineDao.kt
  5. 42
      app/src/main/java/io/legado/app/data/entities/DrawLineEntity.kt
  6. 31
      app/src/main/java/io/legado/app/data/entities/IdealDetailEntity.kt
  7. 53
      app/src/main/java/io/legado/app/data/entities/IdealEntity.kt
  8. 15
      app/src/main/java/io/legado/app/data/entities/IdealRelation.kt
  9. 22
      app/src/main/java/io/legado/app/data/entities/IdealWithDetail.kt
  10. 4
      app/src/main/java/io/legado/app/help/BookHelp.kt
  11. 2
      app/src/main/java/io/legado/app/model/localBook/LocalBook.kt
  12. 37
      app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
  13. 74
      app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt
  14. 29
      app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt
  15. 11
      app/src/main/java/io/legado/app/ui/book/read/page/entities/TextChar.kt
  16. 13
      app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt

@ -10,7 +10,7 @@ static def releaseTime() {
def name = "legado"
def version = "3." + releaseTime()
def gitCommits = Integer.parseInt('git rev-list --count HEAD'.execute([], project.rootDir).text.trim())
// def gitCommits = Integer.parseInt('git rev-list --count HEAD'.execute([], project.rootDir).text.trim())
android {
compileSdkVersion 30
@ -30,7 +30,7 @@ android {
applicationId "io.legado.app"
minSdkVersion 21
targetSdkVersion 30
versionCode gitCommits
versionCode 1
versionName version
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
project.ext.set("archivesBaseName", name + "_" + version)

@ -0,0 +1,16 @@
package io.legado.app.data.dao
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Query
import io.legado.app.data.entities.IdealEntity
@Dao
interface BookIdealDao {
/**
* 根据书名查找想法列表
*/
@Query("SELECT * FROM book_ideals WHERE `bookName` = bookName")
fun getIdealList(bookName: String): LiveData<List<IdealEntity>>
}

@ -0,0 +1,23 @@
package io.legado.app.data.dao
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import io.legado.app.data.entities.IdealDetailEntity
@Dao
interface BookIdealDetailDao {
/**
* 根据想法id查询想法内容列表
*/
@Query("SELECT * FROM book_ideal_list WHERE `idealId` = idealId")
fun getIdealListById(idealId: Long): LiveData<List<IdealDetailEntity>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertIDeal(vararg idealDetail: IdealDetailEntity)
}

@ -0,0 +1,4 @@
package io.legado.app.data.dao
class BookLineDao {
}

@ -0,0 +1,42 @@
package io.legado.app.data.entities
import io.legado.app.R
class DrawLineEntity {
// 第几章的划线
var chapterIndex: Int = 0
// 章节的第几个字符开始
var startIndex: Int = 0
// 本章的第几个字符结束
var endIndex: Int = 0
// 划线的样式
var lineStyle: LineStyle = LineStyle.SHAPE
// 划线颜色
var lineColor: LineColor = LineColor.BLUE
enum class LineColor {
BLUE,
ORANGE,
YELLOW,
GREEN
}
enum class LineStyle {
STEEP, // 直线
WAVE, // 波浪线
SHAPE, // 选中样式
}
fun getLineColor() = when (lineColor) {
LineColor.BLUE -> R.color.md_blue_200
LineColor.GREEN -> R.color.md_green_600
LineColor.ORANGE -> R.color.md_orange_600
LineColor.YELLOW -> R.color.md_yellow_600
}
}

@ -0,0 +1,31 @@
package io.legado.app.data.entities
import android.os.Parcelable
import androidx.room.Entity
import kotlinx.parcelize.Parcelize
@Entity(primaryKeys = ["id", "idealId"], tableName = "book_ideal_list")
@Parcelize
data class IdealDetailEntity(
var id: Long = 0b1,
var idealId: Long, // 整个想法的id
var idealContent: String? = "", // 想法内容
var userAvatar: String? = "", // 用户头像
var assistCount: Int = 0, // 赞数量
var replyCount: Int = 0 // 回复数量
) : Parcelable {
override fun hashCode(): Int {
return id.hashCode()
}
override fun equals(other: Any?): Boolean {
if (other !is IdealDetailEntity) return false
return id == other.id &&
idealId == other.idealId &&
idealContent == other.idealContent &&
userAvatar == other.userAvatar &&
assistCount == other.assistCount &&
replyCount == other.replyCount
}
}

@ -0,0 +1,53 @@
package io.legado.app.data.entities
import android.os.Parcelable
import androidx.room.Entity
import androidx.room.PrimaryKey
import io.legado.app.R
import kotlinx.parcelize.Parcelize
/**
* 想法
*/
@Parcelize
@Entity(tableName = "book_ideals")
data class IdealEntity(
@PrimaryKey
var idealId: Long = 0,
// 在本章里面的第几个想法
var inChapterIndex: Int = 0,
// 书id
var bookName: String = "",
// 第几章的想法
var chapterIndex: Int = 0,
// 章节的第几个字符开始
var startIndex: Int = 0,
// 本章的第几个字符结束
var endIndex: Int = 0
) : Parcelable {
override fun hashCode(): Int {
return idealId.hashCode()
}
override fun equals(other: Any?): Boolean {
if (other is IdealEntity) {
return idealId == other.idealId &&
chapterIndex == other.chapterIndex &&
startIndex == other.startIndex &&
endIndex == other.endIndex
}
return false
}
fun getLineColor() = R.color.md_red_300
}

@ -0,0 +1,15 @@
package io.legado.app.data.entities
import android.os.Parcelable
import androidx.room.Entity
import kotlinx.parcelize.Parcelize
@Entity(
tableName = "ideal_detail_list",
primaryKeys = ["id", "idealId"]
)
@Parcelize
data class IdealRelation(
var id: Long = 0b1,
var idealId: Long = 0b1
) : Parcelable

@ -0,0 +1,22 @@
package io.legado.app.data.entities
import android.os.Parcelable
import androidx.room.Embedded
import androidx.room.Junction
import androidx.room.Relation
import kotlinx.parcelize.Parcelize
@Parcelize
data class IdealWithDetail(
@Embedded
var idealEntity: IdealEntity,
@Relation(
parentColumn = "idealId",
entityColumn = "id",
entity = IdealDetailEntity::class,
associateBy = Junction(IdealRelation::class)
)
var idealList: List<IdealDetailEntity>
) : Parcelable

@ -192,9 +192,9 @@ object BookHelp {
fun getContent(book: Book, bookChapter: BookChapter): String? {
if (book.isLocalTxt()) {
return LocalBook.getContext(book, bookChapter)
return LocalBook.getContent(book, bookChapter)
} else if (book.isEpub() && !hasContent(book, bookChapter)) {
val string = LocalBook.getContext(book, bookChapter)
val string = LocalBook.getContent(book, bookChapter)
string?.let {
FileUtils.createFileIfNotExist(
downloadDir,

@ -29,7 +29,7 @@ object LocalBook {
}
}
fun getContext(book: Book, chapter: BookChapter): String? {
fun getContent(book: Book, chapter: BookChapter): String? {
return if (book.isEpub()) {
EpubFile.getContent(book, chapter)
} else {

@ -420,14 +420,33 @@ class ReadBookActivity : ReadBookBaseActivity(),
MotionEvent.ACTION_DOWN -> textActionMenu.dismiss()
MotionEvent.ACTION_MOVE -> {
when (v.id) {
R.id.cursor_left -> readView.curPage.selectStartMove(
event.rawX + cursorLeft.width,
event.rawY - cursorLeft.height
)
R.id.cursor_right -> readView.curPage.selectEndMove(
event.rawX - cursorRight.width,
event.rawY - cursorRight.height
)
R.id.cursor_left -> {
with(readView) {
curPage.selectStartMoveIndex(
firstRelativePage,
firstLineIndex,
firstCharIndex
)
curPage.selectStartMove(
event.rawX + cursorLeft.width,
event.rawY - cursorLeft.height
)
}
}
R.id.cursor_right -> {
with(readView) {
curPage.selectEndMoveIndex(
firstRelativePage,
firstLineIndex,
firstCharIndex
)
curPage.selectEndMove(
event.rawX + cursorRight.width,
event.rawY - cursorRight.height
)
}
}
}
}
MotionEvent.ACTION_UP -> showTextActionMenu()
@ -540,7 +559,7 @@ class ReadBookActivity : ReadBookBaseActivity(),
*/
override fun onMenuActionFinally() = with(binding) {
textActionMenu.dismiss()
readView.curPage.cancelSelect()
readView.cancelSelect()
readView.isTextSelected = false
}

@ -2,6 +2,7 @@ package io.legado.app.ui.book.read.page
import android.content.Context
import android.graphics.Canvas
import android.graphics.DashPathEffect
import android.graphics.Paint
import android.graphics.RectF
import android.util.AttributeSet
@ -10,6 +11,8 @@ import android.view.View
import io.legado.app.R
import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.Bookmark
import io.legado.app.data.entities.DrawLineEntity
import io.legado.app.data.entities.IdealEntity
import io.legado.app.help.ReadBookConfig
import io.legado.app.lib.theme.accentColor
import io.legado.app.service.help.ReadBook
@ -27,6 +30,7 @@ import io.legado.app.utils.toastOnUi
import kotlinx.coroutines.CoroutineScope
import kotlin.math.min
/**
* 阅读内容界面
*/
@ -39,6 +43,16 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
style = Paint.Style.FILL
}
}
private val drawLinePaint by lazy {
Paint().apply {
color = context.getCompatColor(R.color.btn_bg_press_2)
style = Paint.Style.FILL
strokeWidth = 4f
isAntiAlias = true
isDither = true
}
}
private var callBack: CallBack
private val visibleRect = RectF()
private val selectStart = arrayOf(0, 0, 0)
@ -152,9 +166,59 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
} else {
canvas.drawText(it.charData, it.start, lineBase, textPaint)
}
if (it.selected) {
canvas.drawRect(it.start, lineTop, it.end, lineBottom, selectedPaint)
drawLine(canvas, it, lineTop, lineBottom)
}
}
/**
* 选中划线画想法
*/
private fun drawLine(canvas: Canvas, textChar: TextChar, lineTop: Float, lineBottom: Float) {
// 缩进字符就不选中了
if (textChar.isIndent) return
// todo 划线
val drawLineEntity = textChar.drawLineEntity
val idealEntity = textChar.idealEntity
if (drawLineEntity != null) {
drawLinePaint.pathEffect = null
drawLinePaint.color = context.getCompatColor(drawLineEntity.getLineColor())
when (drawLineEntity.lineStyle) {
DrawLineEntity.LineStyle.SHAPE -> { // 矩形线
canvas.drawRect(
textChar.start,
lineTop,
textChar.end,
lineBottom,
drawLinePaint
)
}
DrawLineEntity.LineStyle.STEEP -> { // 直线
canvas.drawLine(
textChar.start,
lineBottom,
textChar.end,
lineBottom,
drawLinePaint
)
}
else -> { // 波浪线
}
}
} else if (idealEntity != null) {
drawLinePaint.pathEffect = DashPathEffect(floatArrayOf(4f, 4f), 0f)
drawLinePaint.color = context.getCompatColor(idealEntity.getLineColor())
canvas.drawLine(textChar.start, lineBottom, textChar.end, lineBottom, drawLinePaint)
}
// 选中
if (textChar.selected) {
canvas.drawRect(textChar.start, lineTop, textChar.end, lineBottom, selectedPaint)
}
}
@ -260,7 +324,6 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
return
}
}
}
}
@ -282,10 +345,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
for ((charIndex, textChar) in textLine.textChars.withIndex()) {
if (x > textChar.start && x < textChar.end) {
if (selectStart[0] != relativePos || selectStart[1] != lineIndex || selectStart[2] != charIndex) {
if (selectToInt(relativePos, lineIndex, charIndex) > selectToInt(
selectEnd
)
) {
if (selectToInt(relativePos, lineIndex, charIndex) > selectToInt(selectEnd)) {
return
}
selectStart[0] = relativePos

@ -72,9 +72,9 @@ class ReadView(context: Context, attrs: AttributeSet) :
}
var isTextSelected = false
private var pressOnTextSelected = false
private var firstRelativePage = 0
private var firstLineIndex: Int = 0
private var firstCharIndex: Int = 0
var firstRelativePage = 0
var firstLineIndex: Int = 0
var firstCharIndex: Int = 0
val slopSquare by lazy { ViewConfiguration.get(context).scaledTouchSlop }
private val tlRect = RectF()
@ -155,11 +155,19 @@ class ReadView(context: Context, attrs: AttributeSet) :
*/
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
if (event.pointerCount > 1) {
isSelected = false
longPressed = false
removeCallbacks(longPressRunnable)
//cancelSelect()
return super.onTouchEvent(event)
}
callBack.screenOffTimerStart()
when (event.action) {
MotionEvent.ACTION_DOWN -> {
if (isTextSelected) {
curPage.cancelSelect()
cancelSelect()
isTextSelected = false
pressOnTextSelected = true
} else {
@ -245,6 +253,14 @@ class ReadView(context: Context, attrs: AttributeSet) :
pageDelegate?.onScroll()
}
/**
* 取消选中
*/
fun cancelSelect() {
curPage.cancelSelect()
}
/**
* 长按选择
*/
@ -357,6 +373,7 @@ class ReadView(context: Context, attrs: AttributeSet) :
* 单击
*/
private fun onSingleTapUp() {
cancelSelect()
when {
isTextSelected -> isTextSelected = false
mcRect.contains(startX, startY) -> if (!isAbortAnim) {
@ -402,7 +419,7 @@ class ReadView(context: Context, attrs: AttributeSet) :
/**
* 选择文本
*/
private fun selectText(x: Float, y: Float) {
fun selectText(x: Float, y: Float) {
curPage.selectText(x, y) { relativePage, lineIndex, charIndex ->
when {
relativePage > firstRelativePage -> {
@ -435,7 +452,7 @@ class ReadView(context: Context, attrs: AttributeSet) :
fun onDestroy() {
pageDelegate?.onDestroy()
curPage.cancelSelect()
cancelSelect()
}
fun fillPage(direction: PageDirection): Boolean {

@ -1,9 +1,18 @@
package io.legado.app.ui.book.read.page.entities
import io.legado.app.data.entities.DrawLineEntity
import io.legado.app.data.entities.IdealEntity
data class TextChar(
val charData: String,
var start: Float,
var end: Float,
var selected: Boolean = false,
var isImage: Boolean = false
var isImage: Boolean = false,
// 是否是首行缩进字符
var isIndent: Boolean = false,
// 划线
var drawLineEntity: DrawLineEntity? = null,
// 想法
var idealEntity: IdealEntity? = null
)

@ -119,10 +119,10 @@ object ChapterProvider {
if (matcher.find()) {
matcher.group(1)?.let { src ->
//if (!book.isEpub()) {
durY = setTypeImage(
book, bookChapter, src,
durY, textPages, book.getImageStyle()
)
durY = setTypeImage(
book, bookChapter, src,
durY, textPages, book.getImageStyle()
)
//}
}
} else {
@ -324,13 +324,14 @@ object ChapterProvider {
srcList.removeFirst(),
start = paddingLeft + x,
end = paddingLeft + x1,
isImage = true
isImage = true,
isIndent = true
)
)
} else {
textLine.textChars.add(
TextChar(
it, start = paddingLeft + x, end = paddingLeft + x1
it, start = paddingLeft + x, end = paddingLeft + x1, isIndent = true
)
)
}

Loading…
Cancel
Save