主要功能項(xiàng):
1. 可以拖動(dòng)
2. 拖動(dòng)完可以自動(dòng)貼邊
3. 可處理點(diǎn)擊事件
4. 可變化背景圖(美術(shù)改圖,棄之不用啊,可惜可惜)
4.1 吸附左邊的時(shí)候,背景為左邊直角右邊圓角的矩形
4.2 吸附右邊的時(shí)候,背景為右邊直角左邊圓角的矩形
4.3 移動(dòng)的時(shí)候,背景為圓角的矩形
第一步.上代碼
class FloatView(context: Context, attributeSet: AttributeSet) :
RelativeLayout(context, attributeSet) {
private val MORE_SPACE = 10.0 //移動(dòng)距離像素,超過才算移動(dòng)
private var mView: View? = null
init {
mView = LayoutInflater.from(context).inflate(R.layout.layout_float_view, this, true)
val rotation = ObjectAnimator.ofFloat(mView?.iv_playing_bg, "rotation", 0f, 360f)
rotation.duration = 15000
rotation.repeatCount = -1
rotation.start()
val scaleX = ObjectAnimator.ofFloat(mView?.iv_playing_bg, "scaleX", 0.5f, 1f, 0.5f)
scaleX.duration = 15000
scaleX.repeatCount = -1
scaleX.start()
val scaleY = ObjectAnimator.ofFloat(mView?.iv_playing_bg, "scaleY", 0.5f, 1f, 0.5f)
scaleY.duration = 15000
scaleY.repeatCount = -1
scaleY.start()
}
//控件的寬高
private var mWidth = 0
private var mHeight = 0
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
mWidth = w
mHeight = h
}
//屏幕寬高以及半屏和上下邊距
private var mScreenWith = 0
private var mScreenHeight = 0
private var halfWith = 0
private var mTop = 0
private var mBottom = 0
/**
* 設(shè)置父布局的大小
*/
fun setParentSize(width: Int, height: Int, top: Int, bottom: Int) {
mScreenWith = width
mScreenHeight = height
halfWith = width / 2
mTop = top
mBottom = bottom
}
private var lastX = 0f //按下x
private var lastY = 0f //按下y
private var isMove = false //是拖動(dòng)?
private var isPress = false //是點(diǎn)擊?
private var offsetX = 0f //偏移量x,移動(dòng)的時(shí)候不加偏移量,按鈕會(huì)發(fā)生位置錯(cuò)誤,手指下會(huì)是按鈕的左上角,而不是最初按下位置
private var offsetY = 0f //偏移量y
private var isChangeBg = false //吸邊的時(shí)候可以更改變化按鈕的左右兩邊圓角,后面美術(shù)改了個(gè)方圖,不用了,但思路留在這
override fun onTouchEvent(ev: MotionEvent): Boolean {
val x = ev.rawX
val y = ev.rawY
when (ev.action) {
MotionEvent.ACTION_DOWN -> {
isMove = false
isPress = true
lastX = x
lastY = y
offsetX = getX() - x
offsetY = getY() - y
}
MotionEvent.ACTION_MOVE -> {
isMove = isMove(ev)
isPress = if (isMove) {
if (!isChangeBg) {
isChangeBg = true
//思路:這里換背景圖
}
val xandY = getXandY(x + offsetX, y + offsetY)
setX(xandY[0].toFloat())
setY(xandY[1].toFloat())
false
} else {
true
}
}
MotionEvent.ACTION_UP -> {
isChangeBg = false
if (isPress) {
callback?.click()
isPress = true
}
if (isMove) {
isMove = false
}
if (x != 0f || x != (mScreenWith - mWidth).toFloat()) {
if (x <= halfWith) {
//在左半邊,吸附左邊
if (x >lastX) {
animate().setInterpolator(DecelerateInterpolator())
.setDuration(500)
.xBy(-x - offsetX)
.start()
//思路:這里換背景圖
}
} else {
//在右半邊,吸附右邊
if (x < laxtX) {
animate().setInterpolator(DecelerateInterpolator())
.setDuration(500)
.xBy(mScreenWith - mWidth - x - offsetX)
.start()
//思路:這里換背景圖
}
}
}
}
}
return true
}
/**
* 獲取移動(dòng)到的x和y
*/
private fun getXandY(x: Float, y: Float): IntArray {
val resultX = when {
x < 0 -> {
0
//思路:這里換背景圖
}
x > mScreenWith - mWidth -> {
mScreenWith - mWidth
//思路:這里換背景圖
}
else -> {
x.toInt()
}
}
val resultY: Int = when {
y < mTop -> {
mTop
}
y > mScreenHeight - mBottom - mWidth -> {
mScreenHeight - mBottom - mWidth
}
else -> {
y.toInt()
}
}
return intArrayOf(resultX, resultY)
}
private var callback: OnClickCallback? = null
fun setOnClickCallback(callback: OnClickCallback) {
this.callback = callback
}
interface OnClickCallback {
fun click()
}
private fun isMove(ev: MotionEvent): Boolean {
val x = ev.rawX
val y = ev.rawY
val moreSpace =
Math.sqrt(((x - lastX) * (x - lastX) + (y - lastY) * (y - lastY)).toDouble())
return moreSpace > MORE_SPACE
}
}
第二步.上布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/dp_60"
android:layout_height="@dimen/dp_60">
<ImageView
android:id="@+id/iv_playing_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@mipmap/bg_playing" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@mipmap/playing"
android:layout_margin="@dimen/dp_5" />
</RelativeLayout>
第三步.使用
//設(shè)置可移動(dòng)范圍,正常是要貼邊手機(jī)兩側(cè),所以僅需要特別標(biāo)注上下邊距,畢竟不能滑動(dòng)到狀態(tài)欄和底部tab上去
floatView.setParentSize(手機(jī)寬,手機(jī)高,上邊距,下邊距)
//點(diǎn)擊事件嘛,處理點(diǎn)擊后的業(yè)務(wù)
floatView.setOnClickCallback(...)