VerticalScrollTextView:超过最大高度时支持滚动并且解决滑动冲突的TextView
正如标题所说,这个自定义TextView在它的行数超过最大行数或是高度超过最大高度限制时,会将TextView设置为可纵向滑动的状态,如果没有超过限制,它还是跟普通的TextView保存一致。当滚动到边界时,自动将滑动事件归还给父布局。
效果图
XML布局
当TextView的外层是一些滚动布局时,直接让TextView简单地支持滑动是解决不了问题的,因为这时就会有滑动冲突的问题。 这时VerticalScrollTextView就派上用场了,我们在ScrollView的内部添加一个VerticalScrollTextView,布局代码如下:
源码如下:
VerticalScrollTextView代码解释 : 实现了默认的3个构造方法,在构造方法内部支持了TextView的滚动设置; 内部有一个 isNeedScroll 方法,用于判断是否需要滚动。如果最大行数大于0且小于实际行数,或者最大高度大于0且小于等于实际高度,则需要滚动; 有3个全局参数:mNeedScrollFlag、mLastScrollFlag、mLastY。 同时重写了 onTouchEvent 方法,对用户的屏幕操作进行处理:
当动作为 MotionEvent.ACTION_DOWN 时,会调用isNeedScroll方法,如果需要滚动,则设置 mNeedScrollFlag 为 true,并设置滚动方法和垂直滚动条,同时记录Y轴坐标。当动作为 MotionEvent.ACTION_MOVE 时,如果需要滚动并且触摸点的纵坐标发生了变化,则计算滚动方向并检查是否可以垂直滚动。如果可以垂直滚动的状态发生了变化,则更新状态并请求父视图不拦截触摸事件;当TextView滚动到边界的时候,就会通知父视图,触摸事件重新由它处理,这时也会将mNeedScrollFlag设置为false,代表此次的触摸事件将不再滑动TextView(变量orientation是判断当前的滑动方向,canScrollVertically方法是判断当前方向是否可继续滑动)。当动作为 MotionEvent.ACTION_UP 时,重置标志位。
class VerticalScrollTextView : AppCompatTextView {
private var mNeedScrollFlag = false
private var mLastScrollFlag = false
private var mLastY = 0F
constructor(context : Context) : this(context, null)
constructor(context : Context, attrs : AttributeSet?) : this(context, attrs, 0)
constructor(context : Context, attrs : AttributeSet?, defStyleAttr : Int) : super(context, attrs, defStyleAttr) {
isVerticalScrollBarEnabled = true
movementMethod = ScrollingMovementMethod.getInstance()
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
event?.run {
when (action) {
MotionEvent.ACTION_DOWN -> {
if (isNeedScroll()) {
mNeedScrollFlag = true
mLastY = rawY
}
}
MotionEvent.ACTION_MOVE -> {
if (mNeedScrollFlag and (rawY != mLastY)) {
val orientation = if (rawY - mLastY > 0) -1 else 1
val scrollFlag = canScrollVertically(orientation)
if (scrollFlag != mLastScrollFlag) {
parent.requestDisallowInterceptTouchEvent(scrollFlag)
mLastScrollFlag = scrollFlag
if (!scrollFlag) mNeedScrollFlag = false
}
mLastY = rawY
}
}
MotionEvent.ACTION_UP -> {
mNeedScrollFlag = false
mLastScrollFlag = false
}
}
}
return super.onTouchEvent(event)
}
private fun isNeedScroll() : Boolean {
return ((maxLines > 0) and (maxLines 0) and (maxHeight |