我们好,我是似曾相识2022。不喜欢唱跳篮球,但对杰伦的Rap却情有独钟。
今日给我们带来一个可全屏拖拽,手指离开屏暗地主动贴边,隔必定时间后主动半躲藏的这么一个效果。话不多说直接上效果图:
看到这个效果是不是感觉很熟悉?没错,许多商业APP首页都带一个小辅佐的图标,运用的时分点击它就主动弹出,不运用的时分主动贴边躲藏,当然也是可以随意全屏拖拽,为的是避免遮挡一些要害方位的信息,影响用户体验。接下来我们就来一步步完结它!
要完结上图效果我们得罗列一切的功用点:
- 自定义View,这儿要闪现图片所以继承自ImageView或其子类即可
- 监听屏幕滑动工作,记载和核算其时视图的方位信息
- 动画效果,很明显运用平移动画
- 圆角图片和描边,运用第三方ImageView即可
为了处理小圆球这个图标的问题我们自定View时直接继承自第三方RoundedImageView,一箭双雕直接处理了第一和第四步。我们把焦点聚集到第二三部分,这也是最为杂乱的部分。
之前文章 Android:自定义View完结图片缩放及坐标的核算(上) 中有写到监听界面各类手势可以运用GestureDetector,这儿我们就不选用重写onTouchEvent方法然后再里面监听各类ACTION_UP、ACTION_DOWN、ACTION_MOVE工作的形式来写了。但仍是需求重写onTouchEvent方法将GestureDetector的处理结果回来给它即可:
override fun onTouchEvent(event: MotionEvent): Boolean {
return gestureDetector.onTouchEvent(event)
}
接下来只需在GestureDetector入参的GestureDetector.SimpleOnGestureListener监听中履行对应的操作:
首要需求在onDown方法中记载终究点击屏幕的方位信息lastX、lastY,这儿备份一份点击时的方位信息moveX、moveY,用于后续逻辑判别。
override fun onDown(e: MotionEvent): Boolean {
lastX = e.rawX.toInt()
lastY = e.rawY.toInt()
moveX = lastX
moveY = lastY
return true
}
在onScroll中需求不断批改自定义视图的方位,所以我们需求核算出需求移动方位的信息。通过其时实时滑动点的信息和终究记载的点信息核算出滑动间隔,再重新核算其时视图的上下左右方位,终究我们采纳layout() 方法进行方位设置。
override fun onScroll(
e1: MotionEvent,
e2: MotionEvent,
distanceX: Float,
distanceY: Float
): Boolean {
//获取其时实时点信息
val rawX = e2.rawX.toInt()
val rawY = e2.rawY.toInt()
//改动量
dX = rawX - lastX
dY = rawY - lastY
//获取最新的视图方位
var left = left + dX
var right = right + dX
var top = top + dY
var bottom = bottom + dY
//增加约束规模,上下左右不能超出屏幕规模
if (left < 0) {
left = 0
right = left + width
}
if (right > windowWith) {
right = windowWith
left = right - width
}
if (top < 40) {
top = 40
bottom = top + height
}
if (bottom > windowHight) {
bottom = windowHight
top = bottom - height
}
//更新其时视图方位
layout(left, top, right, bottom)
//更新终究屏幕点信息
lastX = rawX
lastY = rawY
return true
}
到此,我们现已完结了可全屏拖拽的效果了:
现在只差终究一步,通过方位信息判别图标该往哪边贴边,以及移动间隔的核算。
因为GestureDetector没有抬起监听,所以逻辑我们仍是得在onTouchEvent方法中通过监听ACTION_UP的动作进行操作。判别该往哪边贴边很简单,假设终究松开的方位X坐标的超越屏幕一半就往右贴,反之往左。动画我们仍是运用ValueAnimator,因为我们移动也是用layout() 方法进行操作。
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_UP -> {
val x = event.rawX
val y = event.rawY
//抬起点和终究一次按下点x、y间隔大于视图宽的一半才履行
if (abs(x - moveX) > width / 2 || abs(y - moveY) > width / 2) {
val isRight = x > windowWith / 2
//贴边
startAnimator(isRight, windowWith - width, 0)
//隔1.5秒收边
postDelayed({
startAnimator(isRight, windowWith - width * 2 / 3, -width / 3)
}, 1500)
}
return true
}
}
return gestureDetector.onTouchEvent(event)
}
//属性动画履行
private fun startAnimator(isRight: Boolean, rightValue: Int, leftValue: Int) {
ValueAnimator.ofInt(
left,
if (isRight) rightValue else leftValue
).apply {
addUpdateListener { animation ->
val value = animation.animatedValue as Int
//根据监听值不断改动其时视图方位
layout(value, top, value + width, bottom)
}
//插值器 先快后慢
interpolator = AccelerateDecelerateInterpolator()
duration = 600
start()
}
}
这儿运用了两次动画,第一次根据核算得出的方向进行贴边平移,隔了1.5秒后再进行躲藏的操作。到此我们的一切功用全部都完结了接下来总结几点:
- 自定义View时尽量选择最接近方针功用的View进行继承
- 屏幕工作监听除了重写onTouchEvent进行动作监听的方法还有GestureDetector、ScaleGestureDetector等方法
- 重写了onTouchEvent方法后需求注意其回来值,假设都回来false的情况该视图的点击工作有或许会被父View或其他设有监听工作控件所消费,导致滑动监听不被触发。
以上便是完结一个全屏拖拽、主动贴边半躲藏的自定义View的一切内容,希望能给我们带来帮助!