最近偶然间看到一篇博客Android Scroller 滑动机制介绍了Android中ScrollView的滚动本质,并从源码级别进行了介绍,只能说大佬牛逼了,然后写一个demo来加强记忆咯
1、Scroll的方向傻傻乎乎分不清楚
如图所示,如果需要向右边滚动,参数得为负数scrollBy(-dx.toInt(), -dy.toInt())
2、系统给了很多值基础值,需要好好使用
ViewConfiguration viewConfiguration = ViewConfiguration.get(getContext());
//表示滑动的时候,手的移动要大于这个距离才开始移动控件
scaledTouchSlop = viewConfiguration.getScaledTouchSlop();
//获得允许执行一个fling手势动作的最大速度值
scaledMaximumFlingVelocity = viewConfiguration.getScaledMaximumFlingVelocity();
//获得允许执行一个fling手势动作的最小速度值
scaledMinimumFlingVelocity = viewConfiguration.getScaledMinimumFlingVelocity();
3、OverScroller的使用
OverScroller与Scroller大部分api都一模一样,唯一多的就是可以越界滚动一部分长度,这个可以用来做回弹效果
4、简单化的拖动,不如试用下ViewDragHelper
5、滚动的本质
本质上是改变了View.mScrollX和View.mScrollY这两个变量,只会改变控件绘制的位置,控件本身没变
6、其他一些变量
其实View还有mLeft,mTop等等变量,修改这些变量则会移动View在父布局中的位置
View.setLeft/Top/Right/Bottom
View.layout(l,t,r,b);
View.setTranslationX/Y
View.soffsetTopAndBottom
Demo地址
class ScrollTextView : androidx.appcompat.widget.AppCompatButton {
private val scroll = OverScroller(context)
private var velocityTracker: VelocityTracker? = null
private var mLastX: Float = 0f
private var mLastY: Float = 0f
private var maxFling: Int = 0
private var minFling: Int = 0;
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) {
val viewConfiguration = ViewConfiguration.get(getContext())
maxFling = viewConfiguration.scaledMaximumFlingVelocity
minFling = viewConfiguration.scaledMinimumFlingVelocity
Log.i(TAG, "maxFling = $maxFling minFling = $minFling")
}
override fun onTouchEvent(event: MotionEvent): Boolean {
if (velocityTracker == null) {
velocityTracker = VelocityTracker.obtain()
}
velocityTracker?.addMovement(event)
val x = event.rawX
val y = event.rawY
when (event.action) {
MotionEvent.ACTION_DOWN -> {
if (!scroll.isFinished) {
scroll.abortAnimation()
}
}
MotionEvent.ACTION_MOVE -> {
val dx = x - mLastX
val dy = y - mLastY
scrollBy(-dx.toInt(), -dy.toInt())
}
MotionEvent.ACTION_UP,
MotionEvent.ACTION_CANCEL -> {
velocityTracker?.let {
it.computeCurrentVelocity(1000, maxFling.toFloat())
val padding = 30
scroll.fling(scrollX, scrollY, -it.xVelocity.toInt(), -it.yVelocity.toInt(), -measuredWidth / 2 + padding, measuredWidth / 2 - padding, -measuredHeight / 2 + padding, measuredHeight / 2 - padding, 20, 20)
postInvalidateOnAnimation()
}
velocityTracker?.recycle()
velocityTracker = null
}
}
mLastX = x
mLastY = y
return true
}
fun startScroll() {
if (!scroll.isFinished) {
scroll.abortAnimation()
}
scroll.startScroll(scrollX, scrollY, -100, -100, 1000 * 3)
postInvalidateOnAnimation()
}
override fun computeScroll() {
super.computeScroll()
if (scroll.computeScrollOffset()) {
scrollTo(scroll.currX, scroll.currY)
postInvalidateOnAnimation()
}
}
}
ps:一些好用的属性
getCurrVelocity()可以获取剩余的速度
getFinalY/getFinalX 获取滚动的终点
有demo地址吗
@Eric 博客里面的代码就直接是demo,复制到项目里面就可以运行的