正文图片添加长按缩放

pull/279/head
gedoor 4 years ago
parent 10f52f3d2a
commit 7f949035aa
  1. 97
      app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt
  2. 68
      app/src/main/java/io/legado/app/ui/widget/dialog/PhotoDialog.kt
  3. 1267
      app/src/main/java/io/legado/app/ui/widget/image/PhotoView.kt
  4. 48
      app/src/main/java/io/legado/app/ui/widget/image/photo/Info.kt
  5. 56
      app/src/main/java/io/legado/app/ui/widget/image/photo/RotateGestureDetector.kt
  6. 15
      app/src/main/res/layout/dialog_photo_view.xml

@ -16,6 +16,7 @@ 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.TextPage
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.widget.dialog.PhotoDialog
import io.legado.app.utils.activity import io.legado.app.utils.activity
import io.legado.app.utils.getCompatColor import io.legado.app.utils.getCompatColor
import io.legado.app.utils.getPrefBoolean import io.legado.app.utils.getPrefBoolean
@ -213,74 +214,61 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
) { ) {
if (!selectAble) return if (!selectAble) return
if (!visibleRect.contains(x, y)) return if (!visibleRect.contains(x, y)) return
var relativeOffset = relativeOffset(0) var relativeOffset: Float
for ((lineIndex, textLine) in textPage.textLines.withIndex()) { for (relativePos in 0..2) {
relativeOffset = relativeOffset(relativePos)
if (relativePos > 0) {
//滚动翻页
if (!ReadBookConfig.isScroll) return
if (relativeOffset >= ChapterProvider.visibleHeight) return
}
val page = relativePage(relativePos)
for ((lineIndex, textLine) in page.textLines.withIndex()) {
if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) { if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) {
for ((charIndex, textChar) in textLine.textChars.withIndex()) { for ((charIndex, textChar) in textLine.textChars.withIndex()) {
if (x > textChar.start && x < textChar.end) { if (x > textChar.start && x < textChar.end) {
textChar.selected = true initSelect(
invalidate() page.chapterIndex,
selectStart[0] = 0 relativePos,
selectStart[1] = lineIndex textLine,
selectStart[2] = charIndex textChar,
selectEnd[0] = 0 lineIndex,
selectEnd[1] = lineIndex charIndex,
selectEnd[2] = charIndex relativeOffset,
upSelectedStart( select
textChar.start,
textLine.lineBottom + relativeOffset,
textLine.lineTop + relativeOffset
) )
upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
select(0, lineIndex, charIndex)
return return
} }
} }
return return
} }
} }
if (!ReadBookConfig.isScroll) return
//滚动翻页
relativeOffset = relativeOffset(1)
if (relativeOffset >= ChapterProvider.visibleHeight) return
val nextPage = relativePage(1)
for ((lineIndex, textLine) in nextPage.textLines.withIndex()) {
if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) {
for ((charIndex, textChar) in textLine.textChars.withIndex()) {
if (x > textChar.start && x < textChar.end) {
textChar.selected = true
invalidate()
selectStart[0] = 1
selectStart[1] = lineIndex
selectStart[2] = charIndex
selectEnd[0] = 1
selectEnd[1] = lineIndex
selectEnd[2] = charIndex
upSelectedStart(
textChar.start,
textLine.lineBottom + relativeOffset,
textLine.lineTop + relativeOffset
)
upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
select(1, lineIndex, charIndex)
return
}
} }
return
} }
private fun initSelect(
chapterIndex: Int,
relativePage: Int,
textLine: TextLine,
textChar: TextChar,
lineIndex: Int,
charIndex: Int,
relativeOffset: Float,
select: (relativePage: Int, lineIndex: Int, charIndex: Int) -> Unit
) {
if (textChar.isImage) {
activity?.supportFragmentManager?.let {
PhotoDialog.show(it, chapterIndex, textChar.charData)
} }
relativeOffset = relativeOffset(2) } else {
if (relativeOffset >= ChapterProvider.visibleHeight) return
for ((lineIndex, textLine) in relativePage(2).textLines.withIndex()) {
if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) {
for ((charIndex, textChar) in textLine.textChars.withIndex()) {
if (x > textChar.start && x < textChar.end) {
textChar.selected = true textChar.selected = true
invalidate() invalidate()
selectStart[0] = 2 selectStart[0] = relativePage
selectStart[1] = lineIndex selectStart[1] = lineIndex
selectStart[2] = charIndex selectStart[2] = charIndex
selectEnd[0] = 2 selectEnd[0] = relativePage
selectEnd[1] = lineIndex selectEnd[1] = lineIndex
selectEnd[2] = charIndex selectEnd[2] = charIndex
upSelectedStart( upSelectedStart(
@ -289,12 +277,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
textLine.lineTop + relativeOffset textLine.lineTop + relativeOffset
) )
upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset) upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
select(2, lineIndex, charIndex) select(relativePage, lineIndex, charIndex)
return
}
}
return
}
} }
} }

@ -0,0 +1,68 @@
package io.legado.app.ui.widget.dialog
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.FragmentManager
import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
import io.legado.app.service.help.ReadBook
import io.legado.app.ui.book.read.page.provider.ImageProvider
import kotlinx.android.synthetic.main.dialog_photo_view.*
class PhotoDialog : BaseDialogFragment() {
companion object {
fun show(
fragmentManager: FragmentManager,
chapterIndex: Int,
src: String
) {
PhotoDialog().apply {
val bundle = Bundle()
bundle.putInt("chapterIndex", chapterIndex)
bundle.putString("src", src)
arguments = bundle
}.show(fragmentManager, "photoDialog")
}
}
override fun onStart() {
super.onStart()
val dm = DisplayMetrics()
activity?.windowManager?.defaultDisplay?.getMetrics(dm)
dialog?.window?.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.dialog_photo_view, container)
}
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
arguments?.let {
val chapterIndex = it.getInt("chapterIndex")
val src = it.getString("src")
ReadBook.book?.let { book ->
src?.let {
ImageProvider.getImage(book, chapterIndex, src)?.let { bitmap ->
photo_view.setImageBitmap(bitmap)
}
}
}
}
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,48 @@
package io.legado.app.ui.widget.image.photo
import android.graphics.PointF
import android.graphics.RectF
import android.widget.ImageView
class Info(
rect: RectF,
img: RectF,
widget: RectF,
base: RectF,
screenCenter: PointF,
scale: Float,
degrees: Float,
scaleType: ImageView.ScaleType?
) {
// 内部图片在整个手机界面的位置
var mRect = RectF()
// 控件在窗口的位置
var mImgRect = RectF()
var mWidgetRect = RectF()
var mBaseRect = RectF()
var mScreenCenter = PointF()
var mScale = 0f
var mDegrees = 0f
var mScaleType: ImageView.ScaleType? = null
init {
mRect.set(rect)
mImgRect.set(img)
mWidgetRect.set(widget)
mScale = scale
mScaleType = scaleType
mDegrees = degrees
mBaseRect.set(base)
mScreenCenter.set(screenCenter)
}
}

@ -0,0 +1,56 @@
package io.legado.app.ui.widget.image.photo
import android.view.MotionEvent
import kotlin.math.abs
import kotlin.math.atan
class RotateGestureDetector(l: OnRotateListener) {
private val MAX_DEGREES_STEP = 120
private val mListener: OnRotateListener? = null
private var mPrevSlope = 0f
private var mCurrSlope = 0f
private val x1 = 0f
private val y1 = 0f
private val x2 = 0f
private val y2 = 0f
fun onTouchEvent(event: MotionEvent) {
when (event.actionMasked) {
MotionEvent.ACTION_POINTER_DOWN,
MotionEvent.ACTION_POINTER_UP -> {
if (event.pointerCount == 2) mPrevSlope = caculateSlope(event)
}
MotionEvent.ACTION_MOVE -> if (event.pointerCount > 1) {
mCurrSlope = caculateSlope(event)
val currDegrees = Math.toDegrees(atan(mCurrSlope.toDouble()));
val prevDegrees = Math.toDegrees(atan(mPrevSlope.toDouble()));
val deltaSlope = currDegrees - prevDegrees;
if (abs(deltaSlope) <= MAX_DEGREES_STEP) {
mListener?.onRotate(deltaSlope.toFloat(), (x2 + x1) / 2, (y2 + y1) / 2);
}
mPrevSlope = mCurrSlope;
}
}
}
private fun caculateSlope(event: MotionEvent): Float {
val x1 = event.getX(0);
val y1 = event.getY(0);
val x2 = event.getX(1);
val y2 = event.getY(1);
return (y2 - y1) / (x2 - x1);
}
}
interface OnRotateListener {
fun onRotate(degrees: Float, focusX: Float, focusY: Float)
}

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/background">
<io.legado.app.ui.widget.image.PhotoView
android:id="@+id/photo_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerInside" />
</androidx.constraintlayout.widget.ConstraintLayout>
Loading…
Cancel
Save