|
@@ -1,5 +1,6 @@
|
|
|
package io.legado.app.ui.widget.code
|
|
package io.legado.app.ui.widget.code
|
|
|
|
|
|
|
|
|
|
+import android.annotation.SuppressLint
|
|
|
import android.content.Context
|
|
import android.content.Context
|
|
|
import android.graphics.Canvas
|
|
import android.graphics.Canvas
|
|
|
import android.graphics.Paint
|
|
import android.graphics.Paint
|
|
@@ -12,6 +13,7 @@ import android.text.style.BackgroundColorSpan
|
|
|
import android.text.style.ForegroundColorSpan
|
|
import android.text.style.ForegroundColorSpan
|
|
|
import android.text.style.ReplacementSpan
|
|
import android.text.style.ReplacementSpan
|
|
|
import android.util.AttributeSet
|
|
import android.util.AttributeSet
|
|
|
|
|
+import android.view.MotionEvent
|
|
|
import androidx.annotation.ColorInt
|
|
import androidx.annotation.ColorInt
|
|
|
import androidx.appcompat.widget.AppCompatMultiAutoCompleteTextView
|
|
import androidx.appcompat.widget.AppCompatMultiAutoCompleteTextView
|
|
|
import java.util.*
|
|
import java.util.*
|
|
@@ -37,6 +39,12 @@ class CodeView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
|
|
private val mSyntaxPatternMap: MutableMap<Pattern, Int> = HashMap()
|
|
private val mSyntaxPatternMap: MutableMap<Pattern, Int> = HashMap()
|
|
|
private var mIndentCharacterList = mutableListOf('{', '+', '-', '*', '/', '=')
|
|
private var mIndentCharacterList = mutableListOf('{', '+', '-', '*', '/', '=')
|
|
|
|
|
|
|
|
|
|
+ //滑动距离的最大边界
|
|
|
|
|
+ private var mOffsetHeight = 0
|
|
|
|
|
+
|
|
|
|
|
+ //是否到顶或者到底的标志
|
|
|
|
|
+ private var mBottomFlag = false
|
|
|
|
|
+
|
|
|
private val mUpdateRunnable = Runnable {
|
|
private val mUpdateRunnable = Runnable {
|
|
|
val source = text
|
|
val source = text
|
|
|
highlightWithoutChange(source)
|
|
highlightWithoutChange(source)
|
|
@@ -102,6 +110,83 @@ class CodeView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
|
|
addTextChangedListener(mEditorTextWatcher)
|
|
addTextChangedListener(mEditorTextWatcher)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
|
|
|
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
|
|
|
|
+ initOffsetHeight()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun onTextChanged(
|
|
|
|
|
+ text: CharSequence,
|
|
|
|
|
+ start: Int,
|
|
|
|
|
+ lengthBefore: Int,
|
|
|
|
|
+ lengthAfter: Int
|
|
|
|
|
+ ) {
|
|
|
|
|
+ super.onTextChanged(text, start, lengthBefore, lengthAfter)
|
|
|
|
|
+ initOffsetHeight()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun dispatchTouchEvent(event: MotionEvent): Boolean {
|
|
|
|
|
+ if (event.action == MotionEvent.ACTION_DOWN) {
|
|
|
|
|
+ //如果是新的按下事件,则对mBottomFlag重新初始化
|
|
|
|
|
+ mBottomFlag = mOffsetHeight <= 0
|
|
|
|
|
+ }
|
|
|
|
|
+ return super.dispatchTouchEvent(event)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @SuppressLint("ClickableViewAccessibility")
|
|
|
|
|
+ override fun onTouchEvent(event: MotionEvent): Boolean {
|
|
|
|
|
+ val result = super.onTouchEvent(event)
|
|
|
|
|
+ //如果是需要拦截,则再拦截,这个方法会在onScrollChanged方法之后再调用一次
|
|
|
|
|
+ if (!mBottomFlag) parent.requestDisallowInterceptTouchEvent(true)
|
|
|
|
|
+ return result
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun onScrollChanged(horiz: Int, vert: Int, oldHoriz: Int, oldVert: Int) {
|
|
|
|
|
+ super.onScrollChanged(horiz, vert, oldHoriz, oldVert)
|
|
|
|
|
+ if (vert == mOffsetHeight || vert == 0) {
|
|
|
|
|
+ //这里触发父布局或祖父布局的滑动事件
|
|
|
|
|
+ parent.requestDisallowInterceptTouchEvent(false)
|
|
|
|
|
+ mBottomFlag = true
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun showDropDown() {
|
|
|
|
|
+ val screenPoint = IntArray(2)
|
|
|
|
|
+ getLocationOnScreen(screenPoint)
|
|
|
|
|
+ val displayFrame = Rect()
|
|
|
|
|
+ getWindowVisibleDisplayFrame(displayFrame)
|
|
|
|
|
+ val position = selectionStart
|
|
|
|
|
+ val layout = layout
|
|
|
|
|
+ val line = layout.getLineForOffset(position)
|
|
|
|
|
+ val verticalDistanceInDp = (750 + 140 * line) / displayDensity
|
|
|
|
|
+ dropDownVerticalOffset = verticalDistanceInDp.toInt()
|
|
|
|
|
+ val horizontalDistanceInDp = layout.getPrimaryHorizontal(position) / displayDensity
|
|
|
|
|
+ dropDownHorizontalOffset = horizontalDistanceInDp.toInt()
|
|
|
|
|
+ super.showDropDown()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private fun initOffsetHeight() {
|
|
|
|
|
+ val mLayoutHeight: Int
|
|
|
|
|
+
|
|
|
|
|
+ //获得内容面板
|
|
|
|
|
+ val mLayout = layout ?: return
|
|
|
|
|
+ //获得内容面板的高度
|
|
|
|
|
+ mLayoutHeight = mLayout.height
|
|
|
|
|
+ //获取上内边距
|
|
|
|
|
+ val paddingTop: Int = totalPaddingTop
|
|
|
|
|
+ //获取下内边距
|
|
|
|
|
+ val paddingBottom: Int = totalPaddingBottom
|
|
|
|
|
+
|
|
|
|
|
+ //获得控件的实际高度
|
|
|
|
|
+ val mHeight: Int = measuredHeight
|
|
|
|
|
+
|
|
|
|
|
+ //计算滑动距离的边界
|
|
|
|
|
+ mOffsetHeight = mLayoutHeight + paddingTop + paddingBottom - mHeight
|
|
|
|
|
+ if (mOffsetHeight <= 0) {
|
|
|
|
|
+ scrollTo(0, 0)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private fun autoIndent(
|
|
private fun autoIndent(
|
|
|
source: CharSequence,
|
|
source: CharSequence,
|
|
|
dest: Spanned,
|
|
dest: Spanned,
|
|
@@ -365,21 +450,6 @@ class CodeView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
|
|
highlightWhileTextChanging = updateWhileTextChanging
|
|
highlightWhileTextChanging = updateWhileTextChanging
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- override fun showDropDown() {
|
|
|
|
|
- val screenPoint = IntArray(2)
|
|
|
|
|
- getLocationOnScreen(screenPoint)
|
|
|
|
|
- val displayFrame = Rect()
|
|
|
|
|
- getWindowVisibleDisplayFrame(displayFrame)
|
|
|
|
|
- val position = selectionStart
|
|
|
|
|
- val layout = layout
|
|
|
|
|
- val line = layout.getLineForOffset(position)
|
|
|
|
|
- val verticalDistanceInDp = (750 + 140 * line) / displayDensity
|
|
|
|
|
- dropDownVerticalOffset = verticalDistanceInDp.toInt()
|
|
|
|
|
- val horizontalDistanceInDp = layout.getPrimaryHorizontal(position) / displayDensity
|
|
|
|
|
- dropDownHorizontalOffset = horizontalDistanceInDp.toInt()
|
|
|
|
|
- super.showDropDown()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
private inner class TabWidthSpan : ReplacementSpan() {
|
|
private inner class TabWidthSpan : ReplacementSpan() {
|
|
|
override fun getSize(
|
|
override fun getSize(
|
|
|
paint: Paint,
|
|
paint: Paint,
|