悬浮View,应用内悬浮
本章给大家介绍自定义悬浮View,可随手指拖动,手指放开后会自动吸附到手机屏边缘,悬浮View内的Content布局可以任意放入自定义的布局,非常方便。
效果图:
核心部分是手指触摸事件处理,自定义View通过重写OnTouchEvent事件进行处理:
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (!mScrollEnable) return super.onTouchEvent(ev);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
clickDownX = ev.getX();
clickDownY = ev.getY();
clickDownTime = System.currentTimeMillis();
mX = ev.getX();
mY = ev.getY();
super.onTouchEvent(ev);
return true;
case MotionEvent.ACTION_MOVE:
int scaledTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
float x = ev.getX();
float y = ev.getY();
x = x - mX;
y = y - mY;
if (Math.abs(scaledTouchSlop) < Math.abs(x) || Math.abs(scaledTouchSlop) < Math.abs(y)) {
isScroll = true;
}
if (isScroll) {
mScrollLeft = (int) (getX() + x);
mScrollTop = (int) (getY() + y);
mScrollRight = (int) (getX() + getWidth() + x);
mScrollBottom = (int) (getY() + getHeight() + y);
//防止滑出父界面
if (mScrollLeft < 0 || mScrollRight > mParentWidth) {
mScrollLeft = (int) getX();
mScrollRight = (int) getX() + getWidth();
}
if (mScrollTop < 0 || mScrollBottom > mParentHeight) {
mScrollTop = (int) getY();
mScrollBottom = (int) getY() + getHeight();
}
layout(mScrollLeft, mScrollTop, mScrollRight, mScrollBottom);
hasScroll = true;
return true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
clickUpX = ev.getX();
clickUpY = ev.getY();
clickUpTime = System.currentTimeMillis();
float stanceX = clickUpX - clickDownX;
float stanceY = clickUpY - clickDownY;
if(stanceX < 0){
stanceX = stanceX * -1;
}
if(stanceY < 0){
stanceY = stanceY * -1;
}
if((clickUpTime - clickDownTime) < 150 && stanceX < 50 && stanceY < 50){
if(onClickListener!=null){
onClickListener.onClick();
}
}
if (isScroll) {
isScroll = false;
setPressed(false);//重置点击状态
if (isAdsorb) {
//判断是否开启吸附
//获取屏幕中间值
int mind = getScreenWidth() / 2;
//获取控件宽度的中间值
int viewWithMind = getWidth() / 2;
//手指抬起时 自动吸附到屏幕两边
if (mScrollLeft + viewWithMind > mind) {
mScrollRight = getScreenWidth();
mScrollLeft = getScreenWidth() - getWidth();
} else {
mScrollRight = getWidth();
mScrollLeft = 0;
}
layout(mScrollLeft, mScrollTop, mScrollRight, mScrollBottom);
}
return true;
}
break;
}
return super.onTouchEvent(ev);
}
- 核心介绍
这里大概说一下,主要是MOVE操作,记录下手指的坐标,通过layout()方法重新给View定义新的位置,这里是通过计算到View的左右上下边距实现,和矩阵的思路是一样的。当然也可以改成矩阵实现,也可以通过x,y具体坐标去实现。方法多种哈。
-
吸附
主要是手指抬起后(ACTION_UP、ACTION_CANCEL),先去判断控件处于什么位置,拿到屏幕的宽度,看是在屏幕的左半边还是右半边,然后进行动画移动。左边的坐标自然是0,右边是(屏幕宽度-控件的宽度),然后进行动画移动重新布局。 -
踩过的坑
一开始用的吸附是这样做的:
DragView.this.animate()
.setInterpolator(new BounceInterpolator())
.setDuration(0)
.x(0)
.y(realY)
.start();
相信做过的人都不陌生,但是这样做是存在问题的。在一些情况下,里面的布局刷新或者重新绘制,这个吸附就会出现位置错误的情况,后来才改成另一种实现方式。同样遇到这样的问题的可参考本文实现。
-
注意事项
这里要注意ACTION_UP、ACTION_CANCEL这两个操作事件,因为部分手机手指抬起后是走的ACTION_CANCEL,部分是ACTION_UP,所以最好都做一下逻辑处理。 -
具体调用方法
大致就是LayoutInflater实例化出ContentView,然后DolinDragView.setContentView()方法将控件放入。
我这里做的Demo里面是一个轮播图,所以还有别的逻辑,需要的朋友可以去我主页资源处下载完整Demo。