|
|
|
@ -1,5 +1,6 @@ |
|
|
|
|
package io.legado.app.ui.widget.code |
|
|
|
|
|
|
|
|
|
import android.annotation.SuppressLint |
|
|
|
|
import android.content.Context |
|
|
|
|
import android.graphics.Canvas |
|
|
|
|
import android.graphics.Paint |
|
|
|
@ -12,6 +13,7 @@ import android.text.style.BackgroundColorSpan |
|
|
|
|
import android.text.style.ForegroundColorSpan |
|
|
|
|
import android.text.style.ReplacementSpan |
|
|
|
|
import android.util.AttributeSet |
|
|
|
|
import android.view.MotionEvent |
|
|
|
|
import androidx.annotation.ColorInt |
|
|
|
|
import androidx.appcompat.widget.AppCompatMultiAutoCompleteTextView |
|
|
|
|
import java.util.* |
|
|
|
@ -37,6 +39,12 @@ class CodeView @JvmOverloads constructor(context: Context, attrs: AttributeSet? |
|
|
|
|
private val mSyntaxPatternMap: MutableMap<Pattern, Int> = HashMap() |
|
|
|
|
private var mIndentCharacterList = mutableListOf('{', '+', '-', '*', '/', '=') |
|
|
|
|
|
|
|
|
|
//滑动距离的最大边界 |
|
|
|
|
private var mOffsetHeight = 0 |
|
|
|
|
|
|
|
|
|
//是否到顶或者到底的标志 |
|
|
|
|
private var mBottomFlag = false |
|
|
|
|
|
|
|
|
|
private val mUpdateRunnable = Runnable { |
|
|
|
|
val source = text |
|
|
|
|
highlightWithoutChange(source) |
|
|
|
@ -102,6 +110,83 @@ class CodeView @JvmOverloads constructor(context: Context, attrs: AttributeSet? |
|
|
|
|
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( |
|
|
|
|
source: CharSequence, |
|
|
|
|
dest: Spanned, |
|
|
|
@ -365,21 +450,6 @@ class CodeView @JvmOverloads constructor(context: Context, attrs: AttributeSet? |
|
|
|
|
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() { |
|
|
|
|
override fun getSize( |
|
|
|
|
paint: Paint, |
|
|
|
|