由于项目需求需要按钮吸边,就自己动手写了下。对于通过手势拖拽我立马就想到了ViewDragHelper这个系统工具类。有了这个类的存在 我们只需要处理松开手指的逻辑处理和边界控制就行了。
下面是实现代码
public class SuctionSideView extends RelativeLayout {
private final String TAG = "SuctionSideView";
private ViewDragHelper mDragHelper; // 拖拽控制
private View mSuctionView; // 要吸边的view
private Point mAutoBackOriginPos = new Point(); // 记录子view现在的位置
private boolean isInit = false;
/**
* 1 | 2
* -----
* 3 | 4
*/
private int centerX;// 屏幕中心的x值
private int centerY;// 屏幕中心的y值 根据这两个属性建立直角坐标系
public SuctionSideView(@NonNull Context context) {
super(context,null);
}
public SuctionSideView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
addListener();
}
/**
* 初始化布局
* @throws Exception
*/
private void init() {
}
/**
* 监听事件注册
*/
private void addListener() {
mDragHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
return child == mSuctionView;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
final int leftBound = getPaddingLeft() + UIUtil.dp2px(10);
final int rightBound = getWidth() - child.getWidth() - leftBound - UIUtil.dp2px(10);
final int newLeft = Math.min(Math.max(left, leftBound), rightBound);
return newLeft;
}
@Override
public int getViewHorizontalDragRange(View child) {
return getMeasuredWidth() - child.getMeasuredWidth();
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
final int topBound = getPaddingTop() + UIUtil.dp2px(10);
final int bottomBound = getHeight() - child.getHeight() - getPaddingBottom() - UIUtil.dp2px(10);
final int newTop = Math.min(Math.max(top,topBound),bottomBound);
return newTop;
}
@Override
public int getViewVerticalDragRange(View child) {
return getMeasuredHeight() - child.getMeasuredHeight();
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
if (releasedChild == mSuctionView) {
if (releasedChild.getLeft() < centerX) { // 往左移动
moveTo(UIUtil.dp2px(10) + getPaddingLeft(), releasedChild.getTop());
} else { // 往右
moveTo(getWidth() - UIUtil.dp2px(59) - getPaddingRight(), releasedChild.getTop());
}
// if (releasedChild.getLeft() < centerX && releasedChild.getTop() < centerY) { // 1象限
// if (releasedChild.getLeft() < releasedChild.getTop()) { // 往左
// moveTo(0, releasedChild.getTop());
// } else { // 网上
// moveTo(releasedChild.getLeft(), 0);
// }
// } else if (releasedChild.getLeft() > centerX && releasedChild.getTop() < centerY) { // 2象限
// if (getWidth() - releasedChild.getLeft() - getWidth() < getTop() - releasedChild.getHeight()) { // 往右
// moveTo(getWidth() - releasedChild.getWidth() - getPaddingRight(), releasedChild.getTop());
// } else { // 往上
// moveTo(releasedChild.getLeft(), 0);
// }
// } else if (releasedChild.getLeft() < centerX && releasedChild.getTop() > centerY) { // 3 象限
// if (releasedChild.getLeft() < getHeight() - releasedChild.getTop() - releasedChild.getHeight()) { // 往左
// moveTo(0, releasedChild.getTop());
// } else { // 往下
// moveTo(releasedChild.getLeft(), getHeight() - releasedChild.getHeight() - getPaddingBottom());
// }
// } else if (releasedChild.getLeft() > centerX && releasedChild.getTop() > centerY) { // 4象限
// if (getWidth() - releasedChild.getLeft() - releasedChild.getWidth() < getHeight() - releasedChild.getTop() - releasedChild.getHeight()) { // 向右
// moveTo(getWidth() - releasedChild.getWidth() - getPaddingRight(), releasedChild.getTop());
// } else { // 往下
// moveTo(releasedChild.getLeft(), getHeight() - releasedChild.getHeight() - getPaddingBottom());
// }
//
// }
ViewCompat.postInvalidateOnAnimation(SuctionSideView.this);
}
}
});
}
private void moveTo(int xvel, int yvel) {
mAutoBackOriginPos.x = xvel;
mAutoBackOriginPos.y = yvel;
mDragHelper.smoothSlideViewTo(mSuctionView,xvel,yvel);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mSuctionView = getChildAt(0);
LogUtil.i(TAG,"onFinishInflate");
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
centerX = getWidth() / 2;
centerY = getHeight() / 2;
if (!isInit) {
mAutoBackOriginPos.x = getWidth() - UIUtil.dp2px(59);
mAutoBackOriginPos.y = getHeight() - UIUtil.dp2px(75);
isInit = true;
mSuctionView.layout(mAutoBackOriginPos.x,mAutoBackOriginPos.y,mAutoBackOriginPos.x + UIUtil.dp2px(49),mAutoBackOriginPos.y + UIUtil.dp2px(65));
LogUtil.i(TAG,"mAutoBackOriginPos.x:" + mAutoBackOriginPos.x + "mAutoBackOriginPos.y" + mAutoBackOriginPos.y);
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mDragHelper.processTouchEvent(event);
return false;
}
@Override
public void computeScroll() {
if (mDragHelper.continueSettling(true)) {
invalidate();
}
}
}
这里面记录下吸边控件的初始位置。
clampViewPositionHorizontal与clampViewPositionVertical
这是控制吸边控件的上下左右能够活动位置
由于我的项目中值需要左右吸边所以我在
onViewReleased 里只需要判断当前控件的left超过中心线了没
其中涉及到的数字为吸边控件距左距右和控件本身的尺寸,整个代码差不多100行是不是很简单吧。
使用也非常简单 用自定义好的控件包裹住需要被吸边的控件
<cn.lhj.home.widget.SuctionSideView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/title_bar">
<ImageView
android:id="@+id/view"
android:layout_width="49dp"
android:layout_height="65dp"
android:layout_gravity="right|bottom"
android:layout_marginBottom="10dp"
android:layout_marginRight="10dp"
android:scaleType="fitXY"
android:visibility="gone" />
</cn.lhj.home.widget.SuctionSideView>