Android动画与概述主要涵盖了以下内容:
- 动画与过渡(一)、视图动画概述与使用
- 动画与过渡(二)、视图动画进阶:对Animation进行定义扩展
- 动画与过渡(三)、插值器和估值器概述与使用
- 动画与过渡(四)、使用Layout、offset、layoutParam实现位移动画
- 动画与过渡(五)、使用scrollTo、scrollBy、Scroller实现滚动动画
- 动画与过渡(六)、使用ViewDragHelper实现平滑拖动动画
- 动画与过渡(七)、为ViewGroup添加入场动画,LayoutAnimation使用概述
- 动画与过渡(八)、为Viewgroup提供删除、新增平滑动画效果,LayoutTransition使用概述
- 动画与过渡(九)、Svg动画使用概述,Vector Drawable使用,三方SVGA框架使用
- 动画与过渡(十)、Property Animation动画使用概述
- 动画与过渡(十一)、使用Fling动画移动视图,FlingAnimation动画使用概述
- 动画与过渡(十二)、使用物理弹簧动画为视图添加动画,SpringAnimation动画使用概述
- 动画与过渡(十三)、视图、Activity过渡转场动画使用概述
- 动画与过渡(十四)、Lottie动画使用概述
- 动画与过渡实战(十五)、仿今日头条栏目拖动排序效果
- 动画与过渡实战(十六)、仿IOS侧滑删除效果
- 动画与过渡实战(十七)、仿探探卡片翻牌效果
在之前的文章中,分别介绍了使用layout(left, top, right, bottom)、layoutParams、offsetLeftAndRight/offsetTopAndBottom、scrollBy/scrollTo、Scroller方式实现移动效果,本篇文章,将讲解一个更高阶的实现移动效果的方式—ViewDragHelper。
1、ViewDragHelper使用方法介绍ViewDragHelper使用十分简单,基本上就是固定的使用套路。因为ViewDragHelper是一个拖动、移动的辅助类,一般情况下,我们会使用在自定义view or viewgroup之中。
- 使用ViewDragHelper.create(this, object : ViewDragHelper.Callback() {}方法创建一个ViewDragHelper对象
val itemDragHelper = ViewDragHelper.create(this, object : ViewDragHelper.Callback() {
...//do somethings
}
- 在View or ViewGroup中的事件拦截使用viewDragHelper的方法进行*事件接管*。
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
return mItemViewDragHelper?.shouldInterceptTouchEvent(ev) == true
}
override fun onTouchEvent(event: MotionEvent): Boolean {
if (event.action == MotionEvent.ACTION_DOWN) {
this.performClick()
}
mItemViewDragHelper?.processTouchEvent(event)
return true
}
- 重写computeScroll方法,在其中处理刷新
override fun computeScroll() {
if (mItemViewDragHelper?.continueSettling(true) == true) {
ViewCompat.postInvalidateOnAnimation(this)
}
}
- 实现ViewDragHelper.Callback的方法,进行逻辑处理操作。
下面介绍下ViewDragHelper.Callback常用的方法
//用来判断需要拦截那个view对象用来拖动 override fun tryCaptureView(child: View, pointerId: Int): Boolean //用来控制拖动的边界,默认情况下,left和top就是系统默认计算的边界值 override fun clampViewPositionHorizontal(child: View, left: Int, dx: Int): Int override fun clampViewPositionVertical(child: View, top: Int, dy: Int): Int //手指释放时候的反馈 override fun onViewReleased(releasedChild: View, xvel: Float, yvel: Float) //触摸边界时候回调 override fun onEdgeDragStarted(edgeFlags: Int, pointerId: Int)2、实现一个简单的拖动移动效果
上面三个分别是普通拖动(无回弹效果)、推动松手后回弹、在布局边缘拖动效果。
class SimpleViewDragHelperUseView2 @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : LinearLayoutCompat(context, attrs, defStyleAttr) {
private var mTouch1: TextView? = null
private var mTouch2: TextView? = null
private var mTouch3: TextView? = null
private var mItemViewDragHelper: ViewDragHelper? = null
private var mTouch1OriginLeft: Int = 0
private var mTouch1OriginTop: Int = 0
private var mTouch2OriginLeft: Int = 0
private var mTouch2OriginTop: Int = 0
private var mTouch3OriginLeft: Int = 0
private var mTouch3OriginTop: Int = 0
init {
isFocusableInTouchMode = true
isMotionEventSplittingEnabled = true
mItemViewDragHelper =
ViewDragHelper.create(this, object : ViewDragHelper.Callback() {
override fun getViewHorizontalDragRange(child: View): Int {
return mItemViewDragHelper?.touchSlop ?: 0
}
override fun getViewVerticalDragRange(child: View): Int {
return mItemViewDragHelper?.touchSlop ?: 0
}
override fun tryCaptureView(child: View, pointerId: Int): Boolean {
return child == mTouch1 || child == mTouch2
}
override fun clampViewPositionHorizontal(child: View, left: Int, dx: Int): Int {
return left
}
override fun clampViewPositionVertical(child: View, top: Int, dy: Int): Int {
return top
}
override fun onViewReleased(releasedChild: View, xvel: Float, yvel: Float) {
releasedChild.let {
if (it == mTouch2) {
mItemViewDragHelper?.smoothSlideViewTo(
it,
mTouch2OriginLeft,
mTouch2OriginTop
)
} else if (it == mTouch3) {
mItemViewDragHelper?.smoothSlideViewTo(
it,
mTouch3OriginLeft,
mTouch3OriginTop
)
}
ViewCompat.postInvalidateOnAnimation(this@SimpleViewDragHelperUseView2)
}
}
override fun onEdgeDragStarted(edgeFlags: Int, pointerId: Int) {
//边界拖动触发后,设置拖动事件捕获touch3
mTouch3?.let {
mItemViewDragHelper?.captureChildView(it, pointerId)
}
}
})
//设置边界拖动可用
mItemViewDragHelper?.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT)
}
override fun onFinishInflate() {
super.onFinishInflate()
mTouch1 = findViewById(R.id.tvTouchV1)
mTouch2 = findViewById(R.id.tvTouchV2)
mTouch3 = findViewById(R.id.tvTouchV3)
}
override fun computeScroll() {
if (mItemViewDragHelper?.continueSettling(true) == true) {
ViewCompat.postInvalidateOnAnimation(this)
}
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
super.onLayout(changed, l, t, r, b)
mTouch1OriginLeft = mTouch1?.left ?: 0
mTouch1OriginTop = mTouch1?.top ?: 0
mTouch2OriginLeft = mTouch2?.left ?: 0
mTouch2OriginTop = mTouch2?.top ?: 0
mTouch3OriginLeft = mTouch3?.left ?: 0
mTouch3OriginTop = mTouch3?.top ?: 0
}
private var startX = 0F
private var startY = 0F
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
var result: Boolean = mItemViewDragHelper?.shouldInterceptTouchEvent(ev) == true
val curX = ev.x
val curY = ev.y
when (ev.action) {
MotionEvent.ACTION_DOWN -> {
startX = curX
startY = curY
}
MotionEvent.ACTION_MOVE -> {
if (abs(curX - startX) > mItemViewDragHelper?.touchSlop ?: 0 || abs(curY - startY) > mItemViewDragHelper?.touchSlop ?: 0) {
result = true
}
}
}
return result
}
override fun onTouchEvent(event: MotionEvent): Boolean {
if (event.action == MotionEvent.ACTION_DOWN) {
this.performClick()
}
mItemViewDragHelper?.processTouchEvent(event)
return true
}
}



