Android自定义拖拽加吸边View代码赏析
Posted 汤米粥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android自定义拖拽加吸边View代码赏析相关的知识,希望对你有一定的参考价值。
package com.fenqile.shakefeedback.floatingview;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.fenqile.shakefeedback.floatingview.utils.SystemUtils;
/**
* @ClassName FloatingMagnetView
* @Description 磁力吸附悬浮窗
* @Author Yunpeng Li
* @Creation 2018/3/15 下午5:02
* @Mender Yunpeng Li
* @Modification 2018/3/15 下午5:02
*/
public class FloatingMagnetView extends FrameLayout
public static final int MARGIN_EDGE = 33;
private float mOriginalRawX;
private float mOriginalRawY;
private float mOriginalX;
private float mOriginalY;
private MagnetViewListener mMagnetViewListener;
private static final int TOUCH_TIME_THRESHOLD = 150;
private long mLastTouchDownTime;
注意,这里Animator并没有用什么属性动画,而是用最基础的代码实现的移动动画,超赞!
protected MoveAnimator mMoveAnimator;
protected int mScreenWidth;
private int mScreenHeight;
private int mStatusBarHeight;
private boolean isNearestLeft = true;
private float mPortraitY;
private boolean dragEnable = true;
private boolean autoMoveToEdge = true;
public void setMagnetViewListener(MagnetViewListener magnetViewListener)
this.mMagnetViewListener = magnetViewListener;
public FloatingMagnetView(Context context)
this(context, null);
public FloatingMagnetView(Context context, AttributeSet attrs)
this(context, attrs, 0);
public FloatingMagnetView(Context context, AttributeSet attrs, int defStyleAttr)
super(context, attrs, defStyleAttr);
init();
private void init()
mMoveAnimator = new MoveAnimator();
mStatusBarHeight = SystemUtils.getStatusBarHeight(getContext());
setClickable(true);
// updateSize();
/**
* @param dragEnable 是否可拖动
*/
public void updateDragState(boolean dragEnable)
this.dragEnable = dragEnable;
/**
* @param autoMoveToEdge 是否自动到边缘
*/
public void setAutoMoveToEdge(boolean autoMoveToEdge)
this.autoMoveToEdge = autoMoveToEdge;
@Override
public boolean onTouchEvent(MotionEvent event)
if (event == null)
return false;
switch (event.getAction())
case MotionEvent.ACTION_DOWN:
dealDownEvent();
break;
case MotionEvent.ACTION_MOVE:
updateViewPosition(event);
break;
case MotionEvent.ACTION_UP:
clearPortraitY();
if (autoMoveToEdge)
moveToEdge();
if (isOnClickEvent())
dealClickEvent();
else
dealUpEvent();
break;
return true;
protected void dealUpEvent()
if (mMagnetViewListener != null)
mMagnetViewListener.onUp(this);
protected void dealClickEvent()
if (mMagnetViewListener != null)
mMagnetViewListener.onClick(this);
protected void dealDownEvent()
if (mMagnetViewListener != null)
mMagnetViewListener.onDown(this);
protected boolean isOnClickEvent()
return System.currentTimeMillis() - mLastTouchDownTime < TOUCH_TIME_THRESHOLD;
private void updateViewPosition(MotionEvent event)
//dragEnable
if (!dragEnable) return;
//占满width或height时不用变
LayoutParams params = (LayoutParams) getLayoutParams();
//限制不可超出屏幕宽度
float desX = mOriginalX + event.getRawX() - mOriginalRawX;
if (params.width == LayoutParams.WRAP_CONTENT)
if (desX < 0)
desX = MARGIN_EDGE;
if (desX > mScreenWidth)
desX = mScreenWidth - MARGIN_EDGE;
setX(desX);
// 限制不可超出屏幕高度
float desY = mOriginalY + event.getRawY() - mOriginalRawY;
if (params.height == LayoutParams.WRAP_CONTENT)
if (desY < mStatusBarHeight)
desY = mStatusBarHeight;
if (desY > mScreenHeight - getHeight())
desY = mScreenHeight - getHeight();
setY(desY);
private void changeOriginalTouchParams(MotionEvent event)
mOriginalX = getX();
mOriginalY = getY();
mOriginalRawX = event.getRawX();
mOriginalRawY = event.getRawY();
mLastTouchDownTime = System.currentTimeMillis();
protected void updateSize()
ViewGroup viewGroup = (ViewGroup) getParent();
if (viewGroup != null)
mScreenWidth = viewGroup.getWidth() - getWidth();
mScreenHeight = viewGroup.getHeight();
// mScreenWidth = (SystemUtils.getScreenWidth(getContext()) - this.getWidth());
// mScreenHeight = SystemUtils.getScreenHeight(getContext());
public void moveToEdge()
//dragEnable
if (!dragEnable) return;
moveToEdge(isNearestLeft(), false);
public void moveToEdge(boolean isLeft, boolean isLandscape)
float moveDistance = isLeft ? MARGIN_EDGE : mScreenWidth - MARGIN_EDGE;
float y = getY();
if (!isLandscape && mPortraitY != 0)
y = mPortraitY;
clearPortraitY();
mMoveAnimator.start(moveDistance, Math.min(Math.max(0, y), mScreenHeight - getHeight()));
private void clearPortraitY()
mPortraitY = 0;
protected boolean isNearestLeft()
int middle = mScreenWidth / 2;
isNearestLeft = getX() < middle;
return isNearestLeft;
public void onRemove()
if (mMagnetViewListener != null)
mMagnetViewListener.onRemove(this);
//自定义移动动画
protected class MoveAnimator implements Runnable
private Handler handler = new Handler(Looper.getMainLooper());
private float destinationX;
private float destinationY;
private long startingTime;
void start(float x, float y)
this.destinationX = x;
this.destinationY = y;
startingTime = System.currentTimeMillis();
handler.post(this);
@Override
public void run()
if (getRootView() == null || getRootView().getParent() == null)
return;
//400ms
float progress = Math.min(1, (System.currentTimeMillis() - startingTime) / 400f);
float deltaX = (destinationX - getX()) * progress;
float deltaY = (destinationY - getY()) * progress;
move(deltaX, deltaY);
if (progress < 1)
handler.post(this);
private void stop()
handler.removeCallbacks(this);
//通过不断的 setX,setY来移动位置
private void move(float deltaX, float deltaY)
setX(getX() + deltaX);
setY(getY() + deltaY);
@Override
protected void onConfigurationChanged(Configuration newConfig)
super.onConfigurationChanged(newConfig);
if (getParent() != null)
final boolean isLandscape = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
markPortraitY(isLandscape);
((ViewGroup) getParent()).post(new Runnable()
@Override
public void run()
updateSize();
moveToEdge(isNearestLeft, isLandscape);
);
private void markPortraitY(boolean isLandscape)
if (isLandscape)
mPortraitY = getY();
private float touchDownX;
private void initTouchDown(MotionEvent ev)
changeOriginalTouchParams(ev);
updateSize();
mMoveAnimator.stop();
//判断是否拦截父容器
@Override
public boolean onInterceptTouchEvent(MotionEvent ev)
boolean intercepted = false;
switch (ev.getActionMasked())
case MotionEvent.ACTION_DOWN:
intercepted = false;
touchDownX = ev.getX();
initTouchDown(ev);
break;
case MotionEvent.ACTION_MOVE:
intercepted = Math.abs(touchDownX - ev.getX()) >= ViewConfiguration.get(getContext()).getScaledTouchSlop();
break;
case MotionEvent.ACTION_UP:
intercepted = false;
break;
return intercepted;
以上是关于Android自定义拖拽加吸边View代码赏析的主要内容,如果未能解决你的问题,请参考以下文章