BottomDialog.java 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. package com.kongzue.dialogx.dialogs;
  2. import android.animation.Animator;
  3. import android.animation.ObjectAnimator;
  4. import android.view.View;
  5. import android.view.ViewGroup;
  6. import android.view.ViewTreeObserver;
  7. import android.view.animation.AccelerateInterpolator;
  8. import android.view.animation.Animation;
  9. import android.view.animation.AnimationUtils;
  10. import android.view.animation.DecelerateInterpolator;
  11. import android.widget.ImageView;
  12. import android.widget.LinearLayout;
  13. import android.widget.RelativeLayout;
  14. import android.widget.ScrollView;
  15. import android.widget.TextView;
  16. import androidx.annotation.ColorInt;
  17. import com.kongzue.dialogx.DialogX;
  18. import com.kongzue.dialogx.R;
  19. import com.kongzue.dialogx.impl.AnimatorListenerEndCallBack;
  20. import com.kongzue.dialogx.interfaces.BaseDialog;
  21. import com.kongzue.dialogx.interfaces.DialogConvertViewInterface;
  22. import com.kongzue.dialogx.interfaces.DialogLifecycleCallback;
  23. import com.kongzue.dialogx.interfaces.DialogXStyle;
  24. import com.kongzue.dialogx.interfaces.OnBackPressedListener;
  25. import com.kongzue.dialogx.interfaces.OnBindView;
  26. import com.kongzue.dialogx.interfaces.OnDialogButtonClickListener;
  27. import com.kongzue.dialogx.interfaces.OnInputDialogButtonClickListener;
  28. import com.kongzue.dialogx.interfaces.OnMenuItemClickListener;
  29. import com.kongzue.dialogx.style.MaterialStyle;
  30. import com.kongzue.dialogx.util.BottomDialogTouchEventInterceptor;
  31. import com.kongzue.dialogx.util.TextInfo;
  32. import com.kongzue.dialogx.util.views.BlurView;
  33. import com.kongzue.dialogx.util.views.DialogXBaseRelativeLayout;
  34. import com.kongzue.dialogx.util.views.MaxRelativeLayout;
  35. /**
  36. * @author: Kongzue
  37. * @github: https://github.com/kongzue/
  38. * @homepage: http://kongzue.com/
  39. * @mail: myzcxhh@live.cn
  40. * @createTime: 2020/10/6 15:17
  41. */
  42. public class BottomDialog extends BaseDialog {
  43. protected OnBindView<BottomDialog> onBindView;
  44. protected CharSequence title;
  45. protected CharSequence message;
  46. protected CharSequence cancelText = "取消";
  47. protected boolean allowInterceptTouch = true;
  48. protected OnDialogButtonClickListener<BottomDialog> cancelButtonClickListener;
  49. protected TextInfo titleTextInfo;
  50. protected TextInfo messageTextInfo;
  51. protected TextInfo cancelTextInfo = new TextInfo().setBold(true);
  52. /**
  53. * 此值用于,当禁用滑动时(style.overrideBottomDialogRes.touchSlide = false时)的最大显示高度。
  54. * 0:不限制,最大显示到屏幕可用高度。
  55. */
  56. protected float bottomDialogMaxHeight = 0.6f;
  57. protected DialogLifecycleCallback<BottomDialog> dialogLifecycleCallback;
  58. protected BottomDialog me = this;
  59. protected BottomDialog() {
  60. super();
  61. }
  62. private View dialogView;
  63. public static BottomDialog build() {
  64. return new BottomDialog();
  65. }
  66. public BottomDialog(CharSequence title, CharSequence message) {
  67. this.title = title;
  68. this.message = message;
  69. }
  70. public static BottomDialog show(CharSequence title, CharSequence message) {
  71. BottomDialog bottomDialog = new BottomDialog(title, message);
  72. bottomDialog.show();
  73. return bottomDialog;
  74. }
  75. public BottomDialog(CharSequence title, CharSequence message, OnBindView<BottomDialog> onBindView) {
  76. this.title = title;
  77. this.message = message;
  78. this.onBindView = onBindView;
  79. }
  80. public static BottomDialog show(CharSequence title, CharSequence message, OnBindView<BottomDialog> onBindView) {
  81. BottomDialog bottomDialog = new BottomDialog(title, message, onBindView);
  82. bottomDialog.show();
  83. return bottomDialog;
  84. }
  85. public BottomDialog(CharSequence title, OnBindView<BottomDialog> onBindView) {
  86. this.title = title;
  87. this.onBindView = onBindView;
  88. }
  89. public static BottomDialog show(CharSequence title, OnBindView<BottomDialog> onBindView) {
  90. BottomDialog bottomDialog = new BottomDialog(title, onBindView);
  91. bottomDialog.show();
  92. return bottomDialog;
  93. }
  94. public BottomDialog(OnBindView<BottomDialog> onBindView) {
  95. this.onBindView = onBindView;
  96. }
  97. public static BottomDialog show(OnBindView<BottomDialog> onBindView) {
  98. BottomDialog bottomDialog = new BottomDialog(onBindView);
  99. bottomDialog.show();
  100. return bottomDialog;
  101. }
  102. public void show() {
  103. int layoutId = isLightTheme() ? R.layout.layout_dialogx_bottom_material : R.layout.layout_dialogx_bottom_material_dark;
  104. if (style.overrideBottomDialogRes() != null) {
  105. layoutId = style.overrideBottomDialogRes().overrideDialogLayout(isLightTheme());
  106. }
  107. dialogView = createView(layoutId);
  108. dialogImpl = new DialogImpl(dialogView);
  109. show(dialogView);
  110. }
  111. protected DialogImpl dialogImpl;
  112. public class DialogImpl implements DialogConvertViewInterface {
  113. private BottomDialogTouchEventInterceptor bottomDialogTouchEventInterceptor;
  114. public DialogXBaseRelativeLayout boxRoot;
  115. public RelativeLayout boxBkg;
  116. public MaxRelativeLayout bkg;
  117. public ViewGroup boxBody;
  118. public ImageView imgTab;
  119. public TextView txtDialogTitle;
  120. public ScrollView scrollView;
  121. public LinearLayout boxContent;
  122. public TextView txtDialogTip;
  123. public View imgSplit;
  124. public RelativeLayout boxList;
  125. public RelativeLayout boxCustom;
  126. public BlurView blurView;
  127. public ViewGroup boxCancel;
  128. public TextView btnCancel;
  129. public BlurView cancelBlurView;
  130. public DialogImpl(View convertView) {
  131. boxRoot = convertView.findViewById(R.id.box_root);
  132. boxBkg = convertView.findViewById(R.id.box_bkg);
  133. bkg = convertView.findViewById(R.id.bkg);
  134. boxBody = convertView.findViewWithTag("body");
  135. imgTab = convertView.findViewById(R.id.img_tab);
  136. txtDialogTitle = convertView.findViewById(R.id.txt_dialog_title);
  137. scrollView = convertView.findViewById(R.id.scrollView);
  138. boxContent = convertView.findViewById(R.id.box_content);
  139. txtDialogTip = convertView.findViewById(R.id.txt_dialog_tip);
  140. imgSplit = convertView.findViewWithTag("split");
  141. boxList = convertView.findViewById(R.id.box_list);
  142. boxCustom = convertView.findViewById(R.id.box_custom);
  143. blurView = convertView.findViewById(R.id.blurView);
  144. boxCancel = convertView.findViewWithTag("cancelBox");
  145. btnCancel = convertView.findViewWithTag("cancel");
  146. init();
  147. refreshView();
  148. }
  149. /**
  150. * 此值记录了BottomDialog启动后的位置
  151. * ·当内容高度大于屏幕安全区高度时,BottomDialog会以全屏方式启动,但一开始只会展开到 0.8×屏幕高度,
  152. * 此时可以再次上划查看全部内容。
  153. * ·当内容高度小于屏幕安全区高度时,BottomDialog会以内容高度启动。
  154. * <p>
  155. * 记录这个值的目的是,当用户向下滑动时,判断情况该回到这个位置还是关闭对话框,
  156. * 并阻止当内容高度已经完全显示时的继续向上滑动操作。
  157. */
  158. public float bkgEnterAimY = -1;
  159. @Override
  160. public void init() {
  161. boxRoot.setOnLifecycleCallBack(new DialogXBaseRelativeLayout.OnLifecycleCallBack() {
  162. @Override
  163. public void onShow() {
  164. isShow = true;
  165. boxRoot.setAlpha(0f);
  166. boxContent.getViewTreeObserver().addOnGlobalLayoutListener(onContentViewLayoutChangeListener);
  167. getDialogLifecycleCallback().onShow(me);
  168. onDialogInit(dialogImpl);
  169. if (onBindView != null) onBindView.onBind(me, onBindView.getCustomView());
  170. if (style.messageDialogBlurSettings() != null && style.messageDialogBlurSettings().blurBackground() && boxBody != null && boxCancel != null) {
  171. int blurFrontColor = getResources().getColor(style.messageDialogBlurSettings().blurForwardColorRes(isLightTheme()));
  172. blurView = new BlurView(bkg.getContext(), null);
  173. RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(bkg.getWidth(), bkg.getHeight());
  174. blurView.setOverlayColor(backgroundColor == -1 ? blurFrontColor : backgroundColor);
  175. blurView.setTag("blurView");
  176. blurView.setRadiusPx(style.messageDialogBlurSettings().blurBackgroundRoundRadiusPx());
  177. boxBody.addView(blurView, 0, params);
  178. cancelBlurView = new BlurView(boxCancel.getContext(), null);
  179. RelativeLayout.LayoutParams cancelButtonLp = new RelativeLayout.LayoutParams(boxCancel.getWidth(), boxCancel.getHeight());
  180. cancelBlurView.setOverlayColor(backgroundColor == -1 ? blurFrontColor : backgroundColor);
  181. cancelBlurView.setTag("blurView");
  182. cancelBlurView.setUseBlur(false);
  183. cancelBlurView.setRadiusPx(style.messageDialogBlurSettings().blurBackgroundRoundRadiusPx());
  184. boxCancel.addView(cancelBlurView, 0, cancelButtonLp);
  185. }
  186. }
  187. @Override
  188. public void onDismiss() {
  189. isShow = false;
  190. getDialogLifecycleCallback().onDismiss(me);
  191. }
  192. });
  193. if (btnCancel != null) {
  194. btnCancel.setOnClickListener(new View.OnClickListener() {
  195. @Override
  196. public void onClick(View v) {
  197. if (cancelButtonClickListener != null) {
  198. if (!cancelButtonClickListener.onClick(me, v)) {
  199. dismiss();
  200. }
  201. } else {
  202. dismiss();
  203. }
  204. }
  205. });
  206. }
  207. if (imgSplit != null) {
  208. int dividerRes = style.overrideBottomDialogRes().overrideMenuDividerDrawableRes(isLightTheme());
  209. int dividerHeight = style.overrideBottomDialogRes().overrideMenuDividerHeight(isLightTheme());
  210. if (dividerRes != 0) imgSplit.setBackgroundResource(dividerRes);
  211. if (dividerHeight != 0) {
  212. ViewGroup.LayoutParams lp = imgSplit.getLayoutParams();
  213. lp.height = dividerHeight;
  214. imgSplit.setLayoutParams(lp);
  215. }
  216. }
  217. boxRoot.setOnBackPressedListener(new OnBackPressedListener() {
  218. @Override
  219. public boolean onBackPressed() {
  220. if (onBackPressedListener != null && onBackPressedListener.onBackPressed()) {
  221. dismiss();
  222. return false;
  223. }
  224. if (cancelable) {
  225. dismiss();
  226. }
  227. return false;
  228. }
  229. });
  230. bottomDialogTouchEventInterceptor = new BottomDialogTouchEventInterceptor(me, dialogImpl);
  231. boxRoot.post(new Runnable() {
  232. @Override
  233. public void run() {
  234. boxRoot.animate().setDuration(300).alpha(1f).setInterpolator(new DecelerateInterpolator()).setDuration(100).setListener(null);
  235. Animation enterAnim = AnimationUtils.loadAnimation(getContext(), R.anim.anim_dialogx_bottom_enter);
  236. enterAnim.setInterpolator(new DecelerateInterpolator(2f));
  237. bkg.startAnimation(enterAnim);
  238. bkg.setY(bkgEnterAimY);
  239. }
  240. });
  241. }
  242. private ViewTreeObserver.OnGlobalLayoutListener onContentViewLayoutChangeListener = new ViewTreeObserver.OnGlobalLayoutListener() {
  243. @Override
  244. public void onGlobalLayout() {
  245. if (boxContent != null) {
  246. if (bkg.isChildScrollViewCanScroll() && bottomDialogMaxHeight != 0) {
  247. if (bottomDialogMaxHeight <= 1) {
  248. bkgEnterAimY = boxBkg.getHeight() - bkg.getHeight() * bottomDialogMaxHeight;
  249. } else {
  250. bkgEnterAimY = boxBkg.getHeight() - bottomDialogMaxHeight;
  251. }
  252. } else {
  253. bkgEnterAimY = boxBkg.getHeight() - bkg.getHeight();
  254. }
  255. }
  256. }
  257. };
  258. @Override
  259. public void refreshView() {
  260. if (backgroundColor != -1) {
  261. tintColor(bkg, backgroundColor);
  262. if (blurView != null && cancelBlurView != null) {
  263. blurView.setOverlayColor(backgroundColor);
  264. cancelBlurView.setOverlayColor(backgroundColor);
  265. }
  266. }
  267. txtDialogTitle.getPaint().setFakeBoldText(true);
  268. showText(txtDialogTitle, title);
  269. showText(txtDialogTip, message);
  270. useTextInfo(txtDialogTitle, titleTextInfo);
  271. useTextInfo(txtDialogTip, messageTextInfo);
  272. useTextInfo(btnCancel, cancelTextInfo);
  273. if (cancelable) {
  274. boxRoot.setOnClickListener(new View.OnClickListener() {
  275. @Override
  276. public void onClick(View v) {
  277. doDismiss(v);
  278. }
  279. });
  280. } else {
  281. boxRoot.setOnClickListener(null);
  282. }
  283. if (onBindView != null) {
  284. if (onBindView.getCustomView() != null) {
  285. if (onBindView.getCustomView().isAttachedToWindow()) {
  286. boxCustom.removeView(onBindView.getCustomView());
  287. }
  288. ViewGroup.LayoutParams lp = boxCustom.getLayoutParams();
  289. if (lp == null) {
  290. lp = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
  291. }
  292. boxCustom.addView(onBindView.getCustomView(), lp);
  293. }
  294. }
  295. if (isAllowInterceptTouch() && cancelable) {
  296. if (imgTab != null) imgTab.setVisibility(View.VISIBLE);
  297. } else {
  298. if (imgTab != null) imgTab.setVisibility(View.GONE);
  299. }
  300. bottomDialogTouchEventInterceptor.refresh(me, this);
  301. if (imgSplit != null) {
  302. if (txtDialogTitle.getVisibility() == View.VISIBLE || txtDialogTip.getVisibility() == View.VISIBLE) {
  303. imgSplit.setVisibility(View.VISIBLE);
  304. } else {
  305. imgSplit.setVisibility(View.GONE);
  306. }
  307. }
  308. if (boxCancel != null) {
  309. if (isNull(cancelText)) {
  310. boxCancel.setVisibility(View.GONE);
  311. } else {
  312. showText(btnCancel, cancelText);
  313. boxCancel.setVisibility(View.VISIBLE);
  314. }
  315. }
  316. }
  317. @Override
  318. public void doDismiss(View v) {
  319. if (v != null) v.setEnabled(false);
  320. if (boxContent != null)
  321. boxContent.getViewTreeObserver().removeOnGlobalLayoutListener(onContentViewLayoutChangeListener);
  322. ObjectAnimator exitAnim = ObjectAnimator.ofFloat(bkg, "y", bkg.getY(), boxBkg.getHeight());
  323. exitAnim.setDuration(300);
  324. exitAnim.start();
  325. boxRoot.animate().setDuration(300).alpha(0f).setInterpolator(new AccelerateInterpolator()).setDuration(exitAnim.getDuration()).setListener(new AnimatorListenerEndCallBack() {
  326. @Override
  327. public void onAnimationEnd(Animator animation) {
  328. dismiss(dialogView);
  329. }
  330. });
  331. }
  332. public void preDismiss() {
  333. if (cancelable) {
  334. doDismiss(boxRoot);
  335. } else {
  336. ObjectAnimator enterAnim = ObjectAnimator.ofFloat(bkg, "y", bkg.getY(), bkgEnterAimY);
  337. enterAnim.setDuration(300);
  338. enterAnim.start();
  339. }
  340. }
  341. }
  342. protected void onDialogInit(DialogImpl dialog) {
  343. }
  344. public void refreshUI() {
  345. if (dialogImpl == null) return;
  346. dialogImpl.refreshView();
  347. }
  348. public void dismiss() {
  349. if (dialogImpl == null) return;
  350. dialogImpl.doDismiss(null);
  351. }
  352. public DialogLifecycleCallback<BottomDialog> getDialogLifecycleCallback() {
  353. return dialogLifecycleCallback == null ? new DialogLifecycleCallback<BottomDialog>() {
  354. } : dialogLifecycleCallback;
  355. }
  356. public BottomDialog setDialogLifecycleCallback(DialogLifecycleCallback<BottomDialog> dialogLifecycleCallback) {
  357. this.dialogLifecycleCallback = dialogLifecycleCallback;
  358. return this;
  359. }
  360. public OnBackPressedListener getOnBackPressedListener() {
  361. return onBackPressedListener;
  362. }
  363. public BottomDialog setOnBackPressedListener(OnBackPressedListener onBackPressedListener) {
  364. this.onBackPressedListener = onBackPressedListener;
  365. refreshUI();
  366. return this;
  367. }
  368. public BottomDialog setStyle(DialogXStyle style) {
  369. this.style = style;
  370. return this;
  371. }
  372. public BottomDialog setTheme(DialogX.THEME theme) {
  373. this.theme = theme;
  374. return this;
  375. }
  376. public boolean isCancelable() {
  377. return cancelable;
  378. }
  379. public BottomDialog setCancelable(boolean cancelable) {
  380. this.cancelable = cancelable;
  381. refreshUI();
  382. return this;
  383. }
  384. public DialogImpl getDialogImpl() {
  385. return dialogImpl;
  386. }
  387. public CharSequence getTitle() {
  388. return title;
  389. }
  390. public BottomDialog setTitle(CharSequence title) {
  391. this.title = title;
  392. refreshUI();
  393. return this;
  394. }
  395. public CharSequence getMessage() {
  396. return message;
  397. }
  398. public BottomDialog setMessage(CharSequence message) {
  399. this.message = message;
  400. refreshUI();
  401. return this;
  402. }
  403. public CharSequence getCancelButton() {
  404. return cancelText;
  405. }
  406. public BottomDialog setCancelButton(CharSequence cancelText) {
  407. this.cancelText = cancelText;
  408. refreshUI();
  409. return this;
  410. }
  411. public BottomDialog setCancelButton(OnDialogButtonClickListener<BottomDialog> cancelButtonClickListener) {
  412. this.cancelButtonClickListener = cancelButtonClickListener;
  413. return this;
  414. }
  415. public BottomDialog setCancelButton(CharSequence cancelText, OnDialogButtonClickListener<BottomDialog> cancelButtonClickListener) {
  416. this.cancelText = cancelText;
  417. this.cancelButtonClickListener = cancelButtonClickListener;
  418. return this;
  419. }
  420. public BottomDialog setCustomView(OnBindView<BottomDialog> onBindView) {
  421. this.onBindView = onBindView;
  422. refreshUI();
  423. return this;
  424. }
  425. public View getCustomView() {
  426. if (onBindView == null) return null;
  427. return onBindView.getCustomView();
  428. }
  429. public BottomDialog removeCustomView() {
  430. this.onBindView.clean();
  431. refreshUI();
  432. return this;
  433. }
  434. public boolean isAllowInterceptTouch() {
  435. if (style.overrideBottomDialogRes() == null) {
  436. return false;
  437. } else {
  438. return allowInterceptTouch && style.overrideBottomDialogRes().touchSlide();
  439. }
  440. }
  441. public BottomDialog setAllowInterceptTouch(boolean allowInterceptTouch) {
  442. this.allowInterceptTouch = allowInterceptTouch;
  443. refreshUI();
  444. return this;
  445. }
  446. public OnDialogButtonClickListener<BottomDialog> getCancelButtonClickListener() {
  447. return cancelButtonClickListener;
  448. }
  449. public BottomDialog setCancelButtonClickListener(OnDialogButtonClickListener<BottomDialog> cancelButtonClickListener) {
  450. this.cancelButtonClickListener = cancelButtonClickListener;
  451. refreshUI();
  452. return this;
  453. }
  454. public TextInfo getTitleTextInfo() {
  455. return titleTextInfo;
  456. }
  457. public BottomDialog setTitleTextInfo(TextInfo titleTextInfo) {
  458. this.titleTextInfo = titleTextInfo;
  459. refreshUI();
  460. return this;
  461. }
  462. public TextInfo getMessageTextInfo() {
  463. return messageTextInfo;
  464. }
  465. public BottomDialog setMessageTextInfo(TextInfo messageTextInfo) {
  466. this.messageTextInfo = messageTextInfo;
  467. refreshUI();
  468. return this;
  469. }
  470. public TextInfo getCancelTextInfo() {
  471. return cancelTextInfo;
  472. }
  473. public BottomDialog setCancelTextInfo(TextInfo cancelTextInfo) {
  474. this.cancelTextInfo = cancelTextInfo;
  475. refreshUI();
  476. return this;
  477. }
  478. public int getBackgroundColor() {
  479. return backgroundColor;
  480. }
  481. public BottomDialog setBackgroundColor(@ColorInt int backgroundColor) {
  482. this.backgroundColor = backgroundColor;
  483. refreshUI();
  484. return this;
  485. }
  486. }