您的位置:首页 > 其它

使用layout方法实现控件的移动并且不影响点击事件

2015-10-03 02:01 726 查看
控件移动的方法很多种,譬如layout,属性动画,scrollBy和scrollTo等等…

接下来我将使用layout方法来演示控件的移动,并且处理点击事件冲突,以及父布局刷新的时候空间回到原处的处理。

使用layout的好处是适合于有交互的view,相比属性动画,不能兼容到3.0以下,即使使用大神的nineoldandroids3.0下实现的仍旧是view动画;相比scrollBy和scrollTo,虽然操作简单但是只是对view内容的滑动。

接下来将演示对FloatActionButton的移动实现,因为在实际中有这样的需求,浏览商品是一瀑布流,然后item是一张商品图片加快速收藏和购买的按钮,当用户拉到底部全部加载完的时候,右下角的悬浮按钮就刚好挡住了购买的按钮,所以为了人性化,将悬浮按钮做成可移动的,让用户自己调整位置。

简单的效果图如下



实现原理相当简单,继承FloatActionButton重写onTouchEvent方法,记录坐标值调layout同时记录下移动后的坐标;然后继承FloatActionButton所在的父布局的类,重写onlayout,遍历子布局,拿到我们需要的子控件调layout,参数使用的是记录好的坐标,即可。

XML如下,MyCoordinatorLayout是重写onLayout的CoordinatorLayout;

DraggableFloatingButton是重写onTouchEvent的FloatActionButton

<?xml version="1.0" encoding="utf-8"?>
<com.oushangfeng.floatactionbuttondemo.MyCoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".MainActivity">

<android.support.design.widget.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:theme="@style/AppTheme.AppBarOverlay">

<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay"/>

</android.support.design.widget.AppBarLayout>

<include layout="@layout/content_main"/>

<com.oushangfeng.floatactionbuttondemo.DraggableFloatingButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
android:src="@android:drawable/ic_dialog_email"/>

</com.oushangfeng.floatactionbuttondemo.MyCoordinatorLayout>


首先看下DraggableFloatingButton:

①ACTION_DOWN时候记录系统时间,再与ACTION_UP时间作比较,这里设定200ms,即按下松开200ms内,视为动作有效响应此事件;

②ACTION_MOVE会拿到控件的上下左右坐标,加上移动量,进行边界的判断,最后调研layout移动,并记录下移动后的上下左右坐标值

// 记录上次的xy坐标
private int mLastX, mLastY;
// 记录按下和松开手指的时间
private long mDownTime, mUpTime;

// 记录上次移动完的上下左右坐标
private int mLastLeft = -1;
private int mLastRight = -1;
private int mLastTop = -1;
private int mLastBottom = -1;

public int getLastLeft() {
return mLastLeft;
}

public int getLastRight() {
return mLastRight;
}

public int getLastTop() {
return mLastTop;
}

public int getLastBottom() {
return mLastBottom;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getRawX();
int y = (int) event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mDownTime = System.currentTimeMillis();
break;
case MotionEvent.ACTION_MOVE:

int deltaX = x - mLastX;
int deltaY = y - mLastY;

// 移动后的上下左右坐标
int left = getLeft() + deltaX;
int right = getRight() + deltaX;
int top = getTop() + deltaY;
int bottom = getBottom() + deltaY;

int marginLeft = 0;
int marginRight = 0;
int marginTop = 0;
int marginBottom = 0;

if (getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
// 获取margin值,做移动的边界判断
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams)
getLayoutParams();
marginLeft = lp.leftMargin;
marginRight = lp.rightMargin;
marginTop = lp.topMargin;
marginBottom = lp.bottomMargin;
}

int parentWidth = 0;
int parentHeight = 0;

if (getParent() != null && getParent() instanceof ViewGroup) {

ViewGroup parent = (ViewGroup) getParent();

// 拿到父布局宽高
parentWidth = parent.getWidth();
parentHeight = parent.getHeight();

if (left < marginLeft) {
// 移到到最左的时候,限制在marginLeft
left = marginLeft;
right = getWidth() + left;
}

if (right > parentWidth - marginRight) {
// 移动到最右
right = parentWidth - marginRight;
left = right - getWidth();
}

if (top < marginTop) {
// 移动到顶部
top = marginTop;
bottom = getHeight() + top;
}

if (bottom > parentHeight - marginBottom) {
// 移动到底部
bottom = parentHeight - marginBottom;
top = bottom - getHeight();
}

layout(left, top, right, bottom);

// 记录移动后的坐标值
mLastLeft = left;
mLastRight = right;
mLastTop = top;
mLastBottom = bottom;

}

break;
case MotionEvent.ACTION_UP:
mLastX = x;
mLastY = y;
mUpTime = System.currentTimeMillis();
// 点击事件的处理
return mUpTime - mDownTime > 200 || super.onTouchEvent(event);
}

mLastX = x;
mLastY = y;

return super.onTouchEvent(event);
}


接下来看下MyCoordinatorLayout:

这个更简单,在刷新子控件位置的时候,去遍历一下,找到DraggableFloatingButton,如果left不为-1则说明之前移动过了,子控件再自己调次layout用自己记录的上次移动后的坐标,即可保证位置在父布局刷新的时候不受影响

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
for (int i = 0; i < getChildCount(); i++) {
if (getChildAt(i) instanceof DraggableFloatingButton) {
// 为了防止浮动按钮恢复原位,布局子控件位置时使用上次记录的位置
DraggableFloatingButton child = (DraggableFloatingButton) getChildAt(i);
if (child.getLastLeft() != -1) {
child.layout(child.getLastLeft(), child.getLastTop(), child.getLastRight(),
child.getLastBottom());
}
break;
}
}
}


实在是很简单,附demo

可拖动的FloatActionButton
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息