Webtoon reader improvements
This commit is contained in:
@@ -80,26 +80,28 @@ class WebtoonImageView @JvmOverloads constructor(
|
|||||||
val parentHeight = MeasureSpec.getSize(heightMeasureSpec)
|
val parentHeight = MeasureSpec.getSize(heightMeasureSpec)
|
||||||
val resizeWidth = widthSpecMode != MeasureSpec.EXACTLY
|
val resizeWidth = widthSpecMode != MeasureSpec.EXACTLY
|
||||||
val resizeHeight = heightSpecMode != MeasureSpec.EXACTLY
|
val resizeHeight = heightSpecMode != MeasureSpec.EXACTLY
|
||||||
var width = parentWidth
|
var desiredWidth = parentWidth
|
||||||
var height = parentHeight
|
var desiredHeight = parentHeight
|
||||||
if (sWidth > 0 && sHeight > 0) {
|
if (sWidth > 0 && sHeight > 0) {
|
||||||
if (resizeWidth && resizeHeight) {
|
if (resizeWidth && resizeHeight) {
|
||||||
width = sWidth
|
desiredWidth = sWidth
|
||||||
height = sHeight
|
desiredHeight = sHeight
|
||||||
} else if (resizeHeight) {
|
} else if (resizeHeight) {
|
||||||
height = (sHeight.toDouble() / sWidth.toDouble() * width).toInt()
|
desiredHeight = (sHeight.toDouble() / sWidth.toDouble() * desiredWidth).roundToInt()
|
||||||
} else if (resizeWidth) {
|
} else if (resizeWidth) {
|
||||||
width = (sWidth.toDouble() / sHeight.toDouble() * height).toInt()
|
desiredWidth = (sWidth.toDouble() / sHeight.toDouble() * desiredHeight).roundToInt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
width = width.coerceAtLeast(suggestedMinimumWidth)
|
desiredWidth = desiredWidth.coerceAtLeast(suggestedMinimumWidth)
|
||||||
height = height.coerceAtLeast(suggestedMinimumHeight).coerceAtMost(parentHeight())
|
desiredHeight = desiredHeight.coerceAtLeast(suggestedMinimumHeight).coerceAtMost(parentHeight())
|
||||||
setMeasuredDimension(width, height)
|
setMeasuredDimension(desiredWidth, desiredHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDownsamplingChanged() {
|
override fun onDownsamplingChanged() {
|
||||||
super.onDownsamplingChanged()
|
super.onDownsamplingChanged()
|
||||||
adjustScale()
|
post {
|
||||||
|
adjustScale()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onReady() {
|
override fun onReady() {
|
||||||
@@ -107,15 +109,6 @@ class WebtoonImageView @JvmOverloads constructor(
|
|||||||
adjustScale()
|
adjustScale()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
|
||||||
super.onSizeChanged(w, h, oldw, oldh)
|
|
||||||
if (oldh != h && oldw != 0 && oldh != 0 && isReady) {
|
|
||||||
ancestors.firstNotNullOfOrNull { it as? WebtoonRecyclerView }?.updateChildrenScroll()
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun scrollToInternal(pos: Int) {
|
private fun scrollToInternal(pos: Int) {
|
||||||
minScale = width / sWidth.toFloat()
|
minScale = width / sWidth.toFloat()
|
||||||
maxScale = minScale
|
maxScale = minScale
|
||||||
@@ -128,6 +121,7 @@ class WebtoonImageView @JvmOverloads constructor(
|
|||||||
minScale = width / sWidth.toFloat()
|
minScale = width / sWidth.toFloat()
|
||||||
maxScale = minScale
|
maxScale = minScale
|
||||||
minimumScaleType = SCALE_TYPE_CUSTOM
|
minimumScaleType = SCALE_TYPE_CUSTOM
|
||||||
|
requestLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parentHeight(): Int {
|
private fun parentHeight(): Int {
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import android.util.AttributeSet
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.core.view.ViewCompat.TYPE_TOUCH
|
import androidx.core.view.ViewCompat.TYPE_TOUCH
|
||||||
import androidx.core.view.forEach
|
import androidx.core.view.forEach
|
||||||
import androidx.core.view.iterator
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import java.util.Collections
|
||||||
import java.util.LinkedList
|
import java.util.LinkedList
|
||||||
import java.util.WeakHashMap
|
import java.util.WeakHashMap
|
||||||
|
|
||||||
@@ -17,12 +17,11 @@ class WebtoonRecyclerView @JvmOverloads constructor(
|
|||||||
|
|
||||||
private var onPageScrollListeners = LinkedList<OnWebtoonScrollListener>()
|
private var onPageScrollListeners = LinkedList<OnWebtoonScrollListener>()
|
||||||
private val scrollDispatcher = WebtoonScrollDispatcher()
|
private val scrollDispatcher = WebtoonScrollDispatcher()
|
||||||
private val detachedViews = WeakHashMap<View, Unit>()
|
private val detachedViews = Collections.newSetFromMap(WeakHashMap<View, Boolean>())
|
||||||
private var isFixingScroll: Boolean = false
|
|
||||||
|
|
||||||
override fun onChildDetachedFromWindow(child: View) {
|
override fun onChildDetachedFromWindow(child: View) {
|
||||||
super.onChildDetachedFromWindow(child)
|
super.onChildDetachedFromWindow(child)
|
||||||
detachedViews[child] = Unit
|
detachedViews.add(child)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onChildAttachedToWindow(child: View) {
|
override fun onChildAttachedToWindow(child: View) {
|
||||||
@@ -32,7 +31,7 @@ class WebtoonRecyclerView @JvmOverloads constructor(
|
|||||||
|
|
||||||
override fun startNestedScroll(axes: Int) = startNestedScroll(axes, TYPE_TOUCH)
|
override fun startNestedScroll(axes: Int) = startNestedScroll(axes, TYPE_TOUCH)
|
||||||
|
|
||||||
override fun startNestedScroll(axes: Int, type: Int): Boolean = true
|
override fun startNestedScroll(axes: Int, type: Int): Boolean = childCount != 0
|
||||||
|
|
||||||
override fun dispatchNestedPreScroll(
|
override fun dispatchNestedPreScroll(
|
||||||
dx: Int,
|
dx: Int,
|
||||||
@@ -57,13 +56,6 @@ class WebtoonRecyclerView @JvmOverloads constructor(
|
|||||||
return consumedY != 0 || dy == 0
|
return consumedY != 0 || dy == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onScrollStateChanged(state: Int) {
|
|
||||||
super.onScrollStateChanged(state)
|
|
||||||
if (state == SCROLL_STATE_IDLE) {
|
|
||||||
updateChildrenScroll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun consumeVerticalScroll(dy: Int): Int {
|
private fun consumeVerticalScroll(dy: Int): Int {
|
||||||
if (childCount == 0) {
|
if (childCount == 0) {
|
||||||
return 0
|
return 0
|
||||||
@@ -124,43 +116,11 @@ class WebtoonRecyclerView @JvmOverloads constructor(
|
|||||||
forEach { child ->
|
forEach { child ->
|
||||||
(child as WebtoonFrameLayout).target.requestLayout()
|
(child as WebtoonFrameLayout).target.requestLayout()
|
||||||
}
|
}
|
||||||
detachedViews.keys.forEach { child ->
|
detachedViews.forEach { child ->
|
||||||
(child as WebtoonFrameLayout).target.requestLayout()
|
(child as WebtoonFrameLayout).target.requestLayout()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateChildrenScroll() {
|
|
||||||
if (isFixingScroll) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
isFixingScroll = true
|
|
||||||
for (child in this) {
|
|
||||||
val ssiv = (child as WebtoonFrameLayout).target
|
|
||||||
if (adjustScroll(child, ssiv)) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
isFixingScroll = false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun adjustScroll(child: View, ssiv: WebtoonImageView): Boolean = when {
|
|
||||||
child.bottom < height && ssiv.getScroll() < ssiv.getScrollRange() -> {
|
|
||||||
val distance = minOf(height - child.bottom, ssiv.getScrollRange() - ssiv.getScroll())
|
|
||||||
scrollBy(0, -distance)
|
|
||||||
ssiv.scrollBy(distance)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
child.top > 0 && ssiv.getScroll() > 0 -> {
|
|
||||||
val distance = minOf(child.top, ssiv.getScroll())
|
|
||||||
scrollBy(0, distance)
|
|
||||||
ssiv.scrollBy(-distance)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
|
|
||||||
private class WebtoonScrollDispatcher {
|
private class WebtoonScrollDispatcher {
|
||||||
|
|
||||||
private var firstPos = NO_POSITION
|
private var firstPos = NO_POSITION
|
||||||
@@ -178,13 +138,20 @@ class WebtoonRecyclerView @JvmOverloads constructor(
|
|||||||
if (newFirstPos != firstPos || newLastPos != lastPos) {
|
if (newFirstPos != firstPos || newLastPos != lastPos) {
|
||||||
firstPos = newFirstPos
|
firstPos = newFirstPos
|
||||||
lastPos = newLastPos
|
lastPos = newLastPos
|
||||||
rv.onPageScrollListeners.forEach { it.onScrollChanged(rv, dy, newFirstPos, newLastPos) }
|
if (newFirstPos != NO_POSITION && newLastPos != NO_POSITION) {
|
||||||
|
rv.onPageScrollListeners.forEach { it.onScrollChanged(rv, dy, newFirstPos, newLastPos) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OnWebtoonScrollListener {
|
interface OnWebtoonScrollListener {
|
||||||
|
|
||||||
fun onScrollChanged(recyclerView: WebtoonRecyclerView, dy: Int, firstVisiblePosition: Int, lastVisiblePosition: Int)
|
fun onScrollChanged(
|
||||||
|
recyclerView: WebtoonRecyclerView,
|
||||||
|
dy: Int,
|
||||||
|
firstVisiblePosition: Int,
|
||||||
|
lastVisiblePosition: Int,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user