使用layout方法实现控件的移动并且不影响点击事件
2015-10-03 02:01
726 查看
控件移动的方法很多种,譬如layout,属性动画,scrollBy和scrollTo等等…
接下来我将使用layout方法来演示控件的移动,并且处理点击事件冲突,以及父布局刷新的时候空间回到原处的处理。
使用layout的好处是适合于有交互的view,相比属性动画,不能兼容到3.0以下,即使使用大神的nineoldandroids3.0下实现的仍旧是view动画;相比scrollBy和scrollTo,虽然操作简单但是只是对view内容的滑动。
接下来将演示对FloatActionButton的移动实现,因为在实际中有这样的需求,浏览商品是一瀑布流,然后item是一张商品图片加快速收藏和购买的按钮,当用户拉到底部全部加载完的时候,右下角的悬浮按钮就刚好挡住了购买的按钮,所以为了人性化,将悬浮按钮做成可移动的,让用户自己调整位置。
![](https://oscdn.geek-share.com/Uploads/Images/Content/201510/6dcd04d0f0c4c2b3bf4650fcb6ecac45)
实现原理相当简单,继承FloatActionButton重写onTouchEvent方法,记录坐标值调layout同时记录下移动后的坐标;然后继承FloatActionButton所在的父布局的类,重写onlayout,遍历子布局,拿到我们需要的子控件调layout,参数使用的是记录好的坐标,即可。
XML如下,MyCoordinatorLayout是重写onLayout的CoordinatorLayout;
DraggableFloatingButton是重写onTouchEvent的FloatActionButton
首先看下DraggableFloatingButton:
①ACTION_DOWN时候记录系统时间,再与ACTION_UP时间作比较,这里设定200ms,即按下松开200ms内,视为动作有效响应此事件;
②ACTION_MOVE会拿到控件的上下左右坐标,加上移动量,进行边界的判断,最后调研layout移动,并记录下移动后的上下左右坐标值
接下来看下MyCoordinatorLayout:
这个更简单,在刷新子控件位置的时候,去遍历一下,找到DraggableFloatingButton,如果left不为-1则说明之前移动过了,子控件再自己调次layout用自己记录的上次移动后的坐标,即可保证位置在父布局刷新的时候不受影响
实在是很简单,附demo
可拖动的FloatActionButton
接下来我将使用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
相关文章推荐
- Android布局的小窍门?
- Web布局连载——两栏固定布局(五)
- 在 Linux 中如何移动文件
- flex 控件的重要属性
- Delphi控件ListView的属性及使用方法详解
- 样式表CSS布局经验
- PowerShell移动目录中指定文件的方法(非全部文件)
- 鼠标触发移动的分层菜单 层菜单moveMenu
- web下载的ActiveX控件自动更新
- WinForm实现按名称递归查找控件的方法
- css网页布局中注意的几个问题小结
- DL.DT.DD实现左右的布局简单例子第1/2页
- 使用CSS框架布局的缺点和优点小结
- div+CSS网页布局的意义与副作用原因小结第1/2页
- C#中父窗口和子窗口之间控件互操作实例
- Android控件之CheckBox、RadioButton用法实例分析
- MFC中动态创建控件以及事件响应实现方法
- WinForm自定义函数FindControl实现按名称查找控件
- Android控件之ProgressBar用法实例分析
- CSS顶级技巧大放送,div+css布局必知