admin
2021-07-08 1764c1784a4cf1a6afd25fcf1a0eef6187a84218
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
package com.tejia.lijin.app.util;
 
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
 
import com.tejia.lijin.app.callBack.DragFloatActionInterface;
 
/**
 * 可拖拽的悬浮控件按钮
 * 直接xml布局里引用即可。
 * 要设置setOnClickListener点击事件,即可实现拖拽和点击功能。
 * 尺寸大小,样式及背景图片遵循ImageView即可。
 * <p>
 * 手指按下
 * 首先是处理手指按压下的事件,这里我们把拖拽标识符设置为false并记录当前点击的屏幕坐标。然后我们在移动事件处。
 * 手指移动
 * 这里我们把拖拽标识符设置为true,因为手指移动了。然后我们需要计算手指移动了多少偏移量
 * //计算手指移动了多少
 * int dx=rawX-lastX;
 * int dy=rawY-lastY;
 * <p>
 * 而后的两行代码表示控件需要移动的具体距离,后面有一个简单的边缘检测计算。最终通过调用setX以及setY方法实现控件的移动。
 * 手指松开
 * 这里如果是拖拽动作我们才需要处理自己的逻辑否则直接跳过即可。在这里我们首先恢复了按钮的按压效果,在源代码中找到setPressed(boolean)方法,
 * 这是处理按钮点击效果用的,在这里当手指松开后我们需要恢复按钮原来的效果。然后在判断控件需要往哪边吸附,吸附的过程就是做属性动画而已,原理还是不断的改变setX方法让按钮靠边移动
 * <p>
 * 作者:Ggx的代码之旅
 * 链接:https://www.jianshu.com/p/4f55bcbc1b83
 * 来源:简书
 * 简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
 */
@SuppressLint("AppCompatCustomView")
public class DragFloatActionButton extends ImageView {
    private int parentHeigh;//起始高度
    private int parentWidth;//起始宽度
    private int lastX;//最后x位置
    private int lastY;//最后y位置
 
    private boolean isDrag;//是否拖拽
 
    public DragFloatActionButton(Context context) {
        super(context);
        try {
            setisStateReturn((DragFloatActionInterface) context);
        } catch (ClassCastException e) {
 
        }
    }
 
    public DragFloatActionButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        try {
            setisStateReturn((DragFloatActionInterface) context);
        } catch (ClassCastException e) {
 
        }
    }
 
    public DragFloatActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        try {//传入 回调接口
            setisStateReturn((DragFloatActionInterface) context);
        } catch (ClassCastException e) {
 
        }
    }
 
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int rawX = (int) event.getRawX();//相对屏幕的X位置
        int rawY = (int) event.getRawY();//相对屏幕的Y位置
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN://按下
                //设置按压效果
                setPressed(true);
                isDrag = false;//未移动
                getParent().requestDisallowInterceptTouchEvent(true);
                lastX = rawX;
                lastY = rawY;
                ViewGroup parent;
                if (getParent() != null) {
                    parent = (ViewGroup) getParent();
                    parentHeigh = parent.getHeight();
                    parentWidth = parent.getWidth();
                }
                break;
            case MotionEvent.ACTION_MOVE://移动
                if (parentHeigh <= 0 || parentWidth == 0) {
                    isDrag = false;
                    break;
                } else {
                    isDrag = true;//移动
                }
                //计算手指移动了多少
                int dx = rawX - lastX;
                int dy = rawY - lastY;
                //这里修复一些华为手机无法触发点击事件
                int distance = (int) Math.sqrt(dx * dx + dy * dy);
                if (distance == 0) {
                    isDrag = false;
                    break;
                }
                //getX()和getY()获得的永远是view的触摸位置坐标
                float x = getX() + dx;
                float y = getY() + dy;
                setdelete(x, y);
                //检测是否到达边缘 左上右下
                x = x < 0 ? 0 : x > parentWidth - getWidth() ? parentWidth - getWidth() : x;
                y = getY() < 0 ? 0 : y + getHeight() > parentHeigh ? parentHeigh - getHeight() : y;
//                当移动位置大于了 边缘位置 则不计算位置
                if (x > parentWidth - getWidth() || (y + getHeight()) > parentHeigh || ((y + getHeight()) - getHeight()) <= 0) {
                    //((y + getHeight()) - getHeight()) 去掉标题栏的高度 去掉移动状态
                    if (((y + getHeight()) - getHeight()) <= 0 && isDrag) {
                        setX(x);
                        setY(0);//重新设置 图片的高度为0
                        lastX = rawX;
                        lastY = rawY;
                        isDrag = false;
                    }
 
                } else {
                    setX(x);
                    setY(y);
                    lastX = rawX;
                    lastY = rawY;
                }
//                Log.e("eee", "isDrag=" + isDrag + "getX=" + getX() + ";getY=" + getY() + ";parentWidth=" + parentWidth);
                if (aReturn != null) {
                    aReturn.isDrag(true, true);
                }
                break;
            case MotionEvent.ACTION_CANCEL://事件被上层拦截 时触发。
//                Log.e("eee", "onTouchEvent: CANCEL");
                ACTION_UP(rawX);
                break;
            case MotionEvent.ACTION_POINTER_UP:
//                Log.e("eee", "onTouchEvent: ACTION_POINTER_UP");
                break;
            case MotionEvent.ACTION_UP://放开
//                Log.e("eee", "onTouchEvent: 放开");
                ACTION_UP(rawX);
                break;
        }
        //如果是拖拽则消s耗事件,否则正常传递即可。
        return !isNotDrag() || super.onTouchEvent(event);
    }
 
    /**
     * 手指放开
     */
    private void ACTION_UP(int rawX) {
        if (!isNotDrag()) {
            //恢复按压效果
            setPressed(false);
            //Log.i("getX="+getX()+";screenWidthHalf="+screenWidthHalf);
            if (rawX >= parentWidth / 2) {
                //靠右吸附
                animate().setInterpolator(new DecelerateInterpolator())
                        .setDuration(100)
                        .xBy(parentWidth - getWidth() - getX())
                        .start();
            } else {
                //靠左吸附
                ObjectAnimator oa = ObjectAnimator.ofFloat(this, "x", getX(), 0);
                oa.setInterpolator(new DecelerateInterpolator());
                oa.setDuration(100);
                oa.start();
            }
        }
        //传入 没有移动状态 并且判断是否移动到指定区域(移动到指定区域不隐藏,否则隐藏)
        if (aReturn != null) {
            //未移动到指定区域
            if (!isImgWidthboolean && !isImgHeighboolean) {
                aReturn.isDrag(false, false);
            } else if (isImgWidthboolean && isImgHeighboolean) {
                //移动到指定区域
                aReturn.isDrag(false, true);
            } else {
                aReturn.isDrag(false, false);
            }
            //传入 是否移动到区域(宽高 距离判断)
            aReturn.isReturn(isImgWidthboolean, isImgHeighboolean);
        }
    }
 
    private Boolean isNotDrag() {
        return !isDrag && (getX() == 0 || (getX() == parentWidth - getWidth()));
    }
 
    /**
     * 主界面传入进来 指定区域控件的宽高
     *
     * @param imgwidth
     * @param imgHeigh
     */
    public void setWidthHeight(int imgwidth, int imgHeigh) {
        isImgWidth = imgwidth;
        isImgHeigh = imgHeigh;
    }
 
    /**
     * 主界面 右下角 删除控件的宽高
     */
    private int isImgWidth;//
    private int isImgHeigh;
    /**
     * 移动到指定位置(宽高距离判断)
     */
    private boolean isImgWidthboolean;
    private boolean isImgHeighboolean;
 
    /**
     * 删除 拖动控件
     */
    private void setdelete(float x, float y) {
        //宽度大于 右下角控件的宽度
        if (x > (parentWidth - getWidth() - isImgWidth)) {
            isImgWidthboolean = true;
        } else {
            isImgWidthboolean = false;
        }
        //宽度大于 右下角控件的宽度
        if ((y + getHeight()) > (parentHeigh - isImgHeigh)) {
            isImgHeighboolean = true;
        } else {
            isImgHeighboolean = false;
        }
    }
 
    private DragFloatActionInterface aReturn;
 
    /**
     * 接口绑定
     *
     * @param stateReturn 拖动控件 位置(是否进入指定区域), 判断移动状态
     */
    private void setisStateReturn(DragFloatActionInterface stateReturn) {
        aReturn = stateReturn;
    }
 
}