这就是一个没啥用的ViewGroup,纯粹是用来学习实践的例子demo
效果图:

步骤一:布局
1、创建类名为TowPagerView
的类,使它继承ViewPager
2、重写onLayout函数,使它的子view横向排列,铺满
class TowPagerView(context: Context, attrs: AttributeSet?) : ViewPager(context, attrs) {
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
var childLeft = 0
var childTop = 0
var childRight = width
var childBottom = height
for (child in children) {
child.layout(childLeft, childTop, childRight, childBottom)
childLeft += width
childRight += width
}
}
}
步骤二:滑动
1、重写onInterceptTouchEvent
返回true
.拦截事件
2、重写onTouchEvent
在MotionEvent.ACTION_DOWN
l里保存开始位置的x
,y
和scrollX
3、在MotionEvent.ACTION_MOVE
里根据手指的位置计算出滑动的位置
class TowPagerView(context: Context, attrs: AttributeSet?) : ViewPager(context, attrs) {
private var downX = 0f
private var downY = 0f
private var downScrollX = 0f
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
var childLeft = 0
var childTop = 0
var childRight = width
var childBottom = height
for (child in children) {
child.layout(childLeft, childTop, childRight, childBottom)
childLeft += width
childRight += width
}
}
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
return true
}
override fun onTouchEvent(ev: MotionEvent): Boolean {
when (ev.action) {
MotionEvent.ACTION_DOWN -> {
downX = ev.x
downY = ev.y
downScrollX = scrollX.toFloat()
}
MotionEvent.ACTION_MOVE -> {
val dx = (downX - ev.x+downScrollX).toInt()
.coerceAtLeast(0)
.coerceAtMost(width)
scrollTo(dx, 0)
}
}
return true
}
}
步骤三、事件拦截判断
当出现左右滑动时,拦截子view的事件,并且阻止父view拦截事件
private var scrolling = false //是否是开始滑动了
private val viewConfiguration: ViewConfiguration = ViewConfiguration.get(context)
private var pagingSlop = viewConfiguration.scaledPagingTouchSlop//最小的滑动距离
...
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
var result = false
when (ev.action) {
MotionEvent.ACTION_DOWN -> {
scrolling = false
downX = ev.x
downY = ev.y
downScrollX = scrollX.toFloat()
}
MotionEvent.ACTION_MOVE -> {
if (!scrolling) {
val dx = downX - ev.x
if (abs(dx) > pagingSlop) {
scrolling = true
parent.requestDisallowInterceptTouchEvent(true)//父控件不拦截
result = true//自己拦截了
}
}
}
}
return result
}
步骤四、吸附
在松开手指之后根据停留位置判断最终的位置
在MotionEvent.ACTION_UP
里判断当前的滑动距离是否超过了屏幕的一半
val targetPage = if (scrollX > width / 2) 1 else 0
val scrollDistance = if (targetPage == 1) width - scrollX else -scrollX
在最终的移动中使用OverScroller
来实现弹性滑动
private val overScroller: OverScroller = OverScroller(context)
...
override fun onTouchEvent(ev: MotionEvent): Boolean {
when (ev.action) {
...
MotionEvent.ACTION_UP -> {
val targetPage = if (scrollX > width / 2) 1 else 0
val scrollDistance = if (targetPage == 1) width - scrollX else -scrollX
overScroller.startScroll(scrollX, 0, scrollDistance, 0)
postInvalidateOnAnimation()
}
}
return true
}
override fun computeScroll() {
if (overScroller.computeScrollOffset()) {
scrollTo(overScroller.currX, overScroller.currY)
postInvalidateOnAnimation()
}
}
步骤五、惯性滑动
当手指滑动速度大于阀值是,直接判定用户意图
使用VelocityTracker.obtain()
获取VelocityTracker
对象来获得滑动速度的相关数据
注意:需要在onInterceptTouchEvent
和onTouchEvent
的MotionEvent.ACTION_DOWN
事件中清空数据。还有添加所有事件的数据
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
if (ev.actionMasked == MotionEvent.ACTION_DOWN) {
velocityTracker.clear()
}
velocityTracker.addMovement(ev)
...
}
override fun onTouchEvent(ev: MotionEvent): Boolean {
if (ev.actionMasked == MotionEvent.ACTION_DOWN) {
velocityTracker.clear()
}
velocityTracker.addMovement(ev)
...
}
就是一摸一样的代码
接着在onTouchEvent
的MotionEvent.ACTION_UP
事件中获取速度信息并逻辑计算
MotionEvent.ACTION_UP -> {
velocityTracker.computeCurrentVelocity(1000, maxVelocity.toFloat())
val vx = velocityTracker.xVelocity
val targetPage = if (abs(vx) < minVelocity) {
if (scrollX > width / 2) 1 else 0
} else {
if (vx < 0) 1 else 0
}
...
}
基本的都介绍完了,上完整代码
class TowPagerView(context: Context, attrs: AttributeSet?) : ViewPager(context, attrs) {
private var downX = 0f
private var downY = 0f
private var downScrollX = 0f
private var scrolling = false //是否是开始滑动了
private val overScroller: OverScroller = OverScroller(context)
private val viewConfiguration: ViewConfiguration = ViewConfiguration.get(context)
private var pagingSlop = viewConfiguration.scaledPagingTouchSlop//最小的滑动距离
private val velocityTracker = VelocityTracker.obtain()
private var minVelocity = viewConfiguration.scaledMinimumFlingVelocity
private var maxVelocity = viewConfiguration.scaledMaximumFlingVelocity
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
var childLeft = 0
var childTop = 0
var childRight = width
var childBottom = height
for (child in children) {
child.layout(childLeft, childTop, childRight, childBottom)
childLeft += width
childRight += width
}
}
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
if (ev.actionMasked == MotionEvent.ACTION_DOWN) {
velocityTracker.clear()
}
velocityTracker.addMovement(ev)
var result = false
when (ev.action) {
MotionEvent.ACTION_DOWN -> {
scrolling = false
downX = ev.x
downY = ev.y
downScrollX = scrollX.toFloat()
}
MotionEvent.ACTION_MOVE -> {
if (!scrolling) {
val dx = downX - ev.x
if (abs(dx) > pagingSlop) {
scrolling = true
parent.requestDisallowInterceptTouchEvent(true)//父控件不拦截
result = true//自己拦截了
}
}
}
}
return result
}
override fun onTouchEvent(ev: MotionEvent): Boolean {
if (ev.actionMasked == MotionEvent.ACTION_DOWN) {
velocityTracker.clear()
}
velocityTracker.addMovement(ev)
when (ev.action) {
MotionEvent.ACTION_DOWN -> {
downX = ev.x
downY = ev.y
downScrollX = scrollX.toFloat()
}
MotionEvent.ACTION_MOVE -> {
val dx = (downX - ev.x + downScrollX).toInt()
.coerceAtLeast(0)
.coerceAtMost(width)
scrollTo(dx, 0)
}
MotionEvent.ACTION_UP -> {
velocityTracker.computeCurrentVelocity(1000, maxVelocity.toFloat())
val vx = velocityTracker.xVelocity
val targetPage = if (abs(vx) < minVelocity) {
if (scrollX > width / 2) 1 else 0
} else {
if (vx < 0) 1 else 0
}
val scrollDistance = if (targetPage == 1) width - scrollX else -scrollX
overScroller.startScroll(scrollX, 0, scrollDistance, 0)
postInvalidateOnAnimation()
}
}
return true
}
override fun computeScroll() {
if (overScroller.computeScrollOffset()) {
scrollTo(overScroller.currX, overScroller.currY)
postInvalidateOnAnimation()
}
}
}
网友评论