-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
BasePopupView.java
398 lines (353 loc) · 13.2 KB
/
BasePopupView.java
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
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
package com.lxj.xpopup.core;
import android.content.Context;
import android.graphics.Rect;
import android.opengl.ETC1;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.FrameLayout;
import com.lxj.xpopup.XPopup;
import com.lxj.xpopup.animator.PopupAnimator;
import com.lxj.xpopup.animator.ScaleAlphaAnimator;
import com.lxj.xpopup.animator.ScrollScaleAnimator;
import com.lxj.xpopup.animator.ShadowBgAnimator;
import com.lxj.xpopup.animator.TranslateAlphaAnimator;
import com.lxj.xpopup.animator.TranslateAnimator;
import com.lxj.xpopup.enums.PopupStatus;
import com.lxj.xpopup.interfaces.PopupInterface;
import com.lxj.xpopup.util.KeyboardUtils;
import com.lxj.xpopup.util.XPopupUtils;
import java.util.ArrayList;
import static com.lxj.xpopup.enums.PopupAnimation.ScaleAlphaFromCenter;
import static com.lxj.xpopup.enums.PopupAnimation.ScrollAlphaFromLeftTop;
import static com.lxj.xpopup.enums.PopupAnimation.TranslateFromBottom;
/**
* Description: 弹窗基类
* Create by lxj, at 2018/12/7
*/
public abstract class BasePopupView extends FrameLayout implements PopupInterface {
public PopupInfo popupInfo;
protected PopupAnimator popupContentAnimator;
protected ShadowBgAnimator shadowBgAnimator;
private int touchSlop;
public PopupStatus popupStatus = PopupStatus.Dismiss;
public BasePopupView(@NonNull Context context) {
super(context);
touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
shadowBgAnimator = new ShadowBgAnimator(this);
// 添加Popup窗体内容View
View contentView = LayoutInflater.from(context).inflate(getPopupLayoutId(), this, false);
// 事先隐藏,等测量完毕恢复。避免View影子跳动现象
contentView.setAlpha(0);
addView(contentView);
}
public BasePopupView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public BasePopupView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private void focusAndProcessBackPress(){
// 处理返回按键
setFocusableInTouchMode(true);
requestFocus();
// 此处焦点可能被内容的EditText抢走,也需要给EditText也设置返回按下监听
setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
if (popupInfo.isDismissOnBackPressed)
dismiss();
return true;
}
return false;
}
});
//let all EditText can process back pressed.
ArrayList<EditText> list = new ArrayList<>();
XPopupUtils.findAllEditText(list, (ViewGroup) getPopupContentView());
for (int i = 0; i < list.size(); i++) {
final View et = list.get(i);
if(i==0){
et.setFocusable(true);
et.setFocusableInTouchMode(true);
et.requestFocus();
if(popupInfo.autoOpenSoftInput){
postDelayed(new Runnable() {
@Override
public void run() {
KeyboardUtils.showSoftInput(et);
}
}, getAnimationDuration()+10);
}
}
et.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
if (popupInfo.isDismissOnBackPressed)
dismiss();
return true;
}
return false;
}
});
}
}
Runnable afterShow,afterDismiss;
/**
* 执行初始化
*
* @param afterShow
*/
public void init(Runnable afterShow, Runnable afterDismiss) {
if (popupStatus != PopupStatus.Dismiss) return;
popupStatus = PopupStatus.Showing;
this.afterShow = afterShow;
this.afterDismiss = afterDismiss;
//1. 初始化Popup
onCreate();
initPopupContent();
post(new Runnable() {
@Override
public void run() {
// 如果有导航栏,则不能覆盖导航栏,
if (XPopupUtils.isNavBarVisible(getContext())) {
FrameLayout.LayoutParams params = (LayoutParams) getLayoutParams();
params.bottomMargin = XPopupUtils.getNavBarHeight();
setLayoutParams(params);
}
getPopupContentView().setAlpha(1f);
//2. 收集动画执行器
// 优先使用自定义的动画器
if (popupInfo.customAnimator != null) {
popupContentAnimator = popupInfo.customAnimator;
popupContentAnimator.targetView = getPopupContentView();
} else {
// 根据PopupInfo的popupAnimation字段来生成对应的动画执行器,如果popupAnimation字段为null,则返回null
popupContentAnimator = genAnimatorByPopupType();
if (popupContentAnimator == null) {
// 使用默认的animator
popupContentAnimator = getPopupAnimator();
}
}
//3. 初始化动画执行器
shadowBgAnimator.initAnimator();
if (popupContentAnimator != null) {
popupContentAnimator.initAnimator();
}
//4. 执行动画
doShowAnimation();
focusAndProcessBackPress();
// call xpopup init.
doAfterShow();
}
});
}
protected void doAfterShow(){
postDelayed(new Runnable() {
@Override
public void run() {
popupStatus = PopupStatus.Show;
onShow();
afterShow.run();
}
}, getAnimationDuration());
}
/**
* 根据PopupInfo的popupAnimation字段来生成对应的内置的动画执行器
*/
protected PopupAnimator genAnimatorByPopupType() {
if (popupInfo == null || popupInfo.popupAnimation == null) return null;
switch (popupInfo.popupAnimation) {
case ScaleAlphaFromCenter:
case ScaleAlphaFromLeftTop:
case ScaleAlphaFromRightTop:
case ScaleAlphaFromLeftBottom:
case ScaleAlphaFromRightBottom:
return new ScaleAlphaAnimator(getPopupContentView(), popupInfo.popupAnimation);
case TranslateAlphaFromLeft:
case TranslateAlphaFromTop:
case TranslateAlphaFromRight:
case TranslateAlphaFromBottom:
return new TranslateAlphaAnimator(getPopupContentView(), popupInfo.popupAnimation);
case TranslateFromLeft:
case TranslateFromTop:
case TranslateFromRight:
case TranslateFromBottom:
return new TranslateAnimator(getPopupContentView(), popupInfo.popupAnimation);
case ScrollAlphaFromLeft:
case ScrollAlphaFromLeftTop:
case ScrollAlphaFromTop:
case ScrollAlphaFromRightTop:
case ScrollAlphaFromRight:
case ScrollAlphaFromRightBottom:
case ScrollAlphaFromBottom:
case ScrollAlphaFromLeftBottom:
return new ScrollScaleAnimator(getPopupContentView(), popupInfo.popupAnimation);
}
return null;
}
protected abstract int getPopupLayoutId();
/**
* 如果你自己继承BasePopupView来做,这个不用实现
* @return
*/
protected int getImplLayoutId() {
return -1;
}
/**
* 获取PopupAnimator,每种类型的PopupView可以选择返回一个动画器,
* 父类默认实现是根据popupType字段返回一个默认最佳的动画器
*
* @return
*/
protected PopupAnimator getPopupAnimator() {
if (popupInfo == null || popupInfo.popupType == null) return null;
switch (popupInfo.popupType) {
case Center:
return new ScaleAlphaAnimator(getPopupContentView(), ScaleAlphaFromCenter);
case Bottom:
return new TranslateAnimator(getPopupContentView(), TranslateFromBottom);
case AttachView:
return new ScrollScaleAnimator(getPopupContentView(), ScrollAlphaFromLeftTop);
}
return null;
}
/**
* 请使用onCreate,主要给弹窗内部用,未来会移除。
*/
@Deprecated
protected void initPopupContent() { }
/**
* do init.
*/
protected void onCreate(){
}
/**
* 执行显示动画:动画由2部分组成,一个是背景渐变动画,一个是Content的动画;
* 背景动画由父类实现,Content由子类实现
*/
@Override
public void doShowAnimation() {
if (popupInfo.hasShadowBg) {
shadowBgAnimator.animateShow();
}
if (popupContentAnimator != null)
popupContentAnimator.animateShow();
}
/**
* 执行消失动画:动画由2部分组成,一个是背景渐变动画,一个是Content的动画;
* 背景动画由父类实现,Content由子类实现
*/
@Override
public void doDismissAnimation() {
if (popupInfo.hasShadowBg) {
shadowBgAnimator.animateDismiss();
}
if (popupContentAnimator != null)
popupContentAnimator.animateDismiss();
}
/**
* 获取内容View,本质上PopupView显示的内容都在这个View内部。
* 而且我们对PopupView执行的动画,也是对它执行的动画
*
* @return
*/
@Override
public View getPopupContentView() {
return getChildAt(0);
}
public View getPopupImplView() {
return ((ViewGroup) getPopupContentView()).getChildAt(0);
}
@Override
public int getAnimationDuration() {
return XPopup.getAnimationDuration();
}
protected int getMaxWidth() {
return 0;
}
protected int getMaxHeight() {
return popupInfo.maxHeight;
}
/**
* 消失
*/
public void dismiss() {
if (popupStatus == PopupStatus.Dismissing) return;
popupStatus = PopupStatus.Dismissing;
doDismissAnimation();
doAfterDismiss();
}
protected void doAfterDismiss(){
KeyboardUtils.hideSoftInput(this);
postDelayed(new Runnable() {
@Override
public void run() {
popupStatus = PopupStatus.Dismiss;
onDismiss();
if(afterDismiss!=null)afterDismiss.run();
if(dismissWithRunnable!=null)dismissWithRunnable.run();
}
}, getAnimationDuration());
}
Runnable dismissWithRunnable;
protected void dismissWith(Runnable runnable){
this.dismissWithRunnable = runnable;
dismiss();
}
public boolean isShow(){
return popupStatus==PopupStatus.Show;
}
public boolean isDismiss(){
return popupStatus==PopupStatus.Dismiss;
}
/**
* 消失动画执行完毕后执行
*/
protected void onDismiss(){}
/**
* 显示动画执行完毕后执行
*/
protected void onShow(){}
private float x, y;
private long downTime;
@Override
public boolean onTouchEvent(MotionEvent event) {
// 如果自己接触到了点击,并且不在PopupContentView范围内点击,则进行判断是否是点击事件
// 如果是,则dismiss
Rect rect = new Rect();
getPopupContentView().getGlobalVisibleRect(rect);
if (!XPopupUtils.isInRect(event.getX(), event.getY(), rect)) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
x = event.getX();
y = event.getY();
downTime = System.currentTimeMillis();
break;
case MotionEvent.ACTION_UP:
float dx = event.getX() - x;
float dy = event.getY() - y;
float distance = (float) Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
if (distance < touchSlop && (System.currentTimeMillis() - downTime) < 350) {
if (popupInfo.isDismissOnTouchOutside)
dismiss();
}
x = 0;
y = 0;
downTime = 0;
break;
}
}
return true;
}
}