本文实例为大家分享了android实现可拖动的浮动view,供大家参考,具体内容如下
业务来源
页面最小化后,需要出现一个浮动 view 告知用户,防止遮挡视线,需要对 view 做可滑动处理
已知会遇到的问题
1.view 的依赖的布局类型未知【为了后续方便扩展】
外界传递 ViewGroup 自己本身继承 LinearLayout【或者其他 ViewGroup 】
class FloatChannelView(var mContext: Context?, var viewGroup: ViewGroup) : LinearLayout(mContext){ private var mIcon: ImageView = ImageView(context) private var mName: TextView = TextView(context) private var mClose: ImageView = ImageView(context) private var iconWH = dip2Px(38) private var groupPadding = dip2Px(3) private var mViewGroupH = dip2Px(44) private var mViewGroupW = dip2Px(152) private var mBoundaryLeft: Float private var mBoundaryTop: Float private var mBoundaryRight: Float private var mBoundaryBottom: Float private var mScreenWidth = getScreenWidth() // 获取屏幕宽高 private var mScreenHeight = getScreenHeight() private var mDownEventX: Float = 0f // 相对控件的x private var mDownEventY: Float = 0f private var mDownX: Float = 0f // 相对屏幕所在的 x private var mDownY: Float = 0f private var mListener: OnClickListener? = null private var mIsStartAnimation: Boolean = false private val mDefaultMargin = dip2Px(12) private var mMarginLeft = mDefaultMargin private var mMarginTop = mDefaultMargin private var mMarginRight = mDefaultMargin private var mMarginBottom = mDefaultMargin init { layoutParams = LayoutParams(mViewGroupW, mViewGroupH) setPadding(groupPadding, groupPadding, groupPadding, groupPadding) setBackgroundResource(R.drawable.backage) // 建议加一些透明 orientation = HORIZONTAL gravity = Gravity.CENTER_VERTICAL mBoundaryLeft = mMarginLeft.toFloat() mBoundaryTop = mMarginTop.toFloat() mBoundaryRight = mScreenWidth - mMarginRight.toFloat() mBoundaryBottom = (mScreenHeight - mMarginBottom - dip2Px(85)).toFloat() setView() } }
2.拖动事件影响点击,事件分发处理。
override fun onTouchEvent(event: MotionEvent?): Boolean { if (mIsStartAnimation) { // 动画正在进行无需处理 onTouch return true } if (event == null) { return super.onTouchEvent(event) } mIsOnTouch = true //悬浮区域左上角坐标 val x = x val y = y //悬浮区域宽高 val width = mViewGroupW val height = mViewGroupH when (event.actionMasked) { ACTION_DOWN -> { //点击位置坐标 mDownEventX = event.x mDownEventY = event.y mDownX = x mDownY = y } ACTION_UP -> { mUpTime = System.currentTimeMillis() if (mIsMove && abs(mDownX - x) <= 8f && abs(mDownY - y) <= 8f) { mListener?.onClick(this) } mIsMove = false // 抬起后处理边界溢出问题 resilienceAnimation(x, y, x + mViewGroupW, y + mViewGroupH) } ACTION_MOVE -> { val changeX = event.x.toInt() - mDownEventX val changeY = event.y.toInt() - mDownEventY mIsMove = true if (changeX == 0f && changeY == 0f) { return super.onTouchEvent(event) } val left = (x + changeX).toInt() val top = (y + changeY).toInt() val right = left + mViewGroupW val bottom = top + mViewGroupH layout(left, top, right, bottom) } } return true }
3.拖到边界问题。
拖出边界后做了回弹处理
/** * 超出边界回弹 * @param left 当前 x 方向位置 * @param right 当前 y 方向位置 */ private fun resilienceAnimation(left: Float, top: Float, right: Float, bottom: Float) { var startX = 0f var resilienceX = 0f if (mBoundaryLeft <= left && right <= mBoundaryRight) { // x 方向在范围内 // 不处理 } else if (mBoundaryLeft > left) { // left 溢出 startX = 0f resilienceX = mBoundaryLeft - left } else { // right 方向底部溢出 startX = 0f resilienceX = mBoundaryRight - right } var startY = 0f var resilienceY = 0f if (mBoundaryTop <= top && bottom <= mBoundaryBottom) { // y 方向在范围内 // 不处理 } else if (mBoundaryTop > top) { // top 溢出 startY = 0f resilienceY = mBoundaryTop - top } else { // bottom 溢出 startY = 0f resilienceY = mBoundaryBottom - bottom } if (resilienceX == 0f && resilienceY == 0f) { // 在范围内无需回弹 return } // 超出边界回弹 val phaseFirstDuration: Long = 400 var oAnimPhaseFirstTUpX: ObjectAnimator? = null if (resilienceX != 0f) { oAnimPhaseFirstTUpX = ObjectAnimator.ofFloat(this, "translationX", startX, resilienceX) .setDuration(phaseFirstDuration) } var oAnimPhaseFirstTUpY: ObjectAnimator? = null if (resilienceY != 0f) { oAnimPhaseFirstTUpY = ObjectAnimator.ofFloat(this, "translationY", startY, resilienceY) .setDuration(phaseFirstDuration) } val animatorSet = AnimatorSet() if (oAnimPhaseFirstTUpX != null && oAnimPhaseFirstTUpY != null) { animatorSet.play(oAnimPhaseFirstTUpX).with(oAnimPhaseFirstTUpY) } else if (oAnimPhaseFirstTUpX != null) { animatorSet.play(oAnimPhaseFirstTUpX) } else { animatorSet.play(oAnimPhaseFirstTUpY) } animatorSet.childAnimations[animatorSet.childAnimations.size - 1].addListener(object : Animator.AnimatorListener { override fun onAnimationStart(animation: Animator?) { mIsStartAnimation = true } override fun onAnimationEnd(animation: Animator?) { var l = left var t = top var r = right var b = bottom when { mBoundaryLeft > left -> { // x左边溢出 l = mBoundaryLeft r = mBoundaryLeft + mViewGroupW } mBoundaryRight < right -> { // x右边溢出 l = mBoundaryRight - mViewGroupW r = mBoundaryRight } else -> { // x方向未溢出 } } when { mBoundaryTop > top -> { // y 顶部溢出 t = mBoundaryTop b = mBoundaryTop + mViewGroupH } mBoundaryBottom < bottom -> { // y 底部溢出 t = mBoundaryBottom - mViewGroupH b = mBoundaryBottom } else -> { // y方向未溢出 } } // 只进行偏移,实际位置未变化,需要重置偏移量,并重绘 this@FloatChannelView.translationX = 0f this@FloatChannelView.translationY = 0f layout(l.toInt(), t.toInt(), r.toInt(), b.toInt()) mMarginLeft = l.toInt() mMarginTop = t.toInt() mIsStartAnimation = false } override fun onAnimationCancel(animation: Animator?) {} override fun onAnimationRepeat(animation: Animator?) {} }) animatorSet.start()
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。