之前遇到一个需求,实现一个悬浮窗效果:一个ViewGroup里面包含若干个子控件,ViewGroup有点击事件,然后整体可以拖动。
当时在网上看了几篇博客,基本上都是只有一个View控件的悬浮窗,有的ViewGroup实现的,又会有一些Bug,主要是Touch事件的冲突问题。而且在部分手机、系统上又会有问题(被魅族手机坑惨了,魅族真的与众不同!),因此,只得自己研究研究。
实现思路:重写onInterceptTouchEvent 方法,在 MotionEvent.ACTION_DOWN 手指按下的时候记录下初始坐标,然后在MotionEvent.ACTION_UP 手指离开屏幕的时候记录下结束坐标,通过计算出坐标差值来判断是否移动,如果没有移动则执行点击事件处理。
LinearLayout
/**
* 自定义可拖动的LinearLayout
* Created by MS on 2018/6/26.
*/
public class DragViewGroup extends LinearLayout {
private int lastX, lastY, screenWidth, screenHeight;
private int downX, downY;//手指落下时候的位置
private int upX, upY;//手指抬起时候的位置
public int distanceX, distanceY;//移动距离
public DragViewGroup(Context context) {
this(context, null);
}
public DragViewGroup(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DragViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
DisplayMetrics dm = getResources().getDisplayMetrics();
screenWidth = dm.widthPixels;
}
//定位
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
//可以在这里确定这个viewGroup的:宽 = r-l.高 = b - t
}
public void setScreenHeight(int height) {
this.screenHeight = height;
}
//拦截touch事件
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
lastX = (int) ev.getRawX();//设定移动的初始位置相对位置
lastY = (int) ev.getRawY();
downX = lastX;
downY = lastY;
Log.i("sdfasf", "lastX:" + lastX + "");
Log.i("sdfasf", "lastY:" + lastY + "");
break;
case MotionEvent.ACTION_MOVE://移动
//event.getRawX()事件点距离屏幕左上角的距离
int dx = (int) ev.getRawX() - lastX;
int dy = (int) ev.getRawY() - lastY;
int left = this.getLeft() + dx;
int top = this.getTop() + dy;
int right = this.getRight() + dx;
int bottom = this.getBottom() + dy;
if (left < 0) { //最左边
left = 0;
right = left + this.getWidth();
}
if (right > screenWidth) { //最右边
right = screenWidth;
left = right - this.getWidth();
}
if (top < 0) { //最上边
top = 0;
bottom = top + this.getHeight();
}
if (bottom > screenHeight) {//最下边
bottom = screenHeight;
top = bottom - this.getHeight();
}
this.layout(left, top, right, bottom);//设置控件的新位置
lastX = (int) ev.getRawX();//再次将滑动其实位置定位
lastY = (int) ev.getRawY();
break;
case MotionEvent.ACTION_UP:
lastX = (int) ev.getRawX();//设定移动的初始位置相对位置
lastY = (int) ev.getRawY();
upX = lastX;
upY = lastY;
Log.i("sdfasf", "lastX:" + lastX + "");
Log.i("sdfasf", "lastY:" + lastY + "");
distanceX = upX - downX;//记录其移动的距离
distanceY = upY - downY;
// 每次移动都要设置其layout,不然移动的view会回到原来的位置
RelativeLayout.LayoutParams lpFeedback = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
lpFeedback.leftMargin = this.getLeft();
lpFeedback.topMargin = this.getTop();
lpFeedback.setMargins(this.getLeft(), this.getTop(), 0, 0);
this.setLayoutParams(lpFeedback);
break;
default:
break;
}
return super.onInterceptTouchEvent(ev);
}
}
设置监听调用
case R.id.rel_content://悬浮窗内容点击
int distanceX = mDragViewGroup.distanceX;
int distanceY = mDragViewGroup.distanceY;
if (distanceX==0&&distanceY==0){
//点击事件处理 表示没有移动
}
}
break;
功能简单,只是感于被魅族手机坑了很久,因此记下。如有不对的地方请大神指教
网友评论