diff --git a/.gradle/7.3.3/checksums/checksums.lock b/.gradle/7.3.3/checksums/checksums.lock deleted file mode 100644 index 4806728..0000000 Binary files a/.gradle/7.3.3/checksums/checksums.lock and /dev/null differ diff --git a/.gradle/7.3.3/checksums/md5-checksums.bin b/.gradle/7.3.3/checksums/md5-checksums.bin deleted file mode 100644 index f9ab64e..0000000 Binary files a/.gradle/7.3.3/checksums/md5-checksums.bin and /dev/null differ diff --git a/.gradle/7.3.3/checksums/sha1-checksums.bin b/.gradle/7.3.3/checksums/sha1-checksums.bin deleted file mode 100644 index a303793..0000000 Binary files a/.gradle/7.3.3/checksums/sha1-checksums.bin and /dev/null differ diff --git a/.gradle/7.3.3/dependencies-accessors/dependencies-accessors.lock b/.gradle/7.3.3/dependencies-accessors/dependencies-accessors.lock deleted file mode 100644 index 914498e..0000000 Binary files a/.gradle/7.3.3/dependencies-accessors/dependencies-accessors.lock and /dev/null differ diff --git a/.gradle/7.3.3/executionHistory/executionHistory.bin b/.gradle/7.3.3/executionHistory/executionHistory.bin deleted file mode 100644 index 88088b5..0000000 Binary files a/.gradle/7.3.3/executionHistory/executionHistory.bin and /dev/null differ diff --git a/.gradle/7.3.3/executionHistory/executionHistory.lock b/.gradle/7.3.3/executionHistory/executionHistory.lock deleted file mode 100644 index a5a398a..0000000 Binary files a/.gradle/7.3.3/executionHistory/executionHistory.lock and /dev/null differ diff --git a/.gradle/7.3.3/fileChanges/last-build.bin b/.gradle/7.3.3/fileChanges/last-build.bin deleted file mode 100644 index f76dd23..0000000 Binary files a/.gradle/7.3.3/fileChanges/last-build.bin and /dev/null differ diff --git a/.gradle/7.3.3/fileHashes/fileHashes.bin b/.gradle/7.3.3/fileHashes/fileHashes.bin deleted file mode 100644 index 5646ec2..0000000 Binary files a/.gradle/7.3.3/fileHashes/fileHashes.bin and /dev/null differ diff --git a/.gradle/7.3.3/fileHashes/fileHashes.lock b/.gradle/7.3.3/fileHashes/fileHashes.lock deleted file mode 100644 index 5374c4a..0000000 Binary files a/.gradle/7.3.3/fileHashes/fileHashes.lock and /dev/null differ diff --git a/.gradle/7.3.3/fileHashes/resourceHashesCache.bin b/.gradle/7.3.3/fileHashes/resourceHashesCache.bin deleted file mode 100644 index b1d137e..0000000 Binary files a/.gradle/7.3.3/fileHashes/resourceHashesCache.bin and /dev/null differ diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock deleted file mode 100644 index 4a57957..0000000 Binary files a/.gradle/buildOutputCleanup/buildOutputCleanup.lock and /dev/null differ diff --git a/.gradle/buildOutputCleanup/cache.properties b/.gradle/buildOutputCleanup/cache.properties deleted file mode 100644 index 222963c..0000000 --- a/.gradle/buildOutputCleanup/cache.properties +++ /dev/null @@ -1,2 +0,0 @@ -#Tue May 20 10:45:30 CST 2025 -gradle.version=7.3.3 diff --git a/.gradle/buildOutputCleanup/outputFiles.bin b/.gradle/buildOutputCleanup/outputFiles.bin deleted file mode 100644 index 3990b99..0000000 Binary files a/.gradle/buildOutputCleanup/outputFiles.bin and /dev/null differ diff --git a/.gradle/checksums/checksums.lock b/.gradle/checksums/checksums.lock deleted file mode 100644 index 51c8b3b..0000000 Binary files a/.gradle/checksums/checksums.lock and /dev/null differ diff --git a/.gradle/checksums/md5-checksums.bin b/.gradle/checksums/md5-checksums.bin deleted file mode 100644 index 1c60440..0000000 Binary files a/.gradle/checksums/md5-checksums.bin and /dev/null differ diff --git a/.gradle/checksums/sha1-checksums.bin b/.gradle/checksums/sha1-checksums.bin deleted file mode 100644 index 48d0996..0000000 Binary files a/.gradle/checksums/sha1-checksums.bin and /dev/null differ diff --git a/.gradle/configuration-cache/gc.properties b/.gradle/configuration-cache/gc.properties deleted file mode 100644 index e69de29..0000000 diff --git a/.gradle/file-system.probe b/.gradle/file-system.probe deleted file mode 100644 index 650902a..0000000 Binary files a/.gradle/file-system.probe and /dev/null differ diff --git a/.gradle/vcs-1/gc.properties b/.gradle/vcs-1/gc.properties deleted file mode 100644 index e69de29..0000000 diff --git a/AlbumDialog/build.gradle b/AlbumDialog/build.gradle index e4376e2..8aa33d8 100644 --- a/AlbumDialog/build.gradle +++ b/AlbumDialog/build.gradle @@ -27,11 +27,10 @@ android { } dependencies { + api project(path: ':DialogX') + api project(path: ':DialogXMaterialYou') implementation 'androidx.appcompat:appcompat:1.4.1' - def dialogx_version = "0.0.49" - api "com.kongzue.dialogx:DialogX:${dialogx_version}" - api "com.kongzue.dialogx.style:DialogXMaterialYouStyle:${dialogx_version}" implementation 'com.github.bumptech.glide:glide:4.12.0' implementation "androidx.recyclerview:recyclerview:1.2.1" api 'com.github.getActivity:XXPermissions:21.3' diff --git a/DialogX/.gitignore b/DialogX/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/DialogX/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/DialogX/build.gradle b/DialogX/build.gradle new file mode 100644 index 0000000..1d95208 --- /dev/null +++ b/DialogX/build.gradle @@ -0,0 +1,39 @@ +plugins { + id 'com.android.library' +} + +android { + compileSdkVersion 33 + + defaultConfig { + minSdkVersion 19 + targetSdkVersion 33 + + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + viewBinding { + enabled = true + } +} + +task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' +} + +artifacts { + archives sourcesJar +} +dependencies { + implementation 'androidx.appcompat:appcompat:1.4.1' + implementation 'androidx.constraintlayout:constraintlayout:2.1.3' + compileOnly files('libs\\androidx-rs.jar') + api files('libs\\DialogXInterface.jar') +} \ No newline at end of file diff --git a/.gradle/7.3.3/dependencies-accessors/gc.properties b/DialogX/consumer-rules.pro similarity index 100% rename from .gradle/7.3.3/dependencies-accessors/gc.properties rename to DialogX/consumer-rules.pro diff --git a/DialogX/libs/DialogXInterface.jar b/DialogX/libs/DialogXInterface.jar new file mode 100644 index 0000000..1092610 Binary files /dev/null and b/DialogX/libs/DialogXInterface.jar differ diff --git a/DialogX/libs/androidx-rs.jar b/DialogX/libs/androidx-rs.jar new file mode 100644 index 0000000..bce0d16 Binary files /dev/null and b/DialogX/libs/androidx-rs.jar differ diff --git a/DialogX/proguard-rules.pro b/DialogX/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/DialogX/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/DialogX/src/main/AndroidManifest.xml b/DialogX/src/main/AndroidManifest.xml new file mode 100644 index 0000000..1977b08 --- /dev/null +++ b/DialogX/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/DialogX/src/main/java/com/kongzue/dialogx/DialogX.java b/DialogX/src/main/java/com/kongzue/dialogx/DialogX.java new file mode 100644 index 0000000..0b51864 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/DialogX.java @@ -0,0 +1,217 @@ +package com.kongzue.dialogx; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Color; +import android.util.Log; + +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.style.MaterialStyle; +import com.kongzue.dialogx.util.DialogListBuilder; +import com.kongzue.dialogx.util.InputInfo; +import com.kongzue.dialogx.util.TextInfo; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/21 17:07 + */ +public class DialogX { + + public static final String ERROR_INIT_TIPS = "DialogX.init: 初始化异常,context 为 null 或未初始化,详情请查看 https://github.com/kongzue/DialogX/wiki"; + + //开启日志 + public static boolean DEBUGMODE = true; + + //全局主题风格 + public static DialogXStyle globalStyle = MaterialStyle.style(); + + //全局对话框明暗风格 + public static DialogX.THEME globalTheme = DialogX.THEME.LIGHT; + + //TipDialog 和 WaitDialog 明暗风格,不设置则默认根据 globalTheme 定义 + public static DialogX.THEME tipTheme; + + //DialogX 实现模式(实验性功能) + public static IMPL_MODE implIMPLMode = IMPL_MODE.VIEW; + + //对话框最大宽度(像素) + public static int dialogMaxWidth; + + //对话框最大高度(像素) + public static int dialogMaxHeight; + + //对话框最小宽度(像素) + public static int dialogMinWidth; + + //对话框最小高度(像素) + public static int dialogMinHeight; + + //是否允许 InputDialog 自动弹出键盘 + public static boolean autoShowInputKeyboard = true; + + //同时只显示一个 PopTip + public static boolean onlyOnePopTip = false; + + //同时只显示一个 PopNotification + public static boolean onlyOnePopNotification = true; + + //默认按钮文字样式 + public static TextInfo buttonTextInfo; + + //默认确定按钮文字样式 + public static TextInfo okButtonTextInfo; + + //默认标题文字样式 + public static TextInfo titleTextInfo; + + //默认内容文字样式 + public static TextInfo messageTextInfo; + + //默认 WaitDialog 和 TipDialog 文字样式 + public static TextInfo tipTextInfo; + + //默认输入框文字样式 + public static InputInfo inputInfo; + + //默认底部菜单、对话框的标题文字样式 + public static TextInfo menuTitleInfo; + + //默认底部菜单文本样式 + public static TextInfo menuTextInfo; + + //默认对话框背景颜色(值为 ColorInt,为 null 不生效) + public static Integer backgroundColor = null; + + //默认 TipDialog 和 WaitDialog 背景颜色(值为 ColorInt,为 null 不生效) + public static Integer tipBackgroundColor = null; + + /** + * 重写 TipDialog 和 WaitDialog 进度动画颜色, + * 注意此属性为覆盖性质,即设置此值将替换提示框原本的进度动画的颜色,包括亮暗色切换的颜色变化也将被替代 + * (值为 ColorInt,为 null 不生效) + */ + public static Integer tipProgressColor = null; + + //默认对话框默认是否可以点击外围遮罩区域或返回键关闭,此开关不影响提示框(TipDialog)以及等待框(TipDialog) + public static boolean cancelable = true; + + //默认提示框及等待框(WaitDialog、TipDialog)默认是否可以关闭 + public static boolean cancelableTipDialog = false; + + //默认取消按钮文本文字,影响 BottomDialog + public static String cancelButtonText; + + //默认 PopTip 文本样式 + public static TextInfo popTextInfo; + + //默认启动对话框动画时长 + public static long enterAnimDuration = -1; + + //默认关闭对话框动画时长 + public static long exitAnimDuration = -1; + + //全局 Dialog 生命周期监听器 + public static DialogLifecycleCallback dialogLifeCycleListener; + + //是否自动在主线程执行 + public static boolean autoRunOnUIThread = true; + + //使用振动反馈 + public static boolean useHaptic = true; + + /** + * 声明:若 Activity 已使用沉浸式适配请开启(已废弃) + *

+ * 请注意,若你没有使用沉浸式适配,请关闭此选项,此选项将影响对话框布局是否允许延伸至导航栏背后显示 + */ + @Deprecated + public static boolean useActivityLayoutTranslationNavigationBar = false; + + /** + * 设置 BottomDialog 导航栏背景颜色 + * 彩蛋:a_man 私人定制款属性 + */ + public static int bottomDialogNavbarColor = Color.TRANSPARENT; + + //触摸滑动触发阈值,影响 BottomDialog、FullScreenDialog 下滑关闭触发距离,单位:像素 + public static int touchSlideTriggerThreshold = dip2px(35); + + //Window 模式使用全局悬浮窗,需要 SYSTEM_ALERT_WINDOW 权限 + public static boolean globalHoverWindow = false; + + //部分插屏广告 SDK 可能出现背景黑屏的问题,在这里配置需要 DialogX 屏蔽的 Activity 的包名以屏蔽对该 activity 的支持: + public static String[] unsupportedActivitiesPackageNames = new String[]{ + "com.bytedance.sdk.openadsdk.stub.activity", + "com.mobile.auth.gatewayauth", + "com.google.android.gms.ads" + }; + + public static int defaultMessageDialogBackgroundRadius = -1; + + public static int defaultBottomDialogBackgroundRadius = -1; + + public static int defaultFullScreenDialogBackgroundRadius = -1; + + public static int defaultWaitAndTipDialogBackgroundRadius = -1; + + public static int defaultPopMenuBackgroundRadius = -1; + + public static int defaultPopTipBackgroundRadius = -1; + + public static int defaultPopNotificationBackgroundRadius = -1; + + //开启沉浸式适配 + public static boolean enableImmersiveMode = true; + + //沉浸式忽略左右的非安全区 + public static boolean ignoreUnsafeInsetsHorizontal = false; + + public enum THEME { + LIGHT, DARK, AUTO + } + + public enum IMPL_MODE { + VIEW, WINDOW, DIALOG_FRAGMENT, FLOATING_ACTIVITY + } + + public static void init(Context context) { + if (context == null) { + error(ERROR_INIT_TIPS); + return; + } + BaseDialog.init(context); + } + + public static void error(Object o) { + if (DEBUGMODE) Log.e(">>>", o.toString()); + } + + private static int dip2px(float dpValue) { + final float scale = Resources.getSystem().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } + + public static DialogListBuilder showDialogList(BaseDialog... dialogs) { + return DialogListBuilder.create(dialogs).show(); + } + + // 默认消息对话框标题文本 + public static CharSequence defaultMessageDialogTitleText; + + // 等待提示框默认文本 + public static CharSequence defaultWaitDialogWaitingText; + + // 成功提示框默认文本 + public static CharSequence defaultTipDialogSuccessText; + + // 错误提示框默认文本 + public static CharSequence defaultTipDialogErrorText; + + // 警告提示框默认文本 + public static CharSequence defaultTipDialogWarningText; +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/BottomDialog.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/BottomDialog.java new file mode 100644 index 0000000..fb5084e --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/BottomDialog.java @@ -0,0 +1,1449 @@ +package com.kongzue.dialogx.dialogs; + +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.app.Activity; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Outline; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.os.Build; +import android.text.TextUtils; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.view.animation.DecelerateInterpolator; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.appcompat.app.AppCompatActivity; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.interfaces.BaseOnDialogClickCallback; +import com.kongzue.dialogx.interfaces.BlurViewType; +import com.kongzue.dialogx.interfaces.BottomDialogSlideEventLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogConvertViewInterface; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXBaseBottomDialog; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.OnBackPressedListener; +import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener; +import com.kongzue.dialogx.interfaces.OnBindView; +import com.kongzue.dialogx.interfaces.OnDialogButtonClickListener; +import com.kongzue.dialogx.interfaces.OnMenuButtonClickListener; +import com.kongzue.dialogx.interfaces.ScrollController; +import com.kongzue.dialogx.util.BottomDialogTouchEventInterceptor; +import com.kongzue.dialogx.util.TextInfo; +import com.kongzue.dialogx.util.views.DialogScrollView; +import com.kongzue.dialogx.util.views.DialogXBaseRelativeLayout; +import com.kongzue.dialogx.util.views.MaxRelativeLayout; + +import java.util.HashMap; +import java.util.List; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/6 15:17 + */ +public class BottomDialog extends BaseDialog implements DialogXBaseBottomDialog { + + public static int overrideEnterDuration = -1; + public static int overrideExitDuration = -1; + public static BOOLEAN overrideCancelable; + protected OnBindView onBindView; + protected CharSequence title; + protected CharSequence message; + protected CharSequence cancelText; + protected CharSequence okText; + protected CharSequence otherText; + protected boolean allowInterceptTouch = true; + protected boolean bottomNonSafetyAreaBySelf = false; + protected Integer maskColor = null; + protected BaseOnDialogClickCallback cancelButtonClickListener; + protected BaseOnDialogClickCallback okButtonClickListener; + protected BaseOnDialogClickCallback otherButtonClickListener; + protected OnBackgroundMaskClickListener onBackgroundMaskClickListener; + protected OnBackPressedListener onBackPressedListener; + protected BOOLEAN privateCancelable; + protected boolean bkgInterceptTouch = true; + protected float backgroundRadius = DialogX.defaultBottomDialogBackgroundRadius; + protected Drawable titleIcon; + protected DialogXAnimInterface dialogXAnimImpl; + protected BUTTON_SELECT_RESULT buttonSelectResult = BUTTON_SELECT_RESULT.NONE; + protected boolean scrollableWhenContentLargeThanVisibleRange = true; + + protected TextInfo titleTextInfo; + protected TextInfo messageTextInfo; + protected TextInfo menuTextInfo; + protected TextInfo cancelTextInfo = new TextInfo().setBold(true); + protected TextInfo okTextInfo = new TextInfo().setBold(true); + protected TextInfo otherTextInfo = new TextInfo().setBold(true); + + /** + * 此值用于,当禁用滑动时(style.overrideBottomDialogRes.touchSlide = false时)的最大显示高度。 + * 0:不限制,最大显示到屏幕可用高度。 + */ + protected float bottomDialogMaxHeight = 0f; + + protected DialogLifecycleCallback dialogLifecycleCallback; + + protected BottomDialog me = this; + + protected BottomDialog() { + super(); + } + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + public static BottomDialog build() { + return new BottomDialog(); + } + + public static BottomDialog build(DialogXStyle style) { + return new BottomDialog().setStyle(style); + } + + public static BottomDialog build(OnBindView onBindView) { + return new BottomDialog().setCustomView(onBindView); + } + + public BottomDialog(CharSequence title, CharSequence message) { + this.title = title; + this.message = message; + } + + public BottomDialog(int titleResId, int messageResId) { + this.title = getString(titleResId); + this.message = getString(messageResId); + } + + public static BottomDialog show(CharSequence title, CharSequence message) { + BottomDialog bottomDialog = new BottomDialog(title, message); + bottomDialog.show(); + return bottomDialog; + } + + public static BottomDialog show(int titleResId, int messageResId) { + BottomDialog bottomDialog = new BottomDialog(titleResId, messageResId); + bottomDialog.show(); + return bottomDialog; + } + + public BottomDialog(CharSequence title, CharSequence message, OnBindView onBindView) { + this.title = title; + this.message = message; + this.onBindView = onBindView; + } + + public BottomDialog(int titleResId, int messageResId, OnBindView onBindView) { + this.title = getString(titleResId); + this.message = getString(messageResId); + this.onBindView = onBindView; + } + + public static BottomDialog show(CharSequence title, CharSequence message, OnBindView onBindView) { + BottomDialog bottomDialog = new BottomDialog(title, message, onBindView); + bottomDialog.show(); + return bottomDialog; + } + + public static BottomDialog show(int titleResId, int messageResId, OnBindView onBindView) { + BottomDialog bottomDialog = new BottomDialog(titleResId, messageResId, onBindView); + bottomDialog.show(); + return bottomDialog; + } + + public BottomDialog(CharSequence title, OnBindView onBindView) { + this.title = title; + this.onBindView = onBindView; + } + + public BottomDialog(int titleResId, OnBindView onBindView) { + this.title = getString(titleResId); + this.onBindView = onBindView; + } + + public static BottomDialog show(CharSequence title, OnBindView onBindView) { + BottomDialog bottomDialog = new BottomDialog(title, onBindView); + bottomDialog.show(); + return bottomDialog; + } + + public static BottomDialog show(int titleResId, OnBindView onBindView) { + BottomDialog bottomDialog = new BottomDialog(titleResId, onBindView); + bottomDialog.show(); + return bottomDialog; + } + + public BottomDialog(OnBindView onBindView) { + this.onBindView = onBindView; + } + + public static BottomDialog show(OnBindView onBindView) { + BottomDialog bottomDialog = new BottomDialog(onBindView); + bottomDialog.show(); + return bottomDialog; + } + + public BottomDialog show() { + if (isHide && getDialogView() != null && isShow) { + if (hideWithExitAnim && getDialogImpl() != null) { + getDialogView().setVisibility(View.VISIBLE); + getDialogImpl().getDialogXAnimImpl().doShowAnim(me, getDialogImpl().bkg); + } else { + getDialogView().setVisibility(View.VISIBLE); + } + return this; + } + super.beforeShow(); + if (getDialogView() == null) { + int layoutId = isLightTheme() ? R.layout.layout_dialogx_bottom_material : R.layout.layout_dialogx_bottom_material_dark; + if (style.overrideBottomDialogRes() != null) { + layoutId = style.overrideBottomDialogRes().overrideDialogLayout(isLightTheme()); + } + + View dialogView = createView(layoutId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + } else { + show(getDialogView()); + } + return this; + } + + public void show(Activity activity) { + super.beforeShow(); + if (getDialogView() == null) { + int layoutId = isLightTheme() ? R.layout.layout_dialogx_bottom_material : R.layout.layout_dialogx_bottom_material_dark; + if (style.overrideBottomDialogRes() != null) { + layoutId = style.overrideBottomDialogRes().overrideDialogLayout(isLightTheme()); + } + + View dialogView = createView(layoutId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(activity, dialogView); + } else { + show(activity, getDialogView()); + } + } + + protected DialogImpl dialogImpl; + + public class DialogImpl implements DialogConvertViewInterface { + + private BottomDialogTouchEventInterceptor bottomDialogTouchEventInterceptor; + + public DialogXBaseRelativeLayout boxRoot; + public RelativeLayout boxBkg; + public MaxRelativeLayout bkg; + public ImageView imgTab; + public ViewGroup boxBody; + public TextView txtDialogTitle; + public ScrollController scrollView; + public LinearLayout boxContent; + public TextView txtDialogTip; + public View imgSplit; + public ViewGroup boxList; + public RelativeLayout boxCustom; + public ViewGroup boxCancel; + public ImageView splitSelectPositive; + public ImageView splitSelectOther; + + public LinearLayout boxButton; + public TextView btnSelectNegative; + public TextView btnSelectOther; + public TextView btnSelectPositive; + + private List blurViews; + + public DialogImpl(View convertView) { + if (convertView == null) return; + setDialogView(convertView); + boxRoot = convertView.findViewById(R.id.box_root); + boxBkg = convertView.findViewById(R.id.box_bkg); + bkg = convertView.findViewById(R.id.bkg); + imgTab = convertView.findViewById(R.id.img_tab); + boxBody = convertView.findViewById(R.id.box_body); + txtDialogTitle = convertView.findViewById(R.id.txt_dialog_title); + scrollView = convertView.findViewById(R.id.scrollView); + boxContent = convertView.findViewById(R.id.box_content); + txtDialogTip = convertView.findViewById(R.id.txt_dialog_tip); + imgSplit = convertView.findViewWithTag("split"); + boxList = convertView.findViewById(R.id.box_list); + boxCustom = convertView.findViewById(R.id.box_custom); + + if (!scrollableWhenContentLargeThanVisibleRange) { + ViewGroup bodyContent = (ViewGroup) txtDialogTitle.getParent(); + ((ViewGroup) boxContent.getParent()).removeView(boxContent); + bodyContent.addView(boxContent, 1, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + } + boxCancel = convertView.findViewWithTag("cancelBox"); + + boxButton = convertView.findViewById(R.id.box_button); + btnSelectNegative = convertView.findViewById(R.id.btn_selectNegative); + btnSelectOther = convertView.findViewById(R.id.btn_selectOther); + btnSelectPositive = convertView.findViewById(R.id.btn_selectPositive); + splitSelectPositive = convertView.findViewWithTag("imgPositiveButtonSplit"); + splitSelectOther = convertView.findViewWithTag("imgOtherButtonSplit"); + + blurViews = findAllBlurView(convertView); + + init(); + dialogImpl = this; + refreshView(); + } + + public void reBuild() { + init(); + dialogImpl = this; + refreshView(); + } + + /** + * 此值记录了BottomDialog启动后的位置 + * ·当内容高度大于屏幕安全区高度时,BottomDialog会以全屏方式启动,但一开始只会展开到 0.8×屏幕高度, + * 此时可以再次上划查看全部内容。 + * ·当内容高度小于屏幕安全区高度时,BottomDialog会以内容高度启动。 + *

+ * 记录这个值的目的是,当用户向下滑动时,判断情况该回到这个位置还是关闭对话框, + * 并阻止当内容高度已经完全显示时的继续向上滑动操作。 + */ + public float bkgEnterAimY = -1; + + @Override + public void init() { + buttonSelectResult = BUTTON_SELECT_RESULT.NONE; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(getThisOrderIndex()); + } + + if (titleTextInfo == null) titleTextInfo = DialogX.titleTextInfo; + if (messageTextInfo == null) messageTextInfo = DialogX.messageTextInfo; + if (okTextInfo == null) okTextInfo = DialogX.okButtonTextInfo; + if (okTextInfo == null) okTextInfo = DialogX.buttonTextInfo; + if (cancelTextInfo == null) cancelTextInfo = DialogX.buttonTextInfo; + if (otherTextInfo == null) otherTextInfo = DialogX.buttonTextInfo; + if (backgroundColor == null) backgroundColor = DialogX.backgroundColor; + if (cancelText == null) cancelText = DialogX.cancelButtonText; + + if (txtDialogTitle != null) txtDialogTitle.getPaint().setFakeBoldText(true); + if (btnSelectNegative != null) btnSelectNegative.getPaint().setFakeBoldText(true); + if (btnSelectPositive != null) btnSelectPositive.getPaint().setFakeBoldText(true); + if (btnSelectOther != null) btnSelectOther.getPaint().setFakeBoldText(true); + + boxBkg.setY(getRootFrameLayout() == null ? Resources.getSystem().getDisplayMetrics().heightPixels : getRootFrameLayout().getMeasuredHeight()); + + boxRoot.setParentDialog(me); + boxRoot.setOnLifecycleCallBack(new DialogXBaseRelativeLayout.OnLifecycleCallBack() { + @Override + public void onShow() { + + isShow = true; + preShow = false; + + setLifecycleState(Lifecycle.State.CREATED); + getDialogLifecycleCallback().onShow(me); + BottomDialog.this.onShow(me); + + onDialogShow(); + + refreshUI(); + } + + @Override + public void onDismiss() { + isShow = false; + getDialogLifecycleCallback().onDismiss(me); + BottomDialog.this.onDismiss(me); + setLifecycleState(Lifecycle.State.DESTROYED); + dialogImpl = null; + bottomDialogTouchEventInterceptor = null; + dialogLifecycleCallback = null; + System.gc(); + } + }); + + if (btnSelectNegative != null) { + btnSelectNegative.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + haptic(v); + buttonSelectResult = BUTTON_SELECT_RESULT.BUTTON_CANCEL; + if (cancelButtonClickListener != null) { + if (cancelButtonClickListener instanceof OnDialogButtonClickListener) { + if (!((OnDialogButtonClickListener) cancelButtonClickListener).onClick(me, v)) { + dismiss(); + } + } else if (cancelButtonClickListener instanceof OnMenuButtonClickListener) { + if (!((OnMenuButtonClickListener) cancelButtonClickListener).onClick(me, v)) { + dismiss(); + } + } + } else { + dismiss(); + } + } + }); + } + if (btnSelectOther != null) { + btnSelectOther.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + haptic(v); + buttonSelectResult = BUTTON_SELECT_RESULT.BUTTON_OTHER; + if (otherButtonClickListener != null) { + if (otherButtonClickListener instanceof OnDialogButtonClickListener) { + if (!((OnDialogButtonClickListener) otherButtonClickListener).onClick(me, v)) { + dismiss(); + } + } else if (otherButtonClickListener instanceof OnMenuButtonClickListener) { + if (!((OnMenuButtonClickListener) otherButtonClickListener).onClick(me, v)) { + dismiss(); + } + } + } else { + dismiss(); + } + } + }); + } + if (btnSelectPositive != null) { + btnSelectPositive.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + haptic(v); + buttonSelectResult = BUTTON_SELECT_RESULT.BUTTON_OK; + if (okButtonClickListener != null) { + if (okButtonClickListener instanceof OnDialogButtonClickListener) { + if (!((OnDialogButtonClickListener) okButtonClickListener).onClick(me, v)) { + dismiss(); + } + } else if (okButtonClickListener instanceof OnMenuButtonClickListener) { + if (!((OnMenuButtonClickListener) okButtonClickListener).onClick(me, v)) { + dismiss(); + } + } + } else { + dismiss(); + } + } + }); + } + + if (imgSplit != null) { + int dividerRes = style.overrideBottomDialogRes().overrideMenuDividerDrawableRes(isLightTheme()); + int dividerHeight = style.overrideBottomDialogRes().overrideMenuDividerHeight(isLightTheme()); + if (dividerRes != 0) imgSplit.setBackgroundResource(dividerRes); + if (dividerHeight != 0) { + ViewGroup.LayoutParams lp = imgSplit.getLayoutParams(); + lp.height = dividerHeight; + imgSplit.setLayoutParams(lp); + } + } + + boxRoot.setOnBackPressedListener(new DialogXBaseRelativeLayout.PrivateBackPressedListener() { + @Override + public boolean onBackPressed() { + if (onBackPressedListener != null) { + if (onBackPressedListener.onBackPressed(me)) { + dismiss(); + } + } else { + if (isCancelable()) { + dismiss(); + } + } + return true; + } + }); + + boxBkg.post(new Runnable() { + @Override + public void run() { + getDialogXAnimImpl().doShowAnim(BottomDialog.this, bkg); + + Integer blurFrontColor = null; + Float dialogXRadius = null; + if (style.messageDialogBlurSettings() != null) { + blurFrontColor = getColorNullable(getIntStyleAttr(style.messageDialogBlurSettings().blurForwardColorRes(isLightTheme()))); + dialogXRadius = getFloatStyleAttr((float) style.messageDialogBlurSettings().blurBackgroundRoundRadiusPx()); + } + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setOverlayColor(backgroundColor == null ? blurFrontColor : backgroundColor); + ((BlurViewType) blurView).setRadiusPx(dialogXRadius); + } + } + } + }); + + runOnMainDelay(new Runnable() { + @Override + public void run() { + bottomDialogTouchEventInterceptor = new BottomDialogTouchEventInterceptor(me, dialogImpl); + } + }, getEnterAnimationDuration()); + + onDialogInit(); + } + + @Override + public void refreshView() { + if (boxRoot == null || getOwnActivity() == null) { + return; + } + + bkg.setMaxWidth(getMaxWidth()); + bkg.setMaxHeight(getMaxHeight()); + bkg.setMinimumWidth(getMinWidth()); + bkg.setMinimumHeight(getMinHeight()); + + boxRoot.setAutoUnsafePlacePadding(isEnableImmersiveMode()); + boxRoot.setRootPadding(screenPaddings[0], screenPaddings[1], screenPaddings[2], screenPaddings[3]); + if (backgroundColor != null) { + tintColor(bkg, backgroundColor); + if (style.tintButtonBackground()) { + tintColor(btnSelectOther, backgroundColor); + tintColor(btnSelectNegative, backgroundColor); + tintColor(btnSelectPositive, backgroundColor); + } + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setOverlayColor(backgroundColor); + } + } + } + + showText(txtDialogTitle, title); + showText(txtDialogTip, message); + + useTextInfo(txtDialogTitle, titleTextInfo); + useTextInfo(txtDialogTip, messageTextInfo); + useTextInfo(btnSelectNegative, cancelTextInfo); + useTextInfo(btnSelectOther, otherTextInfo); + useTextInfo(btnSelectPositive, okTextInfo); + + if (boxButton != null) { + boxButton.setVisibility((btnSelectNegative != null && btnSelectNegative.getVisibility() == View.VISIBLE) || + (btnSelectOther != null && btnSelectOther.getVisibility() == View.VISIBLE) || + (btnSelectPositive != null && btnSelectPositive.getVisibility() == View.VISIBLE) ? + View.VISIBLE : View.GONE); + } + if (titleIcon != null) { + int size = (int) txtDialogTitle.getTextSize(); + titleIcon.setBounds(0, 0, size, size); + txtDialogTitle.setCompoundDrawablePadding(dip2px(10)); + txtDialogTitle.setCompoundDrawables(titleIcon, null, null, null); + } + + if (bkgInterceptTouch) { + if (isCancelable()) { + boxRoot.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onBackgroundMaskClickListener == null || !onBackgroundMaskClickListener.onClick(me, v)) { + doDismiss(v); + } + } + }); + } else { + boxRoot.setOnClickListener(null); + } + } else { + boxRoot.setClickable(false); + } + boxBkg.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + boxRoot.callOnClick(); + } + }); + if (backgroundRadius > -1) { + if (bkg.getBackground() instanceof GradientDrawable) { + GradientDrawable gradientDrawable = (GradientDrawable) bkg.getBackground(); + if (gradientDrawable != null) + gradientDrawable.setCornerRadii(new float[]{backgroundRadius, backgroundRadius, backgroundRadius, backgroundRadius, 0, 0, 0, 0}); + } + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + bkg.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(0, 0, view.getWidth(), (int) (view.getHeight() + backgroundRadius), backgroundRadius); + } + }); + bkg.setClipToOutline(true); + } + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setRadiusPx(backgroundRadius); + } + } + } + + if (maskColor != null) { + boxRoot.setBackground(new ColorDrawable(maskColor)); + } + + if (onBindView != null && onBindView.getCustomView() != null) { + onBindView.bindParent(boxCustom, me); + if (onBindView.getCustomView() instanceof ScrollController) { + if (scrollView instanceof DialogScrollView) { + ((DialogScrollView) scrollView).setVerticalScrollBarEnabled(false); + } + scrollView = (ScrollController) onBindView.getCustomView(); + } else { + View scrollController = onBindView.getCustomView().findViewWithTag("ScrollController"); + if (scrollController instanceof ScrollController) { + if (scrollView instanceof DialogScrollView) { + ((DialogScrollView) scrollView).setVerticalScrollBarEnabled(false); + } + scrollView = (ScrollController) scrollController; + } + } + } + + if (isAllowInterceptTouch() && isCancelable()) { + if (imgTab != null) imgTab.setVisibility(View.VISIBLE); + } else { + if (imgTab != null) imgTab.setVisibility(View.GONE); + } + + if (bottomDialogTouchEventInterceptor != null) { + bottomDialogTouchEventInterceptor.refresh(me, this); + } + + if (imgSplit != null) { + if (txtDialogTitle.getVisibility() == View.VISIBLE || txtDialogTip.getVisibility() == View.VISIBLE) { + imgSplit.setVisibility(View.VISIBLE); + } else { + imgSplit.setVisibility(View.GONE); + } + } + + if (boxCancel != null) { + if (isNull(cancelText)) { + boxCancel.setVisibility(View.GONE); + } else { + boxCancel.setVisibility(View.VISIBLE); + } + } + + showText(btnSelectPositive, okText); + showText(btnSelectNegative, cancelText); + showText(btnSelectOther, otherText); + if (splitSelectPositive != null) { + splitSelectPositive.setVisibility(btnSelectPositive.getVisibility()); + } + if (splitSelectOther != null) { + splitSelectOther.setVisibility(btnSelectOther.getVisibility()); + } + + onDialogRefreshUI(); + } + + @Override + public void doDismiss(View v) { + if (BottomDialog.this.preDismiss(BottomDialog.this)) { + return; + } + if (v != null) v.setEnabled(false); + if (getOwnActivity() == null) return; + + if (!dismissAnimFlag && getDialogXAnimImpl() != null) { + dismissAnimFlag = true; + + getDialogXAnimImpl().doExitAnim(BottomDialog.this, bkg); + + runOnMainDelay(new Runnable() { + @Override + public void run() { + if (boxRoot != null) { + boxRoot.setVisibility(View.GONE); + } + dismiss(getDialogView()); + } + }, getExitAnimationDuration()); + } + } + + public void preDismiss() { + if (isCancelable()) { + if (getDialogLifecycleCallback() instanceof BottomDialogSlideEventLifecycleCallback) { + if (!((BottomDialogSlideEventLifecycleCallback) getDialogLifecycleCallback()).onSlideClose(me)) { + doDismiss(boxRoot); + } + return; + } + doDismiss(boxRoot); + } else { + long exitAnimDurationTemp = 300; + if (overrideExitDuration >= 0) { + exitAnimDurationTemp = overrideExitDuration; + } + if (exitAnimDuration >= 0) { + exitAnimDurationTemp = exitAnimDuration; + } + ObjectAnimator exitAnim = ObjectAnimator.ofFloat(boxBkg, "y", boxBkg.getY(), boxRoot.getUnsafePlace().top); + exitAnim.setDuration(exitAnimDurationTemp); + exitAnim.start(); + } + } + + protected DialogXAnimInterface getDialogXAnimImpl() { + if (dialogXAnimImpl == null) { + dialogXAnimImpl = new DialogXAnimInterface() { + @Override + public void doShowAnim(BottomDialog dialog, ViewGroup dialogBodyView) { + long enterAnimDurationTemp = getEnterAnimationDuration(); + + float customDialogTop = 0; + if (dialog.isAllowInterceptTouch()) { + if (bottomDialogMaxHeight > 0 && bottomDialogMaxHeight <= 1) { + customDialogTop = boxBkg.getHeight() - bottomDialogMaxHeight * boxBkg.getHeight(); + } else if (bottomDialogMaxHeight > 1) { + customDialogTop = boxBkg.getHeight() - bottomDialogMaxHeight; + } + } else { + if (bottomDialogMaxHeight > 0 && bottomDialogMaxHeight <= 1) { + customDialogTop = boxBkg.getHeight() - bottomDialogMaxHeight * boxBkg.getHeight(); + } else if (bottomDialogMaxHeight > 1) { + customDialogTop = boxBkg.getHeight() - bottomDialogMaxHeight; + } + boxBkg.setPadding(0, 0, 0, (int) customDialogTop); + } + + // 上移动画 + ObjectAnimator enterAnim = ObjectAnimator.ofFloat(boxBkg, "y", getRootFrameLayout() == null ? Resources.getSystem().getDisplayMetrics().heightPixels : getRootFrameLayout().getMeasuredHeight(), bkgEnterAimY = boxRoot.getUnsafePlace().top + customDialogTop); + enterAnim.setDuration(enterAnimDurationTemp); + enterAnim.setAutoCancel(true); + enterAnim.setInterpolator(new DecelerateInterpolator(2f)); + enterAnim.start(); + + // 遮罩层动画 + ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f); + bkgAlpha.setDuration(enterAnimDurationTemp); + bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + boxRoot.setBkgAlpha((Float) animation.getAnimatedValue()); + } + }); + bkgAlpha.start(); + } + + @Override + public void doExitAnim(BottomDialog dialog, ViewGroup dialogBodyView) { + long exitAnimDurationTemp = getExitAnimationDuration(); + + ObjectAnimator exitAnim = ObjectAnimator.ofFloat(boxBkg, "y", boxBkg.getY(), boxBkg.getHeight()); + exitAnim.setDuration(exitAnimDurationTemp); + exitAnim.start(); + + ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1f, 0f); + bkgAlpha.setDuration(exitAnimDurationTemp); + bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + boxRoot.setBkgAlpha((Float) animation.getAnimatedValue()); + } + }); + bkgAlpha.start(); + } + }; + } + return dialogXAnimImpl; + } + + public long getExitAnimationDuration() { + long exitAnimDurationTemp = 300; + if (overrideExitDuration >= 0) { + exitAnimDurationTemp = overrideExitDuration; + } + if (exitAnimDuration != -1) { + exitAnimDurationTemp = exitAnimDuration; + } + return exitAnimDurationTemp; + } + + public long getEnterAnimationDuration() { + long enterAnimDurationTemp = 300; + if (overrideEnterDuration >= 0) { + enterAnimDurationTemp = overrideEnterDuration; + } + if (enterAnimDuration >= 0) { + enterAnimDurationTemp = enterAnimDuration; + } + return enterAnimDurationTemp; + } + + public BottomDialogTouchEventInterceptor getBottomDialogTouchEventInterceptor() { + return bottomDialogTouchEventInterceptor; + } + } + + public void refreshUI() { + if (getDialogImpl() == null) return; + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl != null) dialogImpl.refreshView(); + } + }); + } + + public void dismiss() { + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl == null) return; + dialogImpl.doDismiss(null); + } + }); + } + + public DialogLifecycleCallback getDialogLifecycleCallback() { + return dialogLifecycleCallback == null ? new DialogLifecycleCallback() { + } : dialogLifecycleCallback; + } + + public BottomDialog setDialogLifecycleCallback(DialogLifecycleCallback dialogLifecycleCallback) { + this.dialogLifecycleCallback = dialogLifecycleCallback; + if (isShow) dialogLifecycleCallback.onShow(me); + return this; + } + + public OnBackPressedListener getOnBackPressedListener() { + return (OnBackPressedListener) onBackPressedListener; + } + + public BottomDialog setOnBackPressedListener(OnBackPressedListener onBackPressedListener) { + this.onBackPressedListener = onBackPressedListener; + refreshUI(); + return this; + } + + public BottomDialog setStyle(DialogXStyle style) { + this.style = style; + return this; + } + + public BottomDialog setTheme(DialogX.THEME theme) { + this.theme = theme; + return this; + } + + public boolean isCancelable() { + if (privateCancelable != null) { + return privateCancelable == BOOLEAN.TRUE; + } + if (overrideCancelable != null) { + return overrideCancelable == BOOLEAN.TRUE; + } + return cancelable; + } + + public BottomDialog setCancelable(boolean cancelable) { + this.privateCancelable = cancelable ? BOOLEAN.TRUE : BOOLEAN.FALSE; + refreshUI(); + return this; + } + + public DialogImpl getDialogImpl() { + return dialogImpl; + } + + public CharSequence getTitle() { + return title; + } + + public BottomDialog setTitle(CharSequence title) { + this.title = title; + refreshUI(); + return this; + } + + public BottomDialog setTitle(int titleResId) { + this.title = getString(titleResId); + refreshUI(); + return this; + } + + public CharSequence getMessage() { + return message; + } + + public BottomDialog setMessage(CharSequence message) { + this.message = message; + refreshUI(); + return this; + } + + public BottomDialog setMessage(int messageResId) { + this.message = getString(messageResId); + refreshUI(); + return this; + } + + public CharSequence getCancelButton() { + return cancelText; + } + + public BottomDialog setCancelButton(CharSequence cancelText) { + this.cancelText = cancelText; + refreshUI(); + return this; + } + + public BottomDialog setCancelButton(int cancelTextResId) { + this.cancelText = getString(cancelTextResId); + refreshUI(); + return this; + } + + public BottomDialog setCancelButton(OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelButtonClickListener = cancelButtonClickListener; + return this; + } + + public BottomDialog setCancelButton(CharSequence cancelText, OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelText = cancelText; + this.cancelButtonClickListener = cancelButtonClickListener; + refreshUI(); + return this; + } + + public BottomDialog setCancelButton(int cancelTextResId, OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelText = getString(cancelTextResId); + this.cancelButtonClickListener = cancelButtonClickListener; + refreshUI(); + return this; + } + + public BottomDialog setCustomView(OnBindView onBindView) { + this.onBindView = onBindView; + refreshUI(); + return this; + } + + public View getCustomView() { + if (onBindView == null) return null; + return onBindView.getCustomView(); + } + + public BottomDialog removeCustomView() { + this.onBindView.clean(); + refreshUI(); + return this; + } + + public boolean isAllowInterceptTouch() { + if (style.overrideBottomDialogRes() == null) { + return false; + } else { + return allowInterceptTouch && style.overrideBottomDialogRes().touchSlide(); + } + } + + public BottomDialog setAllowInterceptTouch(boolean allowInterceptTouch) { + this.allowInterceptTouch = allowInterceptTouch; + return this; + } + + public OnDialogButtonClickListener getCancelButtonClickListener() { + return (OnDialogButtonClickListener) cancelButtonClickListener; + } + + public BottomDialog setCancelButtonClickListener(OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelButtonClickListener = cancelButtonClickListener; + return this; + } + + public TextInfo getTitleTextInfo() { + return titleTextInfo; + } + + public BottomDialog setTitleTextInfo(TextInfo titleTextInfo) { + this.titleTextInfo = titleTextInfo; + refreshUI(); + return this; + } + + public TextInfo getMessageTextInfo() { + return messageTextInfo; + } + + public BottomDialog setMessageTextInfo(TextInfo messageTextInfo) { + this.messageTextInfo = messageTextInfo; + refreshUI(); + return this; + } + + public TextInfo getCancelTextInfo() { + return cancelTextInfo; + } + + public BottomDialog setCancelTextInfo(TextInfo cancelTextInfo) { + this.cancelTextInfo = cancelTextInfo; + refreshUI(); + return this; + } + + public TextInfo getOkTextInfo() { + return okTextInfo; + } + + public BottomDialog setOkTextInfo(TextInfo okTextInfo) { + this.okTextInfo = okTextInfo; + return this; + } + + public TextInfo getOtherTextInfo() { + return otherTextInfo; + } + + public BottomDialog setOtherTextInfo(TextInfo otherTextInfo) { + this.otherTextInfo = otherTextInfo; + return this; + } + + public int getBackgroundColor() { + return backgroundColor; + } + + public BottomDialog setBackgroundColor(@ColorInt int backgroundColor) { + this.backgroundColor = backgroundColor; + refreshUI(); + return this; + } + + public BottomDialog setBackgroundColorRes(@ColorRes int backgroundRes) { + this.backgroundColor = getColor(backgroundRes); + refreshUI(); + return this; + } + + public CharSequence getOkButton() { + return okText; + } + + public BottomDialog setOkButton(CharSequence okText) { + this.okText = okText; + refreshUI(); + return this; + } + + public BottomDialog setOkButton(int OkTextResId) { + this.okText = getString(OkTextResId); + refreshUI(); + return this; + } + + public BottomDialog setOkButton(OnDialogButtonClickListener OkButtonClickListener) { + this.okButtonClickListener = OkButtonClickListener; + return this; + } + + public BottomDialog setOkButton(CharSequence OkText, OnDialogButtonClickListener OkButtonClickListener) { + this.okText = OkText; + this.okButtonClickListener = OkButtonClickListener; + refreshUI(); + return this; + } + + public BottomDialog setOkButton(int OkTextResId, OnDialogButtonClickListener OkButtonClickListener) { + this.okText = getString(OkTextResId); + this.okButtonClickListener = OkButtonClickListener; + refreshUI(); + return this; + } + + public CharSequence getOtherButton() { + return otherText; + } + + public BottomDialog setOtherButton(CharSequence otherText) { + this.otherText = otherText; + refreshUI(); + return this; + } + + public BottomDialog setOtherButton(int OtherTextResId) { + this.otherText = getString(OtherTextResId); + refreshUI(); + return this; + } + + public BottomDialog setOtherButton(OnDialogButtonClickListener OtherButtonClickListener) { + this.otherButtonClickListener = OtherButtonClickListener; + return this; + } + + public BottomDialog setOtherButton(CharSequence OtherText, OnDialogButtonClickListener OtherButtonClickListener) { + this.otherText = OtherText; + this.otherButtonClickListener = OtherButtonClickListener; + refreshUI(); + return this; + } + + public BottomDialog setOtherButton(int OtherTextResId, OnDialogButtonClickListener OtherButtonClickListener) { + this.otherText = getString(OtherTextResId); + this.otherButtonClickListener = OtherButtonClickListener; + refreshUI(); + return this; + } + + public BottomDialog setMaskColor(@ColorInt int maskColor) { + this.maskColor = maskColor; + refreshUI(); + return this; + } + + public long getEnterAnimDuration() { + return enterAnimDuration; + } + + public BottomDialog setEnterAnimDuration(long enterAnimDuration) { + this.enterAnimDuration = enterAnimDuration; + return this; + } + + public long getExitAnimDuration() { + return exitAnimDuration; + } + + public BottomDialog setExitAnimDuration(long exitAnimDuration) { + this.exitAnimDuration = exitAnimDuration; + return this; + } + + @Override + public void restartDialog() { + if (getDialogView() != null) { + dismiss(getDialogView()); + isShow = false; + } + if (getDialogImpl().boxCustom != null) { + getDialogImpl().boxCustom.removeAllViews(); + } + if (getDialogImpl().boxList != null) { + getDialogImpl().boxList.removeAllViews(); + } + int layoutId = isLightTheme() ? R.layout.layout_dialogx_bottom_material : R.layout.layout_dialogx_bottom_material_dark; + if (style.overrideBottomDialogRes() != null) { + layoutId = style.overrideBottomDialogRes().overrideDialogLayout(isLightTheme()); + } + + enterAnimDuration = 0; + View dialogView = createView(layoutId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + } + + protected boolean isHide; + + public void hide() { + isHide = true; + hideWithExitAnim = false; + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + } + + protected boolean hideWithExitAnim; + + public void hideWithExitAnim() { + hideWithExitAnim = true; + isHide = true; + if (getDialogImpl() != null) { + getDialogImpl().getDialogXAnimImpl().doExitAnim(me, getDialogImpl().bkg); + + runOnMainDelay(new Runnable() { + @Override + public void run() { + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + } + }, getDialogImpl().getExitAnimationDuration()); + } + } + + @Override + protected void shutdown() { + dismiss(); + } + + public float getBottomDialogMaxHeight() { + return bottomDialogMaxHeight; + } + + public BottomDialog setBottomDialogMaxHeight(float bottomDialogMaxHeight) { + this.bottomDialogMaxHeight = bottomDialogMaxHeight; + return this; + } + + public BottomDialog setMaxWidth(int maxWidth) { + this.maxWidth = maxWidth; + refreshUI(); + return this; + } + + public BottomDialog setMaxHeight(int maxHeight) { + this.maxHeight = maxHeight; + refreshUI(); + return this; + } + + public BottomDialog setMinHeight(int minHeight) { + this.minHeight = minHeight; + refreshUI(); + return this; + } + + public BottomDialog setMinWidth(int minWidth) { + this.minWidth = minWidth; + refreshUI(); + return this; + } + + public BottomDialog setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public boolean isBkgInterceptTouch() { + return bkgInterceptTouch; + } + + public BottomDialog setBkgInterceptTouch(boolean bkgInterceptTouch) { + this.bkgInterceptTouch = bkgInterceptTouch; + return this; + } + + public OnBackgroundMaskClickListener getOnBackgroundMaskClickListener() { + return (OnBackgroundMaskClickListener) onBackgroundMaskClickListener; + } + + public BottomDialog setOnBackgroundMaskClickListener(OnBackgroundMaskClickListener onBackgroundMaskClickListener) { + this.onBackgroundMaskClickListener = onBackgroundMaskClickListener; + return this; + } + + public BottomDialog setRadius(float radiusPx) { + backgroundRadius = radiusPx; + refreshUI(); + return this; + } + + public float getRadius() { + return backgroundRadius; + } + + public Drawable getTitleIcon() { + return titleIcon; + } + + public BottomDialog setTitleIcon(Bitmap titleIcon) { + this.titleIcon = new BitmapDrawable(getResources(), titleIcon); + refreshUI(); + return this; + } + + public BottomDialog setTitleIcon(int titleIconResId) { + this.titleIcon = getResources().getDrawable(titleIconResId); + refreshUI(); + return this; + } + + public BottomDialog setTitleIcon(Drawable titleIcon) { + this.titleIcon = titleIcon; + refreshUI(); + return this; + } + + public DialogXAnimInterface getDialogXAnimImpl() { + return dialogXAnimImpl; + } + + public BottomDialog setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public BottomDialog setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public BottomDialog setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + public BUTTON_SELECT_RESULT getButtonSelectResult() { + return buttonSelectResult; + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new BottomDialog() { + * + * @param dialog self + * @Override public void onShow(BottomDialog dialog) { + * //... + * } + * } + */ + protected void onShow(BottomDialog dialog) { + + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new BottomDialog() { + * + * @param dialog self + * @Override public boolean onDismiss(BottomDialog dialog) { + * WaitDialog.show("Please Wait..."); + * if (dialog.getButtonSelectResult() == BUTTON_SELECT_RESULT.BUTTON_OK) { + * //点击了OK的情况 + * //... + * } else { + * //其他按钮点击、对话框dismiss的情况 + * //... + * } + * return false; + * } + * } + */ + // 用于使用 new 构建实例时,override 的生命周期事件 + protected void onDismiss(BottomDialog dialog) { + + } + + public boolean isBottomNonSafetyAreaBySelf() { + return bottomNonSafetyAreaBySelf; + } + + public BottomDialog setBottomNonSafetyAreaBySelf(boolean bottomNonSafetyAreaBySelf) { + this.bottomNonSafetyAreaBySelf = bottomNonSafetyAreaBySelf; + return this; + } + + public boolean isScrollableWhenContentLargeThanVisibleRange() { + return scrollableWhenContentLargeThanVisibleRange; + } + + public BottomDialog setScrollableWhenContentLargeThanVisibleRange(boolean scrollableWhenContentLargeThanVisibleRange) { + this.scrollableWhenContentLargeThanVisibleRange = scrollableWhenContentLargeThanVisibleRange; + return this; + } + + public BottomDialog setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public BottomDialog setHapticFeedbackEnabled(boolean isHapticFeedbackEnabled) { + this.isHapticFeedbackEnabled = isHapticFeedbackEnabled ? 1 : 0; + return this; + } + + public BottomDialog onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public BottomDialog onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public BottomDialog setEnableImmersiveMode(boolean enableImmersiveMode) { + this.enableImmersiveMode = enableImmersiveMode; + refreshUI(); + return this; + } + + public BottomDialog appendMessage(CharSequence message) { + this.message = TextUtils.concat(this.message, message); + refreshUI(); + return this; + } + + public BottomDialog setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex(" + orderIndex + ") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public BottomDialog bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public BottomDialog setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public BottomDialog cleanAction(int actionId) { + dialogActionRunnableMap.remove(actionId); + return this; + } + + public BottomDialog cleanAllAction() { + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + public void callDialogDismiss() { + dismiss(); + } + + public BottomDialog bindDismissWithLifecycleOwner(LifecycleOwner owner) { + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/BottomMenu.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/BottomMenu.java new file mode 100644 index 0000000..134d52a --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/BottomMenu.java @@ -0,0 +1,1605 @@ +package com.kongzue.dialogx.dialogs; + +import static android.view.View.OVER_SCROLL_NEVER; + +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.text.TextUtils; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.LinearLayout; + +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BottomMenuListViewTouchEvent; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.MenuItemLayoutRefreshCallback; +import com.kongzue.dialogx.interfaces.MenuItemTextInfoInterceptor; +import com.kongzue.dialogx.interfaces.OnBackPressedListener; +import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener; +import com.kongzue.dialogx.interfaces.OnBindView; +import com.kongzue.dialogx.interfaces.OnMenuButtonClickListener; +import com.kongzue.dialogx.interfaces.OnDialogButtonClickListener; +import com.kongzue.dialogx.interfaces.OnIconChangeCallBack; +import com.kongzue.dialogx.interfaces.OnMenuItemClickListener; +import com.kongzue.dialogx.interfaces.OnMenuItemSelectListener; +import com.kongzue.dialogx.interfaces.SELECT_MODE; +import com.kongzue.dialogx.util.BottomMenuArrayAdapter; +import com.kongzue.dialogx.util.ItemDivider; +import com.kongzue.dialogx.util.TextInfo; +import com.kongzue.dialogx.util.views.DialogListView; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/6 23:48 + */ +public class BottomMenu extends BottomDialog { + + protected BottomMenu me = this; + protected int selectionIndex = -1; + protected SELECT_MODE selectMode = SELECT_MODE.NONE; + protected ArrayList selectionItems; + protected boolean showSelectedBackgroundTips = false; + protected MenuItemLayoutRefreshCallback menuMenuItemLayoutRefreshCallback; + protected Map menuUsability = new HashMap(); + protected ItemDivider itemDivider; + + protected OnMenuItemClickListener onMenuItemClickListener; + + public static BottomMenu build() { + return new BottomMenu(); + } + + public static BottomMenu build(DialogXStyle style) { + return new BottomMenu().setStyle(style); + } + + public static BottomMenu build(OnBindView onBindView) { + return new BottomMenu().setCustomView(onBindView); + } + + protected BottomMenu() { + super(); + } + + protected OnIconChangeCallBack onIconChangeCallBack; + protected MenuItemTextInfoInterceptor menuItemTextInfoInterceptor; + protected DialogListView listView; + protected BaseAdapter menuListAdapter; + protected List menuList; + protected List iconResIds; + protected boolean autoTintIconInLightOrDarkMode = true; + + public static BottomMenu show(List menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(List menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu showStringList(List menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.setMenuStringList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu showStringList(List menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.setMenuStringList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(String... menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(String[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence[] menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence title, CharSequence message, List menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence title, CharSequence message, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence title, List menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence title, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu showStringList(CharSequence title, CharSequence message, List menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuStringList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu showStringList(CharSequence title, CharSequence message, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuStringList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence title, CharSequence message, String[] menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence title, CharSequence message, String[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence title, CharSequence message, CharSequence[] menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence title, CharSequence message, CharSequence[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(String title, String message, List menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(String title, String message, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu showStringList(String title, String message, List menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuStringList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu showStringList(String title, String message, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuStringList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(String title, String message, String[] menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(String title, String message, String[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(String title, String message, CharSequence[] menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(String title, String message, CharSequence[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(int titleResId, int messageResId, List menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.message = bottomMenu.getString(messageResId); + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(int titleResId, List menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu showStringList(int titleResId, int messageResId, List menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.message = bottomMenu.getString(messageResId); + bottomMenu.setMenuStringList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(int titleResId, int messageResId, String[] menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.message = bottomMenu.getString(messageResId); + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(int titleResId, int messageResId, CharSequence[] menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.message = bottomMenu.getString(messageResId); + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(int titleResId, int messageResId, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.message = bottomMenu.getString(messageResId); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(int titleResId, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu showStringList(int titleResId, int messageResId, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.message = bottomMenu.getString(messageResId); + bottomMenu.setMenuStringList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(int titleResId, int messageResId, String[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.message = bottomMenu.getString(messageResId); + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(int titleResId, int messageResId, CharSequence[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.message = bottomMenu.getString(messageResId); + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence title, CharSequence[] menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence title, String[] menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence title, CharSequence[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence title, String[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(int titleResId, CharSequence[] menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(int titleResId, String[] menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(int titleResId, CharSequence[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(int titleResId, String[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + private float touchDownY; + + public static final int ITEM_CLICK_DELAY = 100; + private long lastClickTime = 0; + private int[] resultArray; + private CharSequence[] selectTextArray; + + @Override + protected void onDialogShow() { + if (getDialogImpl() != null) { + getDialogImpl().boxList.setVisibility(View.VISIBLE); + + if (!isAllowInterceptTouch()) { + getDialogImpl().bkg.setMaxHeight((int) bottomDialogMaxHeight); + if (bottomDialogMaxHeight != 0) { + dialogImpl.scrollView.lockScroll(true); + } + } + + int dividerDrawableResId = 0; + int dividerHeight = 1; + if (style.overrideBottomDialogRes() != null) { + dividerDrawableResId = style.overrideBottomDialogRes().overrideMenuDividerDrawableRes(isLightTheme()); + dividerHeight = style.overrideBottomDialogRes().overrideMenuDividerHeight(isLightTheme()); + } + if (dividerDrawableResId == 0) { + dividerDrawableResId = isLightTheme() ? R.drawable.rect_dialogx_material_menu_split_divider : R.drawable.rect_dialogx_material_menu_split_divider_night; + } + + + if (!isLightTheme()) { + listView = new DialogListView(getDialogImpl(), getOwnActivity(), R.style.DialogXCompatThemeDark); + } else { + listView = new DialogListView(getDialogImpl(), getOwnActivity()); + } + listView.setTag("ScrollController"); + listView.setOverScrollMode(OVER_SCROLL_NEVER); + listView.setDivider(getResources().getDrawable(dividerDrawableResId)); + listView.setDividerHeight(dividerHeight); + getDialogImpl().scrollView = listView; + + listView.setBottomMenuListViewTouchEvent(new BottomMenuListViewTouchEvent() { + @Override + public void down(MotionEvent event) { + touchDownY = getDialogImpl().boxBkg.getY(); + log("#TouchDown: " + touchDownY); + } + }); + + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + if (!isMenuItemEnable(position)) { + return; + } + haptic(view); + long currentTime = System.currentTimeMillis(); + if (currentTime - lastClickTime > ITEM_CLICK_DELAY) { + lastClickTime = currentTime; + float deltaY = Math.abs(touchDownY - getDialogImpl().boxBkg.getY()); + log("#Click:deltaY= " + deltaY); + if (deltaY > dip2px(15)) { + return; + } + selectionIndex = position; + log("### onMenuItemClickListener=" + onMenuItemClickListener); + switch (selectMode) { + case NONE: + if (onMenuItemClickListener != null) { + if (!onMenuItemClickListener.onClick(me, menuList.get(position), position)) { + dismiss(); + } + } else { + dismiss(); + } + break; + case SINGLE: + if (onMenuItemClickListener instanceof OnMenuItemSelectListener) { + OnMenuItemSelectListener onMenuItemSelectListener = (OnMenuItemSelectListener) onMenuItemClickListener; + if (!onMenuItemSelectListener.onClick(me, menuList.get(position), position)) { + dismiss(); + } else { + menuListAdapter.notifyDataSetInvalidated(); + onMenuItemSelectListener.onOneItemSelect(me, menuList.get(position), position, true); + } + } else { + if (onMenuItemClickListener != null) { + if (!onMenuItemClickListener.onClick(me, menuList.get(position), position)) { + dismiss(); + } + } else { + menuListAdapter.notifyDataSetInvalidated(); + } + } + break; + case MULTIPLE: + if (onMenuItemClickListener instanceof OnMenuItemSelectListener) { + OnMenuItemSelectListener onMenuItemSelectListener = (OnMenuItemSelectListener) onMenuItemClickListener; + if (!onMenuItemSelectListener.onClick(me, menuList.get(position), position)) { + dismiss(); + } else { + if (selectionItems.contains(position)) { + selectionItems.remove(new Integer(position)); + } else { + selectionItems.add(position); + } + menuListAdapter.notifyDataSetInvalidated(); + resultArray = new int[selectionItems.size()]; + selectTextArray = new CharSequence[selectionItems.size()]; + for (int i = 0; i < selectionItems.size(); i++) { + resultArray[i] = selectionItems.get(i); + selectTextArray[i] = menuList.get(resultArray[i]); + } + onMenuItemSelectListener.onMultiItemSelect(me, selectTextArray, resultArray); + } + } else { + if (onMenuItemClickListener != null) { + if (!onMenuItemClickListener.onClick(me, menuList.get(position), position)) { + dismiss(); + } + } else { + if (selectionItems.contains(position)) { + selectionItems.remove(new Integer(position)); + } else { + selectionItems.add(position); + } + menuListAdapter.notifyDataSetInvalidated(); + resultArray = new int[selectionItems.size()]; + selectTextArray = new CharSequence[selectionItems.size()]; + for (int i = 0; i < selectionItems.size(); i++) { + resultArray[i] = selectionItems.get(i); + selectTextArray[i] = menuList.get(resultArray[i]); + } + } + } + break; + } + } + } + }); + if (style.overrideBottomDialogRes() != null) { + if (style.overrideBottomDialogRes().overrideMenuItemLayout(true, 0, 0, false) != 0) { + listView.setSelector(R.color.empty); + } + } + + ViewGroup.LayoutParams listViewLp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + getDialogImpl().boxList.addView(listView, listViewLp); + refreshUI(); + } + } + + @Override + public void refreshUI() { + if (getDialogImpl() == null) return; + if (listView != null) { + if (menuListAdapter == null) { + menuListAdapter = new BottomMenuArrayAdapter(me, getOwnActivity(), menuList); + } + if (listView.getAdapter() == null) { + listView.setAdapter(menuListAdapter); + } else { + if (listView.getAdapter() != menuListAdapter) { + listView.setAdapter(menuListAdapter); + } else { + menuListAdapter.notifyDataSetChanged(); + } + } + } + + // 部分主题下选中项默认按下效果 + if (showSelectedBackgroundTips) { + if (listView != null) { + listView.post(new Runnable() { + @Override + public void run() { + if (menuListAdapter instanceof BottomMenuArrayAdapter && showSelectedBackgroundTips) { + BottomMenuArrayAdapter bottomMenuArrayAdapter = ((BottomMenuArrayAdapter) menuListAdapter); + + View selectItemView = listView.getChildAt(getSelection()); + if (selectItemView != null) { + selectItemView.post(new Runnable() { + @Override + public void run() { + selectItemView.setPressed(true); + } + }); + } + } + } + }); + } + + } + + if (itemDivider != null) { + listView.setDivider(itemDivider.createDividerDrawable(getOwnActivity(), isLightTheme())); + listView.setDividerHeight(itemDivider.getWidth()); + } + super.refreshUI(); + } + + public void preRefreshUI() { + if (getDialogImpl() == null) return; + runOnMain(new Runnable() { + @Override + public void run() { + refreshUI(); + } + }); + } + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + public List getMenuList() { + return menuList; + } + + public BottomMenu setMenuList(List menuList) { + this.menuList = menuList; + this.menuListAdapter = null; + preRefreshUI(); + return this; + } + + private boolean isSameSize(int menuListSize) { + if (this.menuList == null || this.menuList.size() == 0) { + return true; + } + return this.menuList.size() == menuListSize; + } + + public BottomMenu setMenuStringList(List menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(menuList); + this.menuListAdapter = null; + preRefreshUI(); + return this; + } + + public BottomMenu setMenuList(String[] menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(Arrays.asList(menuList)); + this.menuListAdapter = null; + preRefreshUI(); + return this; + } + + public BottomMenu setMenuList(CharSequence[] menuList) { + this.menuList = Arrays.asList(menuList); + this.menuListAdapter = null; + preRefreshUI(); + return this; + } + + public BottomMenu setMenus(CharSequence... menuList) { + this.menuList = Arrays.asList(menuList); + this.menuListAdapter = null; + preRefreshUI(); + return this; + } + + public BottomMenu setMenus(String... menuList) { + this.menuList = Arrays.asList(menuList); + this.menuListAdapter = null; + preRefreshUI(); + return this; + } + + public OnIconChangeCallBack getOnIconChangeCallBack() { + return onIconChangeCallBack; + } + + public BottomMenu setOnIconChangeCallBack(OnIconChangeCallBack onIconChangeCallBack) { + this.onIconChangeCallBack = onIconChangeCallBack; + return this; + } + + public OnBackPressedListener getOnBackPressedListener() { + return (OnBackPressedListener) onBackPressedListener; + } + + public BottomMenu setOnBackPressedListener(OnBackPressedListener onBackPressedListener) { + this.onBackPressedListener = onBackPressedListener; + preRefreshUI(); + return this; + } + + public BottomMenu setDialogLifecycleCallback(DialogLifecycleCallback dialogLifecycleCallback) { + this.dialogLifecycleCallback = dialogLifecycleCallback; + if (isShow) dialogLifecycleCallback.onShow(me); + return this; + } + + public BottomMenu setStyle(DialogXStyle style) { + this.style = style; + return this; + } + + public BottomMenu setTheme(DialogX.THEME theme) { + this.theme = theme; + return this; + } + + public boolean isCancelable() { + if (privateCancelable != null) { + return privateCancelable == BOOLEAN.TRUE; + } + if (overrideCancelable != null) { + return overrideCancelable == BOOLEAN.TRUE; + } + return cancelable; + } + + public BottomMenu setCancelable(boolean cancelable) { + this.privateCancelable = cancelable ? BOOLEAN.TRUE : BOOLEAN.FALSE; + preRefreshUI(); + return this; + } + + public DialogImpl getDialogImpl() { + return dialogImpl; + } + + public CharSequence getTitle() { + return title; + } + + public BottomMenu setTitle(CharSequence title) { + this.title = title; + preRefreshUI(); + return this; + } + + public BottomMenu setTitle(int titleResId) { + this.title = getString(titleResId); + preRefreshUI(); + return this; + } + + public CharSequence getMessage() { + return message; + } + + public BottomMenu setMessage(CharSequence message) { + this.message = message; + preRefreshUI(); + return this; + } + + public BottomMenu setMessage(int messageResId) { + this.message = getString(messageResId); + preRefreshUI(); + return this; + } + + public CharSequence getCancelButton() { + return cancelText; + } + + public BottomMenu setCancelButton(CharSequence cancelText) { + this.cancelText = cancelText; + preRefreshUI(); + return this; + } + + public BottomMenu setCancelButton(int cancelTextResId) { + this.cancelText = getString(cancelTextResId); + preRefreshUI(); + return this; + } + + public BottomMenu setCancelButton(OnMenuButtonClickListener cancelButtonClickListener) { + this.cancelButtonClickListener = cancelButtonClickListener; + return this; + } + + public BottomMenu setCancelButton(CharSequence cancelText, OnMenuButtonClickListener cancelButtonClickListener) { + this.cancelText = cancelText; + this.cancelButtonClickListener = cancelButtonClickListener; + preRefreshUI(); + return this; + } + + public BottomMenu setCancelButton(int cancelTextResId, OnMenuButtonClickListener cancelButtonClickListener) { + this.cancelText = getString(cancelTextResId); + this.cancelButtonClickListener = cancelButtonClickListener; + preRefreshUI(); + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.BottomMenu#setCancelButton(OnMenuButtonClickListener )} + */ + @Deprecated + public BottomMenu setCancelButton(OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelButtonClickListener = cancelButtonClickListener; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.BottomMenu#setCancelButton(CharSequence cancelText, OnMenuButtonClickListener )} + */ + @Deprecated + public BottomMenu setCancelButton(CharSequence cancelText, OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelText = cancelText; + this.cancelButtonClickListener = cancelButtonClickListener; + preRefreshUI(); + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.BottomMenu#setCancelButton(int cancelTextResId, OnMenuButtonClickListener )} + */ + @Deprecated + public BottomMenu setCancelButton(int cancelTextResId, OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelText = getString(cancelTextResId); + this.cancelButtonClickListener = cancelButtonClickListener; + preRefreshUI(); + return this; + } + + public BottomMenu setCustomView(OnBindView onBindView) { + this.onBindView = onBindView; + preRefreshUI(); + return this; + } + + public View getCustomView() { + if (onBindView == null) return null; + return onBindView.getCustomView(); + } + + public BottomMenu removeCustomView() { + this.onBindView.clean(); + preRefreshUI(); + return this; + } + + public boolean isAllowInterceptTouch() { + return super.isAllowInterceptTouch(); + } + + public BottomMenu setAllowInterceptTouch(boolean allowInterceptTouch) { + this.allowInterceptTouch = allowInterceptTouch; + preRefreshUI(); + return this; + } + + public float getBottomDialogMaxHeight() { + return bottomDialogMaxHeight; + } + + public BottomMenu setBottomDialogMaxHeight(float bottomDialogMaxHeight) { + this.bottomDialogMaxHeight = bottomDialogMaxHeight; + return this; + } + + public OnMenuItemClickListener getOnMenuItemClickListener() { + return onMenuItemClickListener; + } + + public BottomMenu setOnMenuItemClickListener(OnMenuItemClickListener onMenuItemClickListener) { + this.onMenuItemClickListener = onMenuItemClickListener; + return this; + } + + public BaseAdapter getMenuListAdapter() { + return menuListAdapter; + } + + public BottomMenu setMenuListAdapter(BaseAdapter menuListAdapter) { + this.menuListAdapter = menuListAdapter; + return this; + } + + public OnMenuButtonClickListener getBottomMenuCancelButtonClickListener() { + return (OnMenuButtonClickListener) cancelButtonClickListener; + } + + public BottomMenu setCancelButtonClickListener(OnMenuButtonClickListener cancelButtonClickListener) { + this.cancelButtonClickListener = cancelButtonClickListener; + return this; + } + + public TextInfo getTitleTextInfo() { + return titleTextInfo; + } + + public BottomMenu setTitleTextInfo(TextInfo titleTextInfo) { + this.titleTextInfo = titleTextInfo; + preRefreshUI(); + return this; + } + + public TextInfo getMessageTextInfo() { + return messageTextInfo; + } + + public BottomMenu setMessageTextInfo(TextInfo messageTextInfo) { + this.messageTextInfo = messageTextInfo; + preRefreshUI(); + return this; + } + + public TextInfo getCancelTextInfo() { + return cancelTextInfo; + } + + public BottomMenu setCancelTextInfo(TextInfo cancelTextInfo) { + this.cancelTextInfo = cancelTextInfo; + preRefreshUI(); + return this; + } + + public int getBackgroundColor() { + return backgroundColor; + } + + public BottomMenu setBackgroundColor(@ColorInt int backgroundColor) { + this.backgroundColor = backgroundColor; + preRefreshUI(); + return this; + } + + public int getSelection() { + return selectionIndex; + } + + public ArrayList getSelectionList() { + return selectionItems; + } + + public BottomMenu setSelection(int selectionIndex) { + this.selectMode = SELECT_MODE.SINGLE; + this.selectionIndex = selectionIndex; + this.selectionItems = null; + menuListAdapter = null; + preRefreshUI(); + return this; + } + + public BottomMenu setSingleSelection() { + this.selectMode = SELECT_MODE.SINGLE; + this.selectionIndex = -1; + this.selectionItems = null; + menuListAdapter = null; + preRefreshUI(); + return this; + } + + public BottomMenu setSelection(int[] selectionItems) { + this.selectMode = SELECT_MODE.MULTIPLE; + this.selectionIndex = -1; + this.selectionItems = new ArrayList<>(); + if (selectionItems != null) { + for (int itemIndex : selectionItems) { + this.selectionItems.add(itemIndex); + } + } + menuListAdapter = null; + preRefreshUI(); + return this; + } + + public BottomMenu setMultiSelection() { + this.selectMode = SELECT_MODE.MULTIPLE; + this.selectionIndex = -1; + this.selectionItems = new ArrayList<>(); + menuListAdapter = null; + preRefreshUI(); + return this; + } + + public BottomMenu setSelection(List selectionItems) { + this.selectMode = SELECT_MODE.MULTIPLE; + this.selectionIndex = -1; + this.selectionItems = new ArrayList<>(selectionItems); + menuListAdapter = null; + preRefreshUI(); + return this; + } + + public BottomMenu setNoSelect() { + this.selectMode = SELECT_MODE.NONE; + this.selectionIndex = -1; + this.selectionItems = null; + menuListAdapter = null; + preRefreshUI(); + return this; + } + + public BottomMenu setBackgroundColorRes(@ColorRes int backgroundRes) { + this.backgroundColor = getColor(backgroundRes); + preRefreshUI(); + return this; + } + + public CharSequence getOkButton() { + return okText; + } + + public BottomMenu setOkButton(CharSequence okText) { + this.okText = okText; + preRefreshUI(); + return this; + } + + public BottomMenu setOkButton(int OkTextResId) { + this.okText = getString(OkTextResId); + preRefreshUI(); + return this; + } + + public BottomMenu setOkButton(OnMenuButtonClickListener okButtonClickListener) { + this.okButtonClickListener = okButtonClickListener; + return this; + } + + public BottomMenu setOkButton(CharSequence okText, OnMenuButtonClickListener okButtonClickListener) { + this.okText = okText; + this.okButtonClickListener = okButtonClickListener; + return this; + } + + public BottomMenu setOkButton(int okTextResId, OnMenuButtonClickListener okButtonClickListener) { + this.okText = getString(okTextResId); + this.okButtonClickListener = okButtonClickListener; + return this; + } + + public BottomMenu setHapticFeedbackEnabled(boolean isHapticFeedbackEnabled) { + this.isHapticFeedbackEnabled = isHapticFeedbackEnabled ? 1 : 0; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.BottomMenu#setOkButton(OnMenuButtonClickListener )} + */ + @Deprecated + public BottomMenu setOkButton(OnDialogButtonClickListener okButtonClickListener) { + this.okButtonClickListener = okButtonClickListener; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.BottomMenu#setOkButton(CharSequence okText, OnMenuButtonClickListener )} + */ + @Deprecated + public BottomMenu setOkButton(CharSequence okText, OnDialogButtonClickListener okButtonClickListener) { + this.okText = okText; + this.okButtonClickListener = okButtonClickListener; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.BottomMenu#setOkButton(int okTextResId, OnMenuButtonClickListener )} + */ + @Deprecated + public BottomMenu setOkButton(int okTextResId, OnDialogButtonClickListener okButtonClickListener) { + this.okText = getString(okTextResId); + this.okButtonClickListener = okButtonClickListener; + return this; + } + + public CharSequence getOtherButton() { + return otherText; + } + + public BottomMenu setOtherButton(CharSequence otherText) { + this.otherText = otherText; + preRefreshUI(); + return this; + } + + public BottomMenu setOtherButton(int OtherTextResId) { + this.otherText = getString(OtherTextResId); + preRefreshUI(); + return this; + } + + public BottomMenu setOtherButton(OnMenuButtonClickListener otherButtonClickListener) { + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + public BottomMenu setOtherButton(CharSequence otherText, OnMenuButtonClickListener otherButtonClickListener) { + this.otherText = otherText; + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + public BottomMenu setOtherButton(int otherTextResId, OnMenuButtonClickListener otherButtonClickListener) { + this.otherText = getString(otherTextResId); + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.BottomMenu#setOtherButton(OnMenuButtonClickListener )} + */ + @Deprecated + public BottomMenu setOtherButton(OnDialogButtonClickListener otherButtonClickListener) { + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.BottomMenu#setOtherButton(CharSequence otherText, OnMenuButtonClickListener )} + */ + @Deprecated + public BottomMenu setOtherButton(CharSequence otherText, OnDialogButtonClickListener otherButtonClickListener) { + this.otherText = otherText; + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.BottomMenu#setOtherButton(int otherTextResId, OnMenuButtonClickListener )} + */ + @Deprecated + public BottomMenu setOtherButton(int otherTextResId, OnDialogButtonClickListener otherButtonClickListener) { + this.otherText = getString(otherTextResId); + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + public BottomMenu setMaskColor(@ColorInt int maskColor) { + this.maskColor = maskColor; + preRefreshUI(); + return this; + } + + public long getEnterAnimDuration() { + return enterAnimDuration; + } + + public BottomMenu setEnterAnimDuration(long enterAnimDuration) { + this.enterAnimDuration = enterAnimDuration; + return this; + } + + public long getExitAnimDuration() { + return exitAnimDuration; + } + + public BottomMenu setExitAnimDuration(long exitAnimDuration) { + this.exitAnimDuration = exitAnimDuration; + return this; + } + + public SELECT_MODE getSelectMode() { + return selectMode; + } + + @Override + protected void shutdown() { + dismiss(); + } + + public BottomMenu setMaxWidth(int maxWidth) { + this.maxWidth = maxWidth; + refreshUI(); + return this; + } + + public BottomMenu setMaxHeight(int maxHeight) { + this.maxHeight = maxHeight; + refreshUI(); + return this; + } + + public BottomMenu setMinHeight(int minHeight) { + this.minHeight = minHeight; + refreshUI(); + return this; + } + + public BottomMenu setMinWidth(int minWidth) { + this.minWidth = minWidth; + refreshUI(); + return this; + } + + public BottomMenu setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public TextInfo getMenuTextInfo() { + if (menuTextInfo == null) return DialogX.menuTextInfo; + return menuTextInfo; + } + + public BottomMenu setMenuTextInfo(TextInfo menuTextInfo) { + this.menuTextInfo = menuTextInfo; + return this; + } + + public MenuItemTextInfoInterceptor getMenuItemTextInfoInterceptor() { + return menuItemTextInfoInterceptor; + } + + public BottomMenu setMenuItemTextInfoInterceptor(MenuItemTextInfoInterceptor menuItemTextInfoInterceptor) { + this.menuItemTextInfoInterceptor = menuItemTextInfoInterceptor; + return this; + } + + public boolean isBkgInterceptTouch() { + return bkgInterceptTouch; + } + + public BottomMenu setBkgInterceptTouch(boolean bkgInterceptTouch) { + this.bkgInterceptTouch = bkgInterceptTouch; + return this; + } + + public OnBackgroundMaskClickListener getOnBackgroundMaskClickListener() { + return onBackgroundMaskClickListener; + } + + public BottomMenu setOnBackgroundMaskClickListener(OnBackgroundMaskClickListener onBackgroundMaskClickListener) { + this.onBackgroundMaskClickListener = onBackgroundMaskClickListener; + return this; + } + + public BottomMenu setRadius(float radiusPx) { + backgroundRadius = radiusPx; + refreshUI(); + return this; + } + + public float getRadius() { + return backgroundRadius; + } + + public BottomMenu setTitleIcon(Bitmap titleIcon) { + this.titleIcon = new BitmapDrawable(getResources(), titleIcon); + refreshUI(); + return this; + } + + public BottomMenu setTitleIcon(int titleIconResId) { + this.titleIcon = getResources().getDrawable(titleIconResId); + refreshUI(); + return this; + } + + public BottomMenu setTitleIcon(Drawable titleIcon) { + this.titleIcon = titleIcon; + refreshUI(); + return this; + } + + public DialogXAnimInterface getDialogXAnimImpl() { + return dialogXAnimImpl; + } + + public BottomMenu setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public BottomMenu setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public BottomMenu setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + public boolean isShowSelectedBackgroundTips() { + return showSelectedBackgroundTips; + } + + public BottomMenu setShowSelectedBackgroundTips(boolean showSelectedBackgroundTips) { + this.showSelectedBackgroundTips = showSelectedBackgroundTips; + refreshUI(); + return this; + } + + // 返回点击的菜单索引 + public int getSelectionIndex() { + return selectionIndex; + } + + // 返回多选时,选择的菜单索引集合 + public int[] getSelectionIndexArray() { + return resultArray; + } + + // 返回多选时,选择的菜单文本集合 + public CharSequence[] getSelectTextArray() { + return selectTextArray; + } + + public MenuItemLayoutRefreshCallback getMenuMenuItemLayoutRefreshCallback() { + return menuMenuItemLayoutRefreshCallback; + } + + public BottomMenu setMenuMenuItemLayoutRefreshCallback(MenuItemLayoutRefreshCallback menuMenuItemLayoutRefreshCallback) { + this.menuMenuItemLayoutRefreshCallback = menuMenuItemLayoutRefreshCallback; + return this; + } + + public TextInfo getOkTextInfo() { + return okTextInfo; + } + + public BottomMenu setOkTextInfo(TextInfo okTextInfo) { + this.okTextInfo = okTextInfo; + return this; + } + + public TextInfo getOtherTextInfo() { + return otherTextInfo; + } + + public BottomMenu setOtherTextInfo(TextInfo otherTextInfo) { + this.otherTextInfo = otherTextInfo; + return this; + } + + public BottomMenu setScrollableWhenContentLargeThanVisibleRange(boolean scrollableWhenContentLargeThanVisibleRange) { + this.scrollableWhenContentLargeThanVisibleRange = scrollableWhenContentLargeThanVisibleRange; + return this; + } + + public BottomMenu setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public BottomMenu onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public BottomMenu onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public BottomMenu setEnableImmersiveMode(boolean enableImmersiveMode) { + this.enableImmersiveMode = enableImmersiveMode; + refreshUI(); + return this; + } + + public List getIconResIds() { + return iconResIds; + } + + public int getIconResIds(int position) { + if (iconResIds != null && position >= 0 && position < iconResIds.size()) { + return iconResIds.get(position); + } + return 0; + } + + public BottomMenu setIconResIds(List iconResIds) { + this.iconResIds = iconResIds; + refreshUI(); + return this; + } + + public BottomMenu setIconResIds(int... resIds) { + if (iconResIds == null) { + iconResIds = new ArrayList<>(); + } + for (int id : resIds) { + iconResIds.add(id); + } + refreshUI(); + return this; + } + + public boolean isAutoTintIconInLightOrDarkMode() { + return autoTintIconInLightOrDarkMode; + } + + public BottomMenu setAutoTintIconInLightOrDarkMode(boolean autoTintIconInLightOrDarkMode) { + this.autoTintIconInLightOrDarkMode = autoTintIconInLightOrDarkMode; + refreshUI(); + return this; + } + + public BottomMenu appendMessage(CharSequence message) { + this.message = TextUtils.concat(this.message, message); + refreshUI(); + return this; + } + + public BottomMenu setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex(" + orderIndex + ") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public BottomMenu bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public BottomMenu enableMenu(int... menuIndex) { + for (int i : menuIndex) { + menuUsability.put(i, true); + } + return this; + } + + public BottomMenu enableMenu(CharSequence... menuText) { + if (menuList != null && !menuList.isEmpty()) { + for (CharSequence c : menuText) { + int index = menuList.indexOf(c); + menuUsability.put(index, true); + } + } else { + error("DialogX: " + dialogKey() + " .enableMenu(" + menuText + ")执行失败,请先初始化菜单项 menuList"); + } + return this; + } + + public BottomMenu enableMenu(String... menuText) { + if (menuList != null && !menuList.isEmpty()) { + for (String c : menuText) { + int index = menuList.indexOf(c); + menuUsability.put(index, true); + } + } else { + error("DialogX: " + dialogKey() + " .enableMenu(" + menuText + ")执行失败,请先初始化菜单项 menuList"); + } + return this; + } + + public BottomMenu disableMenu(int... menuIndex) { + for (int i : menuIndex) { + menuUsability.put(i, false); + } + return this; + } + + public BottomMenu disableMenu(CharSequence... menuText) { + if (menuList != null && !menuList.isEmpty()) { + for (CharSequence c : menuText) { + int index = menuList.indexOf(c); + menuUsability.put(index, false); + } + } else { + error("DialogX: " + dialogKey() + " .disableMenu(" + menuText + ")执行失败,请先初始化菜单项 menuList"); + } + return this; + } + + public BottomMenu disableMenu(String... menuText) { + if (menuList != null && !menuList.isEmpty()) { + for (String c : menuText) { + int index = menuList.indexOf(c); + menuUsability.put(index, false); + } + } else { + error("DialogX: " + dialogKey() + " .disableMenu(" + menuText + ")执行失败,请先初始化菜单项 menuList"); + } + return this; + } + + public boolean isMenuItemEnable(int index) { + Boolean enabled = menuUsability.get(index); + if (enabled == null) { + return true; + } + return enabled; + } + + public BottomMenu setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public BottomMenu cleanAction(int actionId){ + dialogActionRunnableMap.remove(actionId); + return this; + } + + public BottomMenu cleanAllAction(){ + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + protected void callDialogDismissPrivate(){ + dismiss(); + } + + public BottomMenu bindDismissWithLifecycleOwner(LifecycleOwner owner){ + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } + + public BottomMenu setItemDivider(ItemDivider itemDivider) { + this.itemDivider = itemDivider; + refreshUI(); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/CustomDialog.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/CustomDialog.java new file mode 100644 index 0000000..b83d0c2 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/CustomDialog.java @@ -0,0 +1,1226 @@ +package com.kongzue.dialogx.dialogs; + +import android.animation.ValueAnimator; +import android.app.Activity; +import android.graphics.Color; +import android.os.Build; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.view.animation.DecelerateInterpolator; +import android.widget.RelativeLayout; + +import androidx.annotation.ColorInt; +import androidx.annotation.Nullable; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.interfaces.DialogConvertViewInterface; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.OnBackPressedListener; +import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener; +import com.kongzue.dialogx.interfaces.OnBindView; +import com.kongzue.dialogx.util.ObjectRunnable; +import com.kongzue.dialogx.util.views.DialogXBaseRelativeLayout; +import com.kongzue.dialogx.util.views.MaxRelativeLayout; + +import java.lang.ref.WeakReference; +import java.util.HashMap; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/20 11:59 + */ +public class CustomDialog extends BaseDialog { + + public static int overrideEnterDuration = -1; + public static int overrideExitDuration = -1; + public static int overrideEnterAnimRes = 0; + public static int overrideExitAnimRes = 0; + public static BOOLEAN overrideCancelable; + protected OnBindView onBindView; + protected DialogLifecycleCallback dialogLifecycleCallback; + protected OnBackPressedListener onBackPressedListener; + protected CustomDialog me = this; + protected DialogImpl dialogImpl; + protected int enterAnimResId = R.anim.anim_dialogx_default_enter; + protected int exitAnimResId = R.anim.anim_dialogx_default_exit; + protected ALIGN align = ALIGN.CENTER; + protected int maskColor = Color.TRANSPARENT; + protected BOOLEAN privateCancelable; + protected boolean bkgInterceptTouch = true; + protected OnBackgroundMaskClickListener onBackgroundMaskClickListener; + protected DialogXAnimInterface dialogXAnimImpl; + + protected WeakReference baseViewWeakReference; + protected int alignViewGravity = -1; //指定菜单相对 baseView 的位置 + protected int width = -1; //指定菜单宽度 + protected int height = -1; //指定菜单高度 + protected int[] baseViewLoc; + protected int[] marginRelativeBaseView = new int[4]; + + public enum ALIGN { + CENTER, + TOP, + TOP_CENTER, + TOP_LEFT, + TOP_RIGHT, + BOTTOM, + BOTTOM_CENTER, + BOTTOM_LEFT, + BOTTOM_RIGHT, + LEFT, + LEFT_CENTER, + LEFT_TOP, + LEFT_BOTTOM, + RIGHT, + RIGHT_CENTER, + RIGHT_TOP, + RIGHT_BOTTOM + } + + protected CustomDialog() { + super(); + } + + public static CustomDialog build() { + return new CustomDialog(); + } + + public static CustomDialog build(OnBindView onBindView) { + return new CustomDialog().setCustomView(onBindView); + } + + public CustomDialog(OnBindView onBindView) { + this.onBindView = onBindView; + } + + public static CustomDialog show(OnBindView onBindView) { + CustomDialog customDialog = new CustomDialog(onBindView); + customDialog.show(); + return customDialog; + } + + public static CustomDialog show(OnBindView onBindView, ALIGN align) { + CustomDialog customDialog = new CustomDialog(onBindView); + customDialog.align = align; + customDialog.show(); + return customDialog; + } + + public CustomDialog show() { + if (isHide && getDialogView() != null && isShow) { + if (hideWithExitAnim && getDialogImpl() != null && getDialogImpl().boxCustom != null) { + getDialogView().setVisibility(View.VISIBLE); + getDialogImpl().getDialogXAnimImpl().doShowAnim(CustomDialog.this, getDialogImpl().boxCustom); + getDialogImpl().boxCustom.setVisibility(View.VISIBLE); + getDialogImpl().boxCustom.startAnimation(getEnterAnimation()); + } else { + getDialogView().setVisibility(View.VISIBLE); + } + return this; + } + super.beforeShow(); + if (getDialogView() == null) { + View dialogView = createView(R.layout.layout_dialogx_custom); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + } else { + show(getDialogView()); + } + return this; + } + + public CustomDialog show(Activity activity) { + super.beforeShow(); + if (getDialogView() == null) { + View dialogView = createView(R.layout.layout_dialogx_custom); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(activity, dialogView); + } else { + show(activity, getDialogView()); + } + return this; + } + + private ViewTreeObserver viewTreeObserver; + private ViewTreeObserver.OnPreDrawListener baseViewDrawListener; + + public class DialogImpl implements DialogConvertViewInterface { + + public DialogXBaseRelativeLayout boxRoot; + public MaxRelativeLayout boxCustom; + + public DialogImpl(View convertView) { + if (convertView == null) return; + setDialogView(convertView); + boxRoot = convertView.findViewById(R.id.box_root); + boxCustom = convertView.findViewById(R.id.box_custom); + + init(); + dialogImpl = this; + refreshView(); + } + + @Override + public void init() { + if (baseViewLoc == null && baseView() != null) { + baseViewLoc = new int[4]; + baseView().getLocationInWindow(baseViewLoc); + baseViewLoc[2] = baseView().getWidth(); + baseViewLoc[3] = baseView().getHeight(); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(getThisOrderIndex()); + } + + boxRoot.setParentDialog(me); + boxRoot.setOnLifecycleCallBack(new DialogXBaseRelativeLayout.OnLifecycleCallBack() { + @Override + public void onShow() { + isShow = true; + preShow = false; + + setLifecycleState(Lifecycle.State.CREATED); + + getDialogLifecycleCallback().onShow(me); + CustomDialog.this.onShow(me); + onDialogShow(); + + boxCustom.setVisibility(View.GONE); + } + + @Override + public void onDismiss() { + isShow = false; + getDialogLifecycleCallback().onDismiss(me); + CustomDialog.this.onDismiss(me); + setLifecycleState(Lifecycle.State.DESTROYED); + dialogImpl = null; + dialogLifecycleCallback = null; + System.gc(); + } + }); + + boxRoot.setOnBackPressedListener(new DialogXBaseRelativeLayout.PrivateBackPressedListener() { + @Override + public boolean onBackPressed() { + if (onBackPressedListener != null) { + if (onBackPressedListener.onBackPressed(me)) { + dismiss(); + } + } else { + if (isCancelable()) { + dismiss(); + } + } + return true; + } + }); + + boxRoot.post(new Runnable() { + @Override + public void run() { + if (getDialogXAnimImpl() != null) { + getDialogXAnimImpl().doShowAnim(CustomDialog.this, boxCustom); + } + if (getDialogImpl() != null && getDialogImpl().boxCustom != null) { + getDialogImpl().boxCustom.setVisibility(View.VISIBLE); + } + + setLifecycleState(Lifecycle.State.RESUMED); + } + }); + + onDialogInit(); + } + + boolean initSetCustomViewLayoutListener = false; + ALIGN alignCache; + + @Override + public void refreshView() { + if (boxRoot == null || getOwnActivity() == null) { + return; + } + + boxCustom.setMaxWidth(getMaxWidth()); + boxCustom.setMaxHeight(getMaxHeight()); + boxCustom.setMinimumWidth(getMinWidth()); + boxCustom.setMinimumHeight(getMinHeight()); + + boxRoot.setAutoUnsafePlacePadding(isEnableImmersiveMode()); + boxRoot.setRootPadding(screenPaddings[0], screenPaddings[1], screenPaddings[2], screenPaddings[3]); + if (baseView() != null) { + if (!initSetCustomViewLayoutListener) { + if (boxCustom != null) { + RelativeLayout.LayoutParams rlp; + rlp = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + boxCustom.setLayoutParams(rlp); + } + + Runnable onLayoutChangeRunnable = new Runnable() { + @Override + public void run() { + int baseViewLeft = baseViewLoc[0] - (int) boxRoot.getX(); + int baseViewTop = baseViewLoc[1] - (int) boxRoot.getY(); + int calX = 0, calY = 0; + if (alignViewGravity != -1) { + if (isAlignBaseViewGravity(Gravity.CENTER_VERTICAL)) { + calY = (baseViewTop + baseView().getMeasuredHeight() / 2 - boxCustom.getHeight() / 2); + } + if (isAlignBaseViewGravity(Gravity.CENTER_HORIZONTAL)) { + calX = (baseViewLeft + baseView().getMeasuredWidth() / 2 - boxCustom.getWidth() / 2); + } + if (isAlignBaseViewGravity(Gravity.CENTER)) { + calX = (baseViewLeft + baseView().getMeasuredWidth() / 2 - boxCustom.getWidth() / 2); + calY = (baseViewTop + baseView().getMeasuredHeight() / 2 - boxCustom.getHeight() / 2); + } + + if (isAlignBaseViewGravity(Gravity.TOP)) { + calY = baseViewTop - boxCustom.getHeight() - marginRelativeBaseView[3]; + } + if (isAlignBaseViewGravity(Gravity.LEFT)) { + calX = baseViewLeft - boxCustom.getWidth() - marginRelativeBaseView[2]; + } + if (isAlignBaseViewGravity(Gravity.RIGHT)) { + calX = baseViewLeft + baseView().getWidth() + marginRelativeBaseView[0]; + } + if (isAlignBaseViewGravity(Gravity.BOTTOM)) { + calY = baseViewTop + baseView().getHeight() + marginRelativeBaseView[1]; + } + int widthCache = width == 0 ? baseView().getWidth() : width; + int heightCache = height == 0 ? baseView().getHeight() : height; + baseViewLoc[2] = widthCache > 0 ? widthCache : baseViewLoc[2]; + baseViewLoc[3] = heightCache > 0 ? heightCache : baseViewLoc[3]; + + if (calX != 0 && calX != boxCustom.getX()) boxCustom.setX(calX); + if (calY != 0 && calY != boxCustom.getY()) boxCustom.setY(calY); + + onGetBaseViewLoc(baseViewLoc); + } + } + }; + + viewTreeObserver = boxCustom.getViewTreeObserver(); + viewTreeObserver.addOnPreDrawListener(baseViewDrawListener = new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + int[] baseViewLocCache = new int[2]; + if (baseView() != null) { + baseView().getLocationInWindow(baseViewLocCache); + if (getDialogImpl() != null && isShow && baseView().getVisibility() == View.VISIBLE) { + if (baseViewLocCache[0] != 0) { + baseViewLoc[0] = baseViewLocCache[0]; + } + if (baseViewLocCache[1] != 0) { + baseViewLoc[1] = baseViewLocCache[1]; + } + onLayoutChangeRunnable.run(); + } + } else { + removeDrawListener(viewTreeObserver, this); + viewTreeObserver = null; + baseViewDrawListener = null; + } + return true; + } + }); + initSetCustomViewLayoutListener = true; + } + } else { + if (boxCustom != null) { + RelativeLayout.LayoutParams rlp; + rlp = ((RelativeLayout.LayoutParams) boxCustom.getLayoutParams()); + if (rlp == null || (alignCache != null && alignCache != align)) { + rlp = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + } + switch (align) { + case TOP_LEFT: + case LEFT_TOP: + rlp.removeRule(RelativeLayout.CENTER_IN_PARENT); + rlp.addRule(RelativeLayout.ALIGN_PARENT_TOP); + rlp.addRule(RelativeLayout.ALIGN_PARENT_LEFT); + break; + case TOP: + case TOP_CENTER: + rlp.removeRule(RelativeLayout.CENTER_IN_PARENT); + rlp.addRule(RelativeLayout.ALIGN_PARENT_TOP); + rlp.addRule(RelativeLayout.CENTER_HORIZONTAL); + break; + case TOP_RIGHT: + case RIGHT_TOP: + rlp.removeRule(RelativeLayout.CENTER_IN_PARENT); + rlp.addRule(RelativeLayout.ALIGN_PARENT_TOP); + rlp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + break; + case BOTTOM_LEFT: + case LEFT_BOTTOM: + rlp.removeRule(RelativeLayout.CENTER_IN_PARENT); + rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + break; + case BOTTOM: + case BOTTOM_CENTER: + rlp.removeRule(RelativeLayout.CENTER_IN_PARENT); + rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + rlp.addRule(RelativeLayout.CENTER_HORIZONTAL); + break; + case BOTTOM_RIGHT: + case RIGHT_BOTTOM: + rlp.removeRule(RelativeLayout.CENTER_IN_PARENT); + rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + rlp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + break; + case CENTER: + rlp.removeRule(RelativeLayout.ALIGN_PARENT_TOP); + rlp.removeRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + rlp.addRule(RelativeLayout.CENTER_IN_PARENT); + break; + case LEFT: + case LEFT_CENTER: + rlp.removeRule(RelativeLayout.CENTER_IN_PARENT); + rlp.addRule(RelativeLayout.ALIGN_PARENT_TOP); + rlp.addRule(RelativeLayout.CENTER_VERTICAL); + break; + case RIGHT: + case RIGHT_CENTER: + rlp.removeRule(RelativeLayout.CENTER_IN_PARENT); + rlp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + rlp.addRule(RelativeLayout.CENTER_VERTICAL); + break; + } + alignCache = align; + boxCustom.setLayoutParams(rlp); + } + } + + if (bkgInterceptTouch) { + if (isCancelable()) { + boxRoot.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onBackgroundMaskClickListener == null || !onBackgroundMaskClickListener.onClick(me, v)) { + doDismiss(v); + } + } + }); + } else { + boxRoot.setOnClickListener(null); + } + } else { + boxRoot.setClickable(false); + } + + if (onBindView != null && onBindView.getCustomView() != null && boxCustom != null) { + onBindView.bindParent(boxCustom, me); + } + + if (boxCustom != null) { + if (width != -1) { + boxCustom.setMaxWidth(width); + boxCustom.setMinimumWidth(width); + } + + if (height != -1) { + boxCustom.setMaxHeight(height); + boxCustom.setMinimumHeight(height); + } + } + + boxRoot.setBackgroundColor(getMaskColor()); + + onDialogRefreshUI(); + } + + @Override + public void doDismiss(View v) { + if (CustomDialog.this.preDismiss(CustomDialog.this)){ + return; + } + if (v != null) v.setEnabled(false); + if (!dismissAnimFlag && boxCustom != null) { + dismissAnimFlag = true; + boxCustom.post(new Runnable() { + @Override + public void run() { + getDialogXAnimImpl().doExitAnim(CustomDialog.this, boxCustom); + runOnMainDelay(new Runnable() { + @Override + public void run() { + if (boxRoot != null) boxRoot.setVisibility(View.GONE); + if (baseViewDrawListener != null) { + if (viewTreeObserver != null) { + removeDrawListener(viewTreeObserver, baseViewDrawListener); + } else { + if (boxCustom != null) { + removeDrawListener(boxCustom.getViewTreeObserver(), baseViewDrawListener); + } + } + baseViewDrawListener = null; + viewTreeObserver = null; + } + dismiss(getDialogView()); + } + }, getExitAnimationDuration(null)); + } + }); + } + } + + protected DialogXAnimInterface getDialogXAnimImpl() { + if (dialogXAnimImpl == null) { + dialogXAnimImpl = new DialogXAnimInterface() { + @Override + public void doShowAnim(CustomDialog customDialog, ViewGroup dialogBodyView) { + if (getDialogImpl() == null || getDialogImpl().boxCustom == null) { + return; + } + Animation enterAnim = getEnterAnimation(); + long enterAnimationDuration = getEnterAnimationDuration(enterAnim); + enterAnim.setDuration(enterAnimationDuration); + if (boxCustom != null) { + boxCustom.setVisibility(View.VISIBLE); + boxCustom.startAnimation(enterAnim); + } + + if (maskColor != Color.TRANSPARENT) boxRoot.setBackgroundColor(maskColor); + + ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f); + bkgAlpha.setDuration(enterAnimationDuration); + bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + boxRoot.setBkgAlpha((Float) animation.getAnimatedValue()); + } + }); + bkgAlpha.start(); + } + + @Override + public void doExitAnim(CustomDialog customDialog, ViewGroup dialogBodyView) { + if (getDialogImpl() == null || getDialogImpl().boxCustom == null) { + return; + } + int exitAnimResIdTemp = R.anim.anim_dialogx_default_exit; + if (overrideExitAnimRes != 0) { + exitAnimResIdTemp = overrideExitAnimRes; + } + if (exitAnimResId != 0) { + exitAnimResIdTemp = exitAnimResId; + } + + long exitAnimDurationTemp; + if (boxCustom != null) { + Animation exitAnim = AnimationUtils.loadAnimation(getOwnActivity() == null ? boxCustom.getContext() : getOwnActivity(), exitAnimResIdTemp); + exitAnimDurationTemp = getExitAnimationDuration(exitAnim); + exitAnim.setDuration(exitAnimDurationTemp); + boxCustom.startAnimation(exitAnim); + } else { + exitAnimDurationTemp = getExitAnimationDuration(null); + } + + ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1f, 0f); + bkgAlpha.setDuration(exitAnimDurationTemp); + bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + boxRoot.setBkgAlpha((Float) animation.getAnimatedValue()); + } + }); + bkgAlpha.start(); + } + }; + } + return dialogXAnimImpl; + } + + public long getExitAnimationDuration(@Nullable Animation defaultExitAnim) { + if (defaultExitAnim == null && boxCustom.getAnimation() != null) { + defaultExitAnim = boxCustom.getAnimation(); + } + long exitAnimDurationTemp = (defaultExitAnim == null || defaultExitAnim.getDuration() == 0) ? 300 : defaultExitAnim.getDuration(); + if (overrideExitDuration >= 0) { + exitAnimDurationTemp = overrideExitDuration; + } + if (exitAnimDuration != -1) { + exitAnimDurationTemp = exitAnimDuration; + } + return exitAnimDurationTemp; + } + + public long getEnterAnimationDuration(@Nullable Animation defaultEnterAnim) { + if (defaultEnterAnim == null && boxCustom.getAnimation() != null) { + defaultEnterAnim = boxCustom.getAnimation(); + } + long enterAnimDurationTemp = (defaultEnterAnim == null || defaultEnterAnim.getDuration() == 0) ? 300 : defaultEnterAnim.getDuration(); + if (overrideEnterDuration >= 0) { + enterAnimDurationTemp = overrideEnterDuration; + } + if (enterAnimDuration >= 0) { + enterAnimDurationTemp = enterAnimDuration; + } + return enterAnimDurationTemp; + } + } + + private void removeDrawListener(ViewTreeObserver viewTreeObserver, ViewTreeObserver.OnPreDrawListener listener) { + if (viewTreeObserver == null || listener == null || !viewTreeObserver.isAlive()) { + return; + } + try { + viewTreeObserver.removeOnPreDrawListener(listener); + } catch (Exception e) { + } + } + + private Animation getEnterAnimation() { + Animation enterAnim; + if (enterAnimResId == R.anim.anim_dialogx_default_enter && + exitAnimResId == R.anim.anim_dialogx_default_exit && + baseView() == null) { + switch (align) { + case TOP: + case TOP_CENTER: + case TOP_LEFT: + case TOP_RIGHT: + enterAnimResId = R.anim.anim_dialogx_top_enter; + exitAnimResId = R.anim.anim_dialogx_top_exit; + break; + case BOTTOM: + case BOTTOM_CENTER: + case BOTTOM_LEFT: + case BOTTOM_RIGHT: + enterAnimResId = R.anim.anim_dialogx_bottom_enter; + exitAnimResId = R.anim.anim_dialogx_bottom_exit; + break; + case LEFT: + case LEFT_CENTER: + case LEFT_TOP: + case LEFT_BOTTOM: + enterAnimResId = R.anim.anim_dialogx_left_enter; + exitAnimResId = R.anim.anim_dialogx_left_exit; + break; + case RIGHT: + case RIGHT_CENTER: + case RIGHT_TOP: + case RIGHT_BOTTOM: + enterAnimResId = R.anim.anim_dialogx_right_enter; + exitAnimResId = R.anim.anim_dialogx_right_exit; + break; + } + enterAnim = AnimationUtils.loadAnimation(getOwnActivity(), enterAnimResId); + enterAnim.setInterpolator(new DecelerateInterpolator(2f)); + } else { + int enterAnimResIdTemp = R.anim.anim_dialogx_default_enter; + if (overrideEnterAnimRes != 0) { + enterAnimResIdTemp = overrideEnterAnimRes; + } + if (enterAnimResId != 0) { + enterAnimResIdTemp = enterAnimResId; + } + enterAnim = AnimationUtils.loadAnimation(getOwnActivity(), enterAnimResIdTemp); + } + long enterAnimDurationTemp = enterAnim.getDuration(); + if (overrideEnterDuration >= 0) { + enterAnimDurationTemp = overrideEnterDuration; + } + if (enterAnimDuration >= 0) { + enterAnimDurationTemp = enterAnimDuration; + } + enterAnim.setDuration(enterAnimDurationTemp); + return enterAnim; + } + + protected void onGetBaseViewLoc(int[] baseViewLoc) { + } + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + public void refreshUI() { + if (getDialogImpl() == null) return; + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl != null) dialogImpl.refreshView(); + } + }); + } + + public void dismiss() { + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl == null) return; + dialogImpl.doDismiss(null); + } + }); + } + + public DialogLifecycleCallback getDialogLifecycleCallback() { + return dialogLifecycleCallback == null ? new DialogLifecycleCallback() { + } : dialogLifecycleCallback; + } + + public CustomDialog setDialogLifecycleCallback(DialogLifecycleCallback dialogLifecycleCallback) { + this.dialogLifecycleCallback = dialogLifecycleCallback; + if (isShow) dialogLifecycleCallback.onShow(me); + return this; + } + + public OnBackPressedListener getOnBackPressedListener() { + return (OnBackPressedListener) onBackPressedListener; + } + + public CustomDialog setOnBackPressedListener(OnBackPressedListener onBackPressedListener) { + this.onBackPressedListener = onBackPressedListener; + refreshUI(); + return this; + } + + public CustomDialog setStyle(DialogXStyle style) { + this.style = style; + return this; + } + + public CustomDialog setTheme(DialogX.THEME theme) { + this.theme = theme; + return this; + } + + public boolean isCancelable() { + if (privateCancelable != null) { + return privateCancelable == BOOLEAN.TRUE; + } + if (overrideCancelable != null) { + return overrideCancelable == BOOLEAN.TRUE; + } + return cancelable; + } + + public CustomDialog setCancelable(boolean cancelable) { + this.privateCancelable = cancelable ? BOOLEAN.TRUE : BOOLEAN.FALSE; + refreshUI(); + return this; + } + + public CustomDialog.DialogImpl getDialogImpl() { + return dialogImpl; + } + + public CustomDialog setCustomView(OnBindView onBindView) { + this.onBindView = onBindView; + refreshUI(); + return this; + } + + public View getCustomView() { + if (onBindView == null) return null; + return onBindView.getCustomView(); + } + + public CustomDialog removeCustomView() { + this.onBindView.clean(); + refreshUI(); + return this; + } + + public int getEnterAnimResId() { + return enterAnimResId; + } + + public CustomDialog setEnterAnimResId(int enterAnimResId) { + this.enterAnimResId = enterAnimResId; + return this; + } + + public int getExitAnimResId() { + return exitAnimResId; + } + + public CustomDialog setExitAnimResId(int exitAnimResId) { + this.exitAnimResId = exitAnimResId; + return this; + } + + public CustomDialog setAnimResId(int enterAnimResId, int exitAnimResId) { + this.enterAnimResId = enterAnimResId; + this.exitAnimResId = exitAnimResId; + return this; + } + + public ALIGN getAlign() { + return align; + } + + public CustomDialog setAlign(ALIGN align) { + this.align = align; + refreshUI(); + return this; + } + + public boolean isAutoUnsafePlacePadding() { + return isEnableImmersiveMode(); + } + + /** + * 改为使用 .setEnableImmersiveMode(boolean) 来控制是否适配沉浸式 + * + * @param autoUnsafePlacePadding 是否适配沉浸式 + * @return CustomDialog + */ + @Deprecated + public CustomDialog setAutoUnsafePlacePadding(boolean autoUnsafePlacePadding) { + setEnableImmersiveMode(autoUnsafePlacePadding); + return this; + } + + /** + * 改为使用 .setEnableImmersiveMode(boolean) 来控制是否适配沉浸式 + * + * @param fullscreen 是否适配沉浸式 + * @return CustomDialog + */ + @Deprecated + public CustomDialog setFullScreen(boolean fullscreen) { + setEnableImmersiveMode(!fullscreen); + return this; + } + + public CustomDialog setMaskColor(@ColorInt int maskColor) { + this.maskColor = maskColor; + refreshUI(); + return this; + } + + public int getMaskColor() { + return maskColor; + } + + public long getEnterAnimDuration() { + return enterAnimDuration; + } + + public CustomDialog setEnterAnimDuration(long enterAnimDuration) { + this.enterAnimDuration = enterAnimDuration; + return this; + } + + public long getExitAnimDuration() { + return exitAnimDuration; + } + + public CustomDialog setExitAnimDuration(long exitAnimDuration) { + this.exitAnimDuration = exitAnimDuration; + return this; + } + + @Override + public void restartDialog() { + if (getDialogView() != null) { + if (getDialogImpl() != null && getDialogImpl().boxCustom != null) { + if (baseViewDrawListener != null) { + if (viewTreeObserver != null) { + removeDrawListener(viewTreeObserver, baseViewDrawListener); + } else { + if (getDialogImpl().boxCustom != null) { + removeDrawListener(getDialogImpl().boxCustom.getViewTreeObserver(), baseViewDrawListener); + } + } + baseViewDrawListener = null; + viewTreeObserver = null; + } + } + dismiss(getDialogView()); + isShow = false; + } + if (getDialogImpl() != null && getDialogImpl().boxCustom != null) { + getDialogImpl().boxCustom.removeAllViews(); + } + + enterAnimDuration = 0; + View dialogView = createView(R.layout.layout_dialogx_custom); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + } + + public void hide() { + isHide = true; + hideWithExitAnim = false; + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + } + + protected boolean hideWithExitAnim; + + public void hideWithExitAnim() { + hideWithExitAnim = true; + isHide = true; + if (getDialogImpl() != null) { + getDialogImpl().getDialogXAnimImpl().doExitAnim(CustomDialog.this, getDialogImpl().boxCustom); + + runOnMainDelay(new Runnable() { + @Override + public void run() { + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + } + }, getDialogImpl().getExitAnimationDuration(null)); + } + } + + @Override + protected void shutdown() { + dismiss(); + } + + public CustomDialog setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public boolean isBkgInterceptTouch() { + return bkgInterceptTouch; + } + + public CustomDialog setBkgInterceptTouch(boolean bkgInterceptTouch) { + this.bkgInterceptTouch = bkgInterceptTouch; + refreshUI(); + return this; + } + + public int getAlignBaseViewGravity() { + return alignViewGravity; + } + + /** + * 判断是否有设置对应的位置关系 + * + * @param gravity 位置关系 + * @return 是否具备位置关系 + */ + public boolean isAlignBaseViewGravity(int gravity) { + return (alignViewGravity & gravity) == gravity; + } + + public CustomDialog setAlignBaseViewGravity(View baseView, int alignGravity) { + this.baseView(baseView); + this.alignViewGravity = alignGravity; + baseViewLoc = new int[4]; + baseView.getLocationInWindow(baseViewLoc); + setFullScreen(true); + return this; + } + + public CustomDialog setAlignBaseView(View baseView) { + this.baseView(baseView); + baseViewLoc = new int[4]; + baseView.getLocationInWindow(baseViewLoc); + setFullScreen(true); + return this; + } + + public CustomDialog setAlignBaseViewGravity(int alignGravity) { + this.alignViewGravity = alignGravity; + if (baseView() != null) { + baseViewLoc = new int[4]; + baseView().getLocationInWindow(baseViewLoc); + } + setFullScreen(true); + return this; + } + + public CustomDialog setAlignBaseViewGravity(View baseView, int alignGravity, int marginLeft, + int marginTop, int marginRight, int marginBottom) { + this.marginRelativeBaseView = new int[]{marginLeft, marginTop, marginRight, marginBottom}; + refreshUI(); + return setAlignBaseViewGravity(baseView, alignGravity); + } + + public int[] getBaseViewMargin() { + return marginRelativeBaseView; + } + + public CustomDialog setBaseViewMargin(int[] marginRelativeBaseView) { + this.marginRelativeBaseView = marginRelativeBaseView; + refreshUI(); + return this; + } + + public CustomDialog setBaseViewMargin(int marginLeft, int marginTop, + int marginRight, int marginBottom) { + this.marginRelativeBaseView = new int[]{marginLeft, marginTop, marginRight, marginBottom}; + refreshUI(); + return this; + } + + public CustomDialog setBaseViewMarginLeft(int marginLeft) { + this.marginRelativeBaseView[0] = marginLeft; + refreshUI(); + return this; + } + + public CustomDialog setBaseViewMarginTop(int marginTop) { + this.marginRelativeBaseView[1] = marginTop; + refreshUI(); + return this; + } + + public CustomDialog setBaseViewMarginRight(int marginRight) { + this.marginRelativeBaseView[2] = marginRight; + refreshUI(); + return this; + } + + public CustomDialog setBaseViewMarginBottom(int marginBottom) { + this.marginRelativeBaseView[3] = marginBottom; + refreshUI(); + return this; + } + + public int getBaseViewMarginLeft(int marginLeft) { + return this.marginRelativeBaseView[0]; + } + + public int getBaseViewMarginTop(int marginLeft) { + return this.marginRelativeBaseView[1]; + } + + public int getBaseViewMarginRight(int marginLeft) { + return this.marginRelativeBaseView[2]; + } + + public int getBaseViewMarginBottom(int marginLeft) { + return this.marginRelativeBaseView[3]; + } + + public View getBaseView() { + return baseView(); + } + + public int getWidth() { + return width; + } + + /** + * 设置对话框 UI 宽度(单位:像素) + * + * @param width 宽度(像素) + * @return CustomDialog实例 + */ + public CustomDialog setWidth(int width) { + this.width = width; + refreshUI(); + return this; + } + + public int getHeight() { + return height; + } + + /** + * 设置对话框 UI 高度(单位:像素) + * + * @param height 高度(像素) + * @return CustomDialog实例 + */ + public CustomDialog setHeight(int height) { + this.height = height; + refreshUI(); + return this; + } + + public OnBackgroundMaskClickListener getOnBackgroundMaskClickListener() { + return onBackgroundMaskClickListener; + } + + public CustomDialog setOnBackgroundMaskClickListener(OnBackgroundMaskClickListener onBackgroundMaskClickListener) { + this.onBackgroundMaskClickListener = onBackgroundMaskClickListener; + return this; + } + + public DialogXAnimInterface getDialogXAnimImpl() { + return dialogXAnimImpl; + } + + public CustomDialog setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public CustomDialog setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public CustomDialog setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new CustomDialog() { + * + * @param dialog self + * @Override public void onShow(CustomDialog dialog) { + * //... + * } + * } + */ + protected void onShow(CustomDialog dialog) { + + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new CustomDialog() { + * + * @param dialog self + * @Override public boolean onDismiss(CustomDialog dialog) { + * WaitDialog.show("Please Wait..."); + * if (dialog.getButtonSelectResult() == BUTTON_SELECT_RESULT.BUTTON_OK) { + * //点击了OK的情况 + * //... + * } else { + * //其他按钮点击、对话框dismiss的情况 + * //... + * } + * return false; + * } + * } + */ + //用于使用 new 构建实例时,override 的生命周期事件 + protected void onDismiss(CustomDialog dialog) { + + } + + protected CustomDialog baseView(View view) { + if (view == null && baseViewWeakReference != null) { + baseViewWeakReference.clear(); + baseViewWeakReference = null; + } else { + baseViewWeakReference = new WeakReference<>(view); + } + return this; + } + + protected View baseView() { + return baseViewWeakReference == null ? null : baseViewWeakReference.get(); + } + + public CustomDialog setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public CustomDialog onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public CustomDialog onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public CustomDialog setEnableImmersiveMode(boolean enableImmersiveMode) { + this.enableImmersiveMode = enableImmersiveMode; + refreshUI(); + return this; + } + + public CustomDialog setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex("+orderIndex+") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public CustomDialog bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public CustomDialog setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public CustomDialog cleanAction(int actionId){ + dialogActionRunnableMap.remove(actionId); + return this; + } + + public CustomDialog cleanAllAction(){ + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + public void callDialogDismiss(){ + dismiss(); + } + + public CustomDialog bindDismissWithLifecycleOwner(LifecycleOwner owner){ + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } + + public CustomDialog setMaxWidth(int maxWidth) { + this.maxWidth = maxWidth; + refreshUI(); + return this; + } + + public CustomDialog setMaxHeight(int maxHeight) { + this.maxHeight = maxHeight; + refreshUI(); + return this; + } + + public CustomDialog setMinHeight(int minHeight) { + this.minHeight = minHeight; + refreshUI(); + return this; + } + + public CustomDialog setMinWidth(int minWidth) { + this.minWidth = minWidth; + refreshUI(); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/FullScreenDialog.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/FullScreenDialog.java new file mode 100644 index 0000000..a0606b7 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/FullScreenDialog.java @@ -0,0 +1,1024 @@ +package com.kongzue.dialogx.dialogs; + +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; + +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.app.Activity; +import android.graphics.Outline; +import android.graphics.Rect; +import android.graphics.drawable.GradientDrawable; +import android.os.Build; +import android.view.RoundedCorner; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.view.WindowInsets; +import android.view.animation.DecelerateInterpolator; +import android.widget.RelativeLayout; + +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.interfaces.DialogConvertViewInterface; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXBaseBottomDialog; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.OnBackPressedListener; +import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener; +import com.kongzue.dialogx.interfaces.OnBindView; +import com.kongzue.dialogx.interfaces.OnSafeInsetsChangeListener; +import com.kongzue.dialogx.interfaces.ScrollController; +import com.kongzue.dialogx.util.FullScreenDialogTouchEventInterceptor; +import com.kongzue.dialogx.util.views.ActivityScreenShotImageView; +import com.kongzue.dialogx.util.views.DialogXBaseRelativeLayout; +import com.kongzue.dialogx.util.views.MaxRelativeLayout; + +import java.lang.reflect.Method; +import java.util.HashMap; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/6 15:17 + */ +public class FullScreenDialog extends BaseDialog implements DialogXBaseBottomDialog { + + public static final int ACTIVITY_CONTENT_RADIUS_KEEP = -2; + public static final int ACTIVITY_CONTENT_RADIUS_DEFAULT = -1; + + public static int overrideEnterDuration = -1; + public static int overrideExitDuration = -1; + public static BOOLEAN overrideCancelable; + + protected OnBindView onBindView; + protected OnBackPressedListener onBackPressedListener; + protected BOOLEAN privateCancelable; + protected boolean hideZoomBackground; + protected float backgroundRadius = DialogX.defaultFullScreenDialogBackgroundRadius; + protected float activityContentRadius = ACTIVITY_CONTENT_RADIUS_DEFAULT; + protected boolean allowInterceptTouch = true; + protected DialogXAnimInterface dialogXAnimImpl; + protected boolean bottomNonSafetyAreaBySelf = false; + protected boolean hideActivityContentView; + protected Integer maskColor = null; + + protected DialogLifecycleCallback dialogLifecycleCallback; + protected OnBackgroundMaskClickListener onBackgroundMaskClickListener; + + protected FullScreenDialog me = this; + + protected FullScreenDialog() { + super(); + } + + public static FullScreenDialog build() { + return new FullScreenDialog(); + } + + public static FullScreenDialog build(OnBindView onBindView) { + return new FullScreenDialog(onBindView); + } + + public FullScreenDialog(OnBindView onBindView) { + this.onBindView = onBindView; + } + + public static FullScreenDialog show(OnBindView onBindView) { + FullScreenDialog FullScreenDialog = new FullScreenDialog(onBindView); + FullScreenDialog.show(); + return FullScreenDialog; + } + + public FullScreenDialog show() { + if (isHide && getDialogView() != null && isShow) { + if (hideWithExitAnim && getDialogImpl() != null) { + getDialogView().setVisibility(View.VISIBLE); + getDialogImpl().getDialogXAnimImpl().doShowAnim(me, getDialogImpl().bkg); + } else { + getDialogView().setVisibility(View.VISIBLE); + } + return this; + } + super.beforeShow(); + if (getDialogView() == null) { + View dialogView = createView(isLightTheme() ? R.layout.layout_dialogx_fullscreen : R.layout.layout_dialogx_fullscreen_dark); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + } else { + show(getDialogView()); + } + return this; + } + + public void show(Activity activity) { + super.beforeShow(); + if (getDialogView() == null) { + View dialogView = createView(isLightTheme() ? R.layout.layout_dialogx_fullscreen : R.layout.layout_dialogx_fullscreen_dark); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(activity, dialogView); + } else { + show(activity, getDialogView()); + } + } + + protected DialogImpl dialogImpl; + + public class DialogImpl implements DialogConvertViewInterface { + + private FullScreenDialogTouchEventInterceptor fullScreenDialogTouchEventInterceptor; + + public ActivityScreenShotImageView imgZoomActivity; + public DialogXBaseRelativeLayout boxRoot; + public RelativeLayout boxBkg; + public MaxRelativeLayout bkg; + public RelativeLayout boxCustom; + public ScrollController scrollView; + + public DialogImpl setScrollView(ScrollController scrollView) { + this.scrollView = scrollView; + return this; + } + + public DialogImpl(View convertView) { + if (convertView == null) return; + setDialogView(convertView); + imgZoomActivity = convertView.findViewById(R.id.img_zoom_activity); + boxRoot = convertView.findViewById(R.id.box_root); + boxBkg = convertView.findViewById(R.id.box_bkg); + bkg = convertView.findViewById(R.id.bkg); + boxCustom = convertView.findViewById(R.id.box_custom); + imgZoomActivity.hideActivityContentView = hideActivityContentView; + + imgZoomActivity.bindDialog(FullScreenDialog.this); + + if (hideZoomBackground) { + convertView.setBackgroundResource(R.color.black20); + imgZoomActivity.setVisibility(View.GONE); + } else { + convertView.setBackgroundResource(R.color.black); + imgZoomActivity.setVisibility(View.VISIBLE); + } + init(); + dialogImpl = this; + refreshView(); + } + + public float bkgEnterAimY = -1; + protected int enterY; + + private Rect mUnsafeRect = new Rect(0, 0, 0, 0); + + public float getEnterY() { + return Math.max(0, boxRoot.getSafeHeight() - enterY); + } + + @Override + public void init() { + boxRoot.setParentDialog(me); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(getThisOrderIndex()); + } + + boxRoot.setOnLifecycleCallBack(new DialogXBaseRelativeLayout.OnLifecycleCallBack() { + @Override + public void onShow() { + isShow = true; + preShow = false; + + setLifecycleState(Lifecycle.State.CREATED); + onDialogShow(); + + getDialogLifecycleCallback().onShow(me); + FullScreenDialog.this.onShow(me); + } + + @Override + public void onDismiss() { + isShow = false; + getDialogLifecycleCallback().onDismiss(me); + FullScreenDialog.this.onDismiss(me); + setLifecycleState(Lifecycle.State.DESTROYED); + fullScreenDialogTouchEventInterceptor = null; + dialogImpl = null; + dialogLifecycleCallback = null; + System.gc(); + } + }); + + boxRoot.setOnBackPressedListener(new DialogXBaseRelativeLayout.PrivateBackPressedListener() { + @Override + public boolean onBackPressed() { + if (onBackPressedListener != null) { + if (onBackPressedListener.onBackPressed(me)) { + dismiss(); + } + } else { + if (isCancelable()) { + dismiss(); + } + } + return true; + } + }); + + fullScreenDialogTouchEventInterceptor = new FullScreenDialogTouchEventInterceptor(me, dialogImpl); + boxRoot.setBkgAlpha(0f); + + boxRoot.post(new Runnable() { + @Override + public void run() { + bkg.setY(boxRoot.getHeight()); + getDialogXAnimImpl().doShowAnim(me, bkg); + setLifecycleState(Lifecycle.State.RESUMED); + } + }); + boxRoot.setOnSafeInsetsChangeListener(new OnSafeInsetsChangeListener() { + @Override + public void onChange(Rect unsafeRect) { + mUnsafeRect.set(unsafeRect); + makeEnterY(); + if (!enterAnimRunning && getEnterY() != 0) { + bkg.setY(getEnterY()); + } + } + }); + + bkg.setOnYChanged(new MaxRelativeLayout.OnYChanged() { + @Override + public void y(float y) { + float realY = y + bkg.getTop(); + float zoomScale = 1 - (boxRoot.getHeight() - realY) * 0.00002f; + if (zoomScale > 1) zoomScale = 1; + if (!hideZoomBackground) { + imgZoomActivity.setScale(zoomScale); + imgZoomActivity.setRadius( + getActivityZoomRadius(getDeviceRadius(), getActivityContentRadius(), ((boxRoot.getHeight() - realY) / boxRoot.getHeight())) + ); + } + } + }); + + /** + * 给自定义布局增加监听,如果布局高度发生改变,则重新计算位置,位置发生变化,则再次使用动画移动布局到指定位置 + * 目的是给自定义布局高度为wrap_content的用于纠正布局的Y轴位置 + */ + boxCustom.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { + int oldHeight = oldBottom - oldTop; + int newHeight = bottom - top; + if (oldHeight != newHeight) { + /** + * 高度发生改变 + * 这里判断是否在两种动画途中 + */ + if (!enterAnimRunning && !boxRoot.getFitSystemBarUtils().isInSmoothingPadding()) { + makeEnterY(); + float newBkgEnterAimY = boxRoot.getSafeHeight() - mUnsafeRect.bottom - enterY - boxRoot.getUnsafePlace().top; + if (newBkgEnterAimY < 0) newBkgEnterAimY = 0; + if (newBkgEnterAimY != bkgEnterAimY && bkg.getY() != newBkgEnterAimY) { + float oldVal = bkgEnterAimY; + bkgEnterAimY = newBkgEnterAimY; + //需要重新定义终点 + doShowAnimRepeat((int) oldVal, (int) newBkgEnterAimY, true); + } else if (bkg.getY() != newBkgEnterAimY && newBkgEnterAimY != 0) { + bkg.setY(newBkgEnterAimY); + } + } + } + } + }); + onDialogInit(); + } + + private boolean isMatchParentHeightCustomView() { + if (onBindView != null && onBindView.getCustomView() != null) { + ViewGroup.LayoutParams lp = onBindView.getCustomView().getLayoutParams(); + if (lp != null) { + return lp.height == MATCH_PARENT; + } + } + return false; + } + + private void makeEnterY() { + int customViewHeight = boxCustom.getHeight(); + + if (customViewHeight == 0 || isMatchParentHeightCustomView()) { + customViewHeight = ((int) boxRoot.getSafeHeight()); + } + enterY = customViewHeight; + } + + @Override + public void refreshView() { + if (boxRoot == null || getOwnActivity() == null) { + return; + } + boxRoot.setAutoUnsafePlacePadding(isEnableImmersiveMode()); + boxRoot.setRootPadding(screenPaddings[0], screenPaddings[1], screenPaddings[2], screenPaddings[3]); + if (backgroundColor != null) { + tintColor(bkg, backgroundColor); + } + + bkg.setMaxWidth(getMaxWidth()); + bkg.setMaxHeight(getMaxHeight()); + bkg.setMinimumWidth(getMinWidth()); + bkg.setMinimumHeight(getMinHeight()); + + if (isCancelable()) { + boxRoot.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onBackgroundMaskClickListener == null || !onBackgroundMaskClickListener.onClick(me, v)) { + doDismiss(v); + } + } + }); + } else { + boxRoot.setOnClickListener(null); + } + if (backgroundRadius > -1) { + if (bkg.getBackground() instanceof GradientDrawable) { + GradientDrawable gradientDrawable = (GradientDrawable) bkg.getBackground(); + if (gradientDrawable != null) gradientDrawable.setCornerRadii(new float[]{ + backgroundRadius, backgroundRadius, backgroundRadius, backgroundRadius, 0, 0, 0, 0 + }); + } + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + bkg.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(0, 0, view.getWidth(), (int) (view.getHeight() + backgroundRadius), backgroundRadius); + } + }); + bkg.setClipToOutline(true); + } + } + if (maskColor != null) { + boxRoot.setBackgroundColor(maskColor); + } + + if (onBindView != null) { + onBindView.bindParent(boxCustom, me); + if (onBindView.getCustomView() instanceof ScrollController) { + scrollView = (ScrollController) onBindView.getCustomView(); + } else { + View scrollController = onBindView.getCustomView().findViewWithTag("ScrollController"); + if (scrollController instanceof ScrollController) { + scrollView = (ScrollController) scrollController; + } + } + } + + if (hideZoomBackground) { + getDialogView().setBackgroundResource(R.color.black20); + imgZoomActivity.setVisibility(View.GONE); + } else { + getDialogView().setBackgroundResource(R.color.black); + imgZoomActivity.setVisibility(View.VISIBLE); + } + + fullScreenDialogTouchEventInterceptor.refresh(me, this); + + onDialogRefreshUI(); + } + + @Override + public void doDismiss(View v) { + if (FullScreenDialog.this.preDismiss(FullScreenDialog.this)) { + return; + } + if (v != null) v.setEnabled(false); + if (getOwnActivity() == null) return; + + if (!dismissAnimFlag && getDialogXAnimImpl() != null) { + dismissAnimFlag = true; + getDialogXAnimImpl().doExitAnim(me, bkg); + + runOnMainDelay(new Runnable() { + @Override + public void run() { + if (boxRoot != null) { + boxRoot.setVisibility(View.GONE); + } + dismiss(getDialogView()); + } + }, getExitAnimationDuration()); + } + } + + public void preDismiss() { + if (isCancelable()) { + doDismiss(boxRoot); + } else { + long exitAnimDurationTemp = 300; + if (overrideExitDuration >= 0) { + exitAnimDurationTemp = overrideExitDuration; + } + if (exitAnimDuration >= 0) { + exitAnimDurationTemp = exitAnimDuration; + } + + ObjectAnimator exitAnim = ObjectAnimator.ofFloat(bkg, "y", bkg.getY(), bkgEnterAimY); + exitAnim.setDuration(exitAnimDurationTemp); + exitAnim.start(); + } + } + + private boolean enterAnimRunning = true; + + /** + * 弹窗显示的动画 + * 动画执行途中实时检测终点是否改变,如果改变则中断这次动画重新设置新终点的动画并执行 + * + * @param start 起点位置 + * @param end 终点位置 + */ + private void doShowAnimRepeat(int start, int end, boolean isRepeat) { + enterAnimRunning = true; + long enterAnimDurationTemp = getEnterAnimationDuration(); + + ValueAnimator enterAnimVal = ValueAnimator.ofInt(start, end); + enterAnimVal.setDuration(enterAnimDurationTemp); + enterAnimVal.setInterpolator(new DecelerateInterpolator()); + enterAnimVal.addUpdateListener(animation -> { + int thisVal = (int) animation.getAnimatedValue(); + bkg.setY(thisVal); + + makeEnterY(); + float newBkgEnterAimY = boxRoot.getSafeHeight() - enterY; + if (newBkgEnterAimY < 0) newBkgEnterAimY = 0; + if (newBkgEnterAimY != bkgEnterAimY) { + bkgEnterAimY = newBkgEnterAimY; + //需要重新定义终点 + animation.cancel(); + doShowAnimRepeat(thisVal, (int) newBkgEnterAimY, true); + } else if (thisVal >= end) { + enterAnimRunning = false; + } + }); + enterAnimVal.start(); + bkg.setVisibility(View.VISIBLE); + + if (!isRepeat) { + ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f); + bkgAlpha.setDuration(enterAnimDurationTemp); + bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float value = (float) animation.getAnimatedValue(); + boxRoot.setBkgAlpha(value); + } + }); + bkgAlpha.start(); + } + } + + protected DialogXAnimInterface getDialogXAnimImpl() { + if (dialogXAnimImpl == null) { + dialogXAnimImpl = new DialogXAnimInterface() { + @Override + public void doShowAnim(FullScreenDialog dialog, ViewGroup dialogBodyView) { +// long enterAnimDurationTemp = getEnterAnimationDuration(); + makeEnterY(); + bkgEnterAimY = boxRoot.getSafeHeight() - enterY; + if (bkgEnterAimY < 0) bkgEnterAimY = 0; + //启动带监控终点位置变化的动画 + doShowAnimRepeat(boxRoot.getHeight(), (int) bkgEnterAimY, false); +// ObjectAnimator enterAnim = ObjectAnimator.ofFloat(bkg, "y", boxRoot.getHeight(), bkgEnterAimY); +// enterAnim.setDuration(enterAnimDurationTemp); +// enterAnim.setInterpolator(new DecelerateInterpolator()); +// enterAnim.start(); +// bkg.setVisibility(View.VISIBLE); +// +// ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f); +// bkgAlpha.setDuration(enterAnimDurationTemp); +// bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { +// @Override +// public void onAnimationUpdate(ValueAnimator animation) { +// float value = (float) animation.getAnimatedValue(); +// boxRoot.setBkgAlpha(value); +// enterAnimRunning = !(value == 1f); +// } +// }); +// bkgAlpha.start(); + } + + @Override + public void doExitAnim(FullScreenDialog dialog, ViewGroup dialogBodyView) { + long exitAnimDurationTemp = getExitAnimationDuration(); + + ObjectAnimator exitAnim = ObjectAnimator.ofFloat(bkg, "y", bkg.getY(), boxBkg.getHeight()); + exitAnim.setDuration(exitAnimDurationTemp); + exitAnim.start(); + + ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1f, 0f); + bkgAlpha.setDuration(exitAnimDurationTemp); + bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float value = (float) animation.getAnimatedValue(); + boxRoot.setBkgAlpha(value); + enterAnimRunning = !(value == 1f); + } + }); + bkgAlpha.start(); + } + }; + } + return dialogXAnimImpl; + } + + public long getExitAnimationDuration() { + long exitAnimDurationTemp = 300; + if (overrideExitDuration >= 0) { + exitAnimDurationTemp = overrideExitDuration; + } + if (exitAnimDuration != -1) { + exitAnimDurationTemp = exitAnimDuration; + } + return exitAnimDurationTemp; + } + + public long getEnterAnimationDuration() { + long enterAnimDurationTemp = 300; + if (overrideEnterDuration >= 0) { + enterAnimDurationTemp = overrideEnterDuration; + } + if (enterAnimDuration >= 0) { + enterAnimDurationTemp = enterAnimDuration; + } + return enterAnimDurationTemp; + } + } + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + public void refreshUI() { + if (getDialogImpl() == null) return; + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl != null) dialogImpl.refreshView(); + } + }); + } + + public void dismiss() { + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl == null) return; + dialogImpl.doDismiss(null); + } + }); + } + + public DialogLifecycleCallback getDialogLifecycleCallback() { + return dialogLifecycleCallback == null ? new DialogLifecycleCallback() { + } : dialogLifecycleCallback; + } + + public FullScreenDialog setDialogLifecycleCallback(DialogLifecycleCallback dialogLifecycleCallback) { + this.dialogLifecycleCallback = dialogLifecycleCallback; + if (isShow) dialogLifecycleCallback.onShow(me); + return this; + } + + public OnBackPressedListener getOnBackPressedListener() { + return (OnBackPressedListener) onBackPressedListener; + } + + public FullScreenDialog setOnBackPressedListener(OnBackPressedListener onBackPressedListener) { + this.onBackPressedListener = onBackPressedListener; + refreshUI(); + return this; + } + + public FullScreenDialog setStyle(DialogXStyle style) { + this.style = style; + return this; + } + + public FullScreenDialog setTheme(DialogX.THEME theme) { + this.theme = theme; + return this; + } + + public boolean isCancelable() { + if (privateCancelable != null) { + return privateCancelable == BOOLEAN.TRUE; + } + if (overrideCancelable != null) { + return overrideCancelable == BOOLEAN.TRUE; + } + return cancelable; + } + + public FullScreenDialog setCancelable(boolean cancelable) { + this.privateCancelable = cancelable ? BOOLEAN.TRUE : BOOLEAN.FALSE; + refreshUI(); + return this; + } + + public DialogImpl getDialogImpl() { + return dialogImpl; + } + + public FullScreenDialog setCustomView(OnBindView onBindView) { + this.onBindView = onBindView; + refreshUI(); + return this; + } + + public View getCustomView() { + if (onBindView == null) return null; + return onBindView.getCustomView(); + } + + public FullScreenDialog removeCustomView() { + this.onBindView.clean(); + refreshUI(); + return this; + } + + public int getBackgroundColor() { + return backgroundColor; + } + + public FullScreenDialog setBackgroundColor(@ColorInt int backgroundColor) { + this.backgroundColor = backgroundColor; + refreshUI(); + return this; + } + + public FullScreenDialog setBackgroundColorRes(@ColorRes int backgroundColorRes) { + this.backgroundColor = getColor(backgroundColorRes); + refreshUI(); + return this; + } + + public long getEnterAnimDuration() { + return enterAnimDuration; + } + + public FullScreenDialog setEnterAnimDuration(long enterAnimDuration) { + this.enterAnimDuration = enterAnimDuration; + return this; + } + + public long getExitAnimDuration() { + return exitAnimDuration; + } + + public FullScreenDialog setExitAnimDuration(long exitAnimDuration) { + this.exitAnimDuration = exitAnimDuration; + return this; + } + + public boolean isHideZoomBackground() { + return hideZoomBackground; + } + + public FullScreenDialog setHideZoomBackground(boolean hideZoomBackground) { + this.hideZoomBackground = hideZoomBackground; + refreshUI(); + return this; + } + + @Override + public void restartDialog() { + if (getDialogView() != null) { + dismiss(getDialogView()); + isShow = false; + } + if (getDialogImpl().boxCustom != null) { + getDialogImpl().boxCustom.removeAllViews(); + } + enterAnimDuration = 0; + View dialogView = createView(isLightTheme() ? R.layout.layout_dialogx_fullscreen : R.layout.layout_dialogx_fullscreen_dark); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + } + + public void hide() { + isHide = true; + hideWithExitAnim = false; + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + } + + protected boolean hideWithExitAnim; + + public void hideWithExitAnim() { + hideWithExitAnim = true; + isHide = true; + if (getDialogImpl() != null) { + getDialogImpl().getDialogXAnimImpl().doExitAnim(me, getDialogImpl().bkg); + runOnMainDelay(new Runnable() { + @Override + public void run() { + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + } + }, getDialogImpl().getExitAnimationDuration()); + } + } + + @Override + protected void shutdown() { + dismiss(); + } + + public FullScreenDialog setMaxWidth(int maxWidth) { + this.maxWidth = maxWidth; + refreshUI(); + return this; + } + + public FullScreenDialog setMaxHeight(int maxHeight) { + this.maxHeight = maxHeight; + refreshUI(); + return this; + } + + public FullScreenDialog setMinHeight(int minHeight) { + this.minHeight = minHeight; + refreshUI(); + return this; + } + + public FullScreenDialog setMinWidth(int minWidth) { + this.minWidth = minWidth; + refreshUI(); + return this; + } + + public FullScreenDialog setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public OnBackgroundMaskClickListener getOnBackgroundMaskClickListener() { + return onBackgroundMaskClickListener; + } + + public FullScreenDialog setOnBackgroundMaskClickListener(OnBackgroundMaskClickListener onBackgroundMaskClickListener) { + this.onBackgroundMaskClickListener = onBackgroundMaskClickListener; + return this; + } + + public FullScreenDialog setRadius(float radiusPx) { + backgroundRadius = radiusPx; + refreshUI(); + return this; + } + + public float getRadius() { + return backgroundRadius; + } + + public boolean isAllowInterceptTouch() { + return allowInterceptTouch; + } + + public FullScreenDialog setAllowInterceptTouch(boolean allowInterceptTouch) { + this.allowInterceptTouch = allowInterceptTouch; + refreshUI(); + return this; + } + + public DialogXAnimInterface getDialogXAnimImpl() { + return dialogXAnimImpl; + } + + public FullScreenDialog setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public FullScreenDialog setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public FullScreenDialog setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new FullScreenDialog() { + * + * @param dialog self + * @Override public void onShow(FullScreenDialog dialog) { + * //... + * } + * } + */ + protected void onShow(FullScreenDialog dialog) { + + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new FullScreenDialog() { + * + * @param dialog self + * @Override public boolean onDismiss(FullScreenDialog dialog) { + * WaitDialog.show("Please Wait..."); + * if (dialog.getButtonSelectResult() == BUTTON_SELECT_RESULT.BUTTON_OK) { + * //点击了OK的情况 + * //... + * } else { + * //其他按钮点击、对话框dismiss的情况 + * //... + * } + * return false; + * } + * } + */ + //用于使用 new 构建实例时,override 的生命周期事件 + protected void onDismiss(FullScreenDialog dialog) { + + } + + public boolean isBottomNonSafetyAreaBySelf() { + return bottomNonSafetyAreaBySelf; + } + + public FullScreenDialog setBottomNonSafetyAreaBySelf(boolean bottomNonSafetyAreaBySelf) { + this.bottomNonSafetyAreaBySelf = bottomNonSafetyAreaBySelf; + return this; + } + + /** + * 是否在显示 FullScreenDialog 时不对 activity 的界面内容进行渲染,这将提升一定的性能 + * 只可以在使用 build 方法构建且在执行show方法之前使用 + * 但这将引发一些问题,例如输入法弹出时 FullScreenDialog 无法上浮等 + * + * @param hideActivityContentView 是否显示 activity 的界面内容 + * @return this + */ + public FullScreenDialog hideActivityContentView(boolean hideActivityContentView) { + this.hideActivityContentView = hideActivityContentView; + return this; + } + + public FullScreenDialog setMaskColor(@ColorInt int maskColor) { + this.maskColor = maskColor; + refreshUI(); + return this; + } + + public float getActivityContentRadius() { + return activityContentRadius >= 0 ? activityContentRadius : activityContentRadius == ACTIVITY_CONTENT_RADIUS_KEEP ? getDeviceRadius() : (getRadius() >= 0 ? getRadius() : dip2px(15)); + } + + private Integer deviceRadiusCache; + + public int getDeviceRadius() { + if (deviceRadiusCache == null) { + deviceRadiusCache = 0; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { + WindowInsets rootInsets = getRootFrameLayout() == null ? publicWindowInsets() : getRootFrameLayout().getRootWindowInsets(); + if (rootInsets != null) { + RoundedCorner lT = rootInsets.getRoundedCorner(RoundedCorner.POSITION_TOP_LEFT); + RoundedCorner rT = rootInsets.getRoundedCorner(RoundedCorner.POSITION_TOP_RIGHT); + if (lT != null && rT != null) { + deviceRadiusCache = Math.max(lT.getRadius(), rT.getRadius()); + } + } + } + if (deviceRadiusCache == 0) { + String manufacturer = Build.MANUFACTURER.toLowerCase(); + if ("xiaomi".equals(manufacturer)) { + try { + Class systemPropertiesClass = Class.forName("android.os.SystemProperties"); + Method getIntMethod = systemPropertiesClass.getMethod("getInt", String.class, int.class); + deviceRadiusCache = (int) getIntMethod.invoke(null, "ro.miui.notch.radius", 0); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + if (deviceRadiusCache == 0) { + try { + int resourceId = me.getResources().getIdentifier("rounded_corner_radius", "dimen", "android"); + if (resourceId > 0) { + deviceRadiusCache = me.getResources().getDimensionPixelSize(resourceId); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + return deviceRadiusCache; + } + + public FullScreenDialog setDeviceRadius(int deviceRadiusPx) { + deviceRadiusCache = deviceRadiusPx; + return this; + } + + public FullScreenDialog setActivityContentRadius(float activityContentRadius) { + this.activityContentRadius = activityContentRadius; + return this; + } + + private float getActivityZoomRadius(float startValue, float endValue, float progressValue) { + return startValue + progressValue * (endValue - startValue); + } + + public FullScreenDialog setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public FullScreenDialog onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public FullScreenDialog onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public FullScreenDialog setEnableImmersiveMode(boolean enableImmersiveMode) { + this.enableImmersiveMode = enableImmersiveMode; + refreshUI(); + return this; + } + + public FullScreenDialog setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex(" + orderIndex + ") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public FullScreenDialog bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public FullScreenDialog setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public FullScreenDialog cleanAction(int actionId) { + dialogActionRunnableMap.remove(actionId); + return this; + } + + public FullScreenDialog cleanAllAction() { + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + public void callDialogDismiss() { + dismiss(); + } + + public FullScreenDialog bindDismissWithLifecycleOwner(LifecycleOwner owner) { + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/GuideDialog.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/GuideDialog.java new file mode 100644 index 0000000..5928d9c --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/GuideDialog.java @@ -0,0 +1,852 @@ +package com.kongzue.dialogx.dialogs; + +import android.app.Activity; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.RelativeLayout; + +import androidx.annotation.ColorInt; +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.OnBackPressedListener; +import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener; +import com.kongzue.dialogx.interfaces.OnBindView; +import com.kongzue.dialogx.interfaces.OnDialogButtonClickListener; +import com.kongzue.dialogx.util.views.DialogXBaseRelativeLayout; + +import java.util.Arrays; +import java.util.HashMap; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2022/8/19 16:35 + */ +public class GuideDialog extends CustomDialog { + + public enum STAGE_LIGHT_TYPE { + RECTANGLE, //矩形 + SQUARE_OUTSIDE, //方形(外围) + SQUARE_INSIDE, //方形(内围) + CIRCLE_OUTSIDE, //圆形(外围) + CIRCLE_INSIDE, //圆形(内围) + } + + protected STAGE_LIGHT_TYPE stageLightType = STAGE_LIGHT_TYPE.CIRCLE_OUTSIDE; + protected Drawable tipImage; + protected float stageLightFilletRadius; //舞台灯光部分的圆角 + protected Integer maskColor = null; + protected OnDialogButtonClickListener onStageLightPathClickListener; + protected int[] baseViewLocationCoordinateCompensation = new int[4]; + + protected GuideDialog() { + super(); + enterAnimResId = R.anim.anim_dialogx_alpha_enter; + exitAnimResId = R.anim.anim_dialogx_default_exit; + this.alignViewGravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; + } + + public static GuideDialog build() { + return new GuideDialog(); + } + + public GuideDialog(View baseView, STAGE_LIGHT_TYPE stageLightType) { + this(); + this.baseView(baseView); + this.stageLightType = stageLightType; + } + + public GuideDialog(View baseView, STAGE_LIGHT_TYPE stageLightType, OnBindView onBindView, int alignBaseViewGravity) { + this(); + this.baseView(baseView); + this.stageLightType = stageLightType; + this.onBindView = onBindView; + this.alignViewGravity = alignBaseViewGravity; + } + + public GuideDialog(View baseView, STAGE_LIGHT_TYPE stageLightType, int tipImageResId, int alignBaseViewGravity) { + this(); + this.baseView(baseView); + this.tipImage = getResources().getDrawable(tipImageResId); + this.stageLightType = stageLightType; + this.alignViewGravity = alignBaseViewGravity; + } + + public GuideDialog(View baseView, STAGE_LIGHT_TYPE stageLightType, Bitmap tipImage, int alignBaseViewGravity) { + this(); + this.baseView(baseView); + this.tipImage = new BitmapDrawable(getResources(), tipImage); + this.stageLightType = stageLightType; + this.alignViewGravity = alignBaseViewGravity; + } + + public GuideDialog(View baseView, STAGE_LIGHT_TYPE stageLightType, Drawable tipImage, int alignBaseViewGravity) { + this(); + this.baseView(baseView); + this.tipImage = tipImage; + this.stageLightType = stageLightType; + this.alignViewGravity = alignBaseViewGravity; + } + + public GuideDialog(int tipImageResId) { + this(); + this.tipImage = getResources().getDrawable(tipImageResId); + } + + public GuideDialog(Bitmap tipImage) { + this(); + this.tipImage = new BitmapDrawable(getResources(), tipImage); + } + + public GuideDialog(Drawable tipImage) { + this(); + this.tipImage = tipImage; + } + + public GuideDialog(int tipImageResId, ALIGN align) { + this(); + this.tipImage = getResources().getDrawable(tipImageResId); + this.align = align; + } + + public GuideDialog(Bitmap tipImage, ALIGN align) { + this(); + this.tipImage = new BitmapDrawable(getResources(), tipImage); + this.align = align; + } + + public GuideDialog(Drawable tipImage, ALIGN align) { + this(); + this.tipImage = tipImage; + this.align = align; + } + + public GuideDialog(OnBindView onBindView) { + this(); + this.onBindView = onBindView; + } + + public GuideDialog(OnBindView onBindView, ALIGN align) { + this(); + this.onBindView = onBindView; + this.align = align; + } + + public GuideDialog(View baseView, int tipImageResId) { + this(); + this.baseView(baseView); + this.tipImage = getResources().getDrawable(tipImageResId); + } + + public GuideDialog(View baseView, Bitmap tipImage) { + this(); + this.baseView(baseView); + this.tipImage = new BitmapDrawable(getResources(), tipImage); + } + + public GuideDialog(View baseView, Drawable tipImage) { + this(); + this.baseView(baseView); + this.tipImage = tipImage; + } + + public GuideDialog(View baseView, STAGE_LIGHT_TYPE stageLightType, int tipImageResId) { + this(); + this.baseView(baseView); + this.stageLightType = stageLightType; + this.tipImage = getResources().getDrawable(tipImageResId); + } + + public GuideDialog(View baseView, STAGE_LIGHT_TYPE stageLightType, Bitmap tipImage) { + this(); + this.baseView(baseView); + this.stageLightType = stageLightType; + this.tipImage = new BitmapDrawable(getResources(), tipImage); + } + + public GuideDialog(View baseView, STAGE_LIGHT_TYPE stageLightType, Drawable tipImage) { + this(); + this.baseView(baseView); + this.stageLightType = stageLightType; + this.tipImage = tipImage; + } + + public GuideDialog(View baseView, int tipImageResId, int alignBaseViewGravity) { + this(); + this.baseView(baseView); + this.alignViewGravity = alignBaseViewGravity; + this.tipImage = getResources().getDrawable(tipImageResId); + } + + public GuideDialog(View baseView, Bitmap tipImage, int alignBaseViewGravity) { + this(); + this.baseView(baseView); + this.alignViewGravity = alignBaseViewGravity; + this.tipImage = new BitmapDrawable(getResources(), tipImage); + } + + public GuideDialog(View baseView, Drawable tipImage, int alignBaseViewGravity) { + this(); + this.baseView(baseView); + this.alignViewGravity = alignBaseViewGravity; + this.tipImage = tipImage; + } + + //静态方法 + public static GuideDialog show(OnBindView onBindView) { + GuideDialog guideDialog = new GuideDialog(onBindView); + guideDialog.show(); + return guideDialog; + } + + public static GuideDialog show(OnBindView onBindView, ALIGN align) { + GuideDialog guideDialog = new GuideDialog(onBindView); + guideDialog.align = align; + guideDialog.show(); + return guideDialog; + } + + public static GuideDialog show(int tipImageResId) { + return new GuideDialog(tipImageResId).show(); + } + + public static GuideDialog show(Bitmap tipImage) { + return new GuideDialog(tipImage).show(); + } + + public static GuideDialog show(Drawable tipImage) { + return new GuideDialog(tipImage).show(); + } + + public static GuideDialog show(int tipImageResId, ALIGN align) { + GuideDialog guideDialog = new GuideDialog(tipImageResId, align); + guideDialog.align = align; + return guideDialog.show(); + } + + public static GuideDialog show(Bitmap tipImage, ALIGN align) { + GuideDialog guideDialog = new GuideDialog(tipImage, align); + guideDialog.align = align; + return guideDialog.show(); + } + + public static GuideDialog show(Drawable tipImage, ALIGN align) { + return new GuideDialog(tipImage, align).show(); + } + + public static GuideDialog show(View baseView, STAGE_LIGHT_TYPE stageLightType) { + return new GuideDialog(baseView, stageLightType).show(); + } + + public static GuideDialog show(View baseView, STAGE_LIGHT_TYPE stageLightType, OnBindView onBindView, int alignBaseViewGravity) { + return new GuideDialog(baseView, stageLightType, onBindView, alignBaseViewGravity).show(); + } + + public static GuideDialog show(View baseView, STAGE_LIGHT_TYPE stageLightType, int tipImageResId, int alignBaseViewGravity) { + return new GuideDialog(baseView, stageLightType, tipImageResId, alignBaseViewGravity).show(); + } + + public static GuideDialog show(View baseView, STAGE_LIGHT_TYPE stageLightType, Bitmap tipImage, int alignBaseViewGravity) { + return new GuideDialog(baseView, stageLightType, tipImage, alignBaseViewGravity).show(); + } + + public static GuideDialog show(View baseView, STAGE_LIGHT_TYPE stageLightType, Drawable tipImage, int alignBaseViewGravity) { + return new GuideDialog(baseView, stageLightType, tipImage, alignBaseViewGravity).show(); + } + + public static GuideDialog show(View baseView, int tipImageResId) { + return new GuideDialog(baseView, tipImageResId).show(); + } + + public static GuideDialog show(View baseView, Bitmap tipImage) { + return new GuideDialog(baseView, tipImage).show(); + } + + public static GuideDialog show(View baseView, Drawable tipImage) { + return new GuideDialog(baseView, tipImage).show(); + } + + public static GuideDialog show(View baseView, STAGE_LIGHT_TYPE stageLightType, int tipImageResId) { + return new GuideDialog(baseView, stageLightType, tipImageResId).show(); + } + + public static GuideDialog show(View baseView, STAGE_LIGHT_TYPE stageLightType, Bitmap tipImage) { + return new GuideDialog(baseView, stageLightType, tipImage).show(); + } + + public static GuideDialog show(View baseView, STAGE_LIGHT_TYPE stageLightType, Drawable tipImage) { + return new GuideDialog(baseView, stageLightType, tipImage).show(); + } + + public static GuideDialog show(View baseView, int tipImageResId, int alignBaseViewGravity) { + return new GuideDialog(baseView, tipImageResId, alignBaseViewGravity).show(); + } + + public static GuideDialog show(View baseView, Bitmap tipImage, int alignBaseViewGravity) { + return new GuideDialog(baseView, tipImage, alignBaseViewGravity).show(); + } + + public static GuideDialog show(View baseView, Drawable tipImage, int alignBaseViewGravity) { + return new GuideDialog(baseView, tipImage, alignBaseViewGravity).show(); + } + + //执行方法 + public GuideDialog show() { + super.show(); + return this; + } + + public GuideDialog show(Activity activity) { + super.show(activity); + return this; + } + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + public GuideDialog setDialogLifecycleCallback(DialogLifecycleCallback dialogLifecycleCallback) { + this.dialogLifecycleCallback = dialogLifecycleCallback; + if (isShow) dialogLifecycleCallback.onShow(me); + return this; + } + + public GuideDialog setOnBackPressedListener(OnBackPressedListener onBackPressedListener) { + this.onBackPressedListener = onBackPressedListener; + refreshUI(); + return this; + } + + public GuideDialog setStyle(DialogXStyle style) { + this.style = style; + return this; + } + + public GuideDialog setTheme(DialogX.THEME theme) { + this.theme = theme; + return this; + } + + public GuideDialog setCancelable(boolean cancelable) { + this.privateCancelable = cancelable ? BOOLEAN.TRUE : BOOLEAN.FALSE; + refreshUI(); + return this; + } + + public GuideDialog.DialogImpl getDialogImpl() { + return dialogImpl; + } + + public GuideDialog setCustomView(OnBindView onBindView) { + this.onBindView = onBindView; + refreshUI(); + return this; + } + + public GuideDialog removeCustomView() { + this.onBindView.clean(); + refreshUI(); + return this; + } + + public GuideDialog setEnterAnimResId(int enterAnimResId) { + this.enterAnimResId = enterAnimResId; + return this; + } + + public GuideDialog setExitAnimResId(int exitAnimResId) { + this.exitAnimResId = exitAnimResId; + return this; + } + + public GuideDialog setAnimResId(int enterAnimResId, int exitAnimResId) { + this.enterAnimResId = enterAnimResId; + this.exitAnimResId = exitAnimResId; + return this; + } + + public GuideDialog setAlign(ALIGN align) { + this.align = align; + return this; + } + + public GuideDialog setAutoUnsafePlacePadding(boolean autoUnsafePlacePadding) { + super.setAutoUnsafePlacePadding(autoUnsafePlacePadding); + return this; + } + + public GuideDialog setFullScreen(boolean fullscreen) { + super.setFullScreen(fullscreen); + return this; + } + + public GuideDialog setMaskColor(@ColorInt int maskColor) { + this.maskColor = maskColor; + refreshUI(); + return this; + } + + public GuideDialog setEnterAnimDuration(long enterAnimDuration) { + this.enterAnimDuration = enterAnimDuration; + return this; + } + + public GuideDialog setExitAnimDuration(long exitAnimDuration) { + this.exitAnimDuration = exitAnimDuration; + return this; + } + + public GuideDialog setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public GuideDialog setBkgInterceptTouch(boolean bkgInterceptTouch) { + this.bkgInterceptTouch = bkgInterceptTouch; + return this; + } + + public GuideDialog setAlignBaseViewGravity(View baseView, int alignGravity) { + this.baseView(baseView); + this.alignViewGravity = alignGravity; + baseViewLoc = new int[4]; + baseView.getLocationInWindow(baseViewLoc); + setFullScreen(true); + return this; + } + + public GuideDialog setAlignBaseViewGravity(View baseView) { + this.baseView(baseView); + baseViewLoc = new int[4]; + baseView.getLocationInWindow(baseViewLoc); + setFullScreen(true); + return this; + } + + public GuideDialog setAlignBaseViewGravity(int alignGravity) { + this.alignViewGravity = alignGravity; + if (baseView() != null) { + baseViewLoc = new int[4]; + baseView().getLocationInWindow(baseViewLoc); + } + setFullScreen(true); + return this; + } + + public GuideDialog setAlignBaseViewGravity(View baseView, int alignGravity, int marginLeft, + int marginTop, int marginRight, int marginBottom) { + this.marginRelativeBaseView = new int[]{marginLeft, marginTop, marginRight, marginBottom}; + return setAlignBaseViewGravity(baseView, alignGravity); + } + + public GuideDialog setBaseViewMargin(int[] marginRelativeBaseView) { + this.marginRelativeBaseView = marginRelativeBaseView; + return this; + } + + public GuideDialog setBaseViewMargin(int marginLeft, int marginTop, + int marginRight, int marginBottom) { + this.marginRelativeBaseView = new int[]{marginLeft, marginTop, marginRight, marginBottom}; + return this; + } + + public GuideDialog setBaseViewMarginLeft(int marginLeft) { + this.marginRelativeBaseView[0] = marginLeft; + return this; + } + + public GuideDialog setBaseViewMarginTop(int marginTop) { + this.marginRelativeBaseView[1] = marginTop; + return this; + } + + public GuideDialog setBaseViewMarginRight(int marginRight) { + this.marginRelativeBaseView[2] = marginRight; + return this; + } + + public GuideDialog setBaseViewMarginBottom(int marginBottom) { + this.marginRelativeBaseView[3] = marginBottom; + return this; + } + + /** + * 设置对话框 UI 宽度(单位:像素) + * + * @param width 宽度(像素) + * @return CustomDialog实例 + */ + public GuideDialog setWidth(int width) { + this.width = width; + refreshUI(); + return this; + } + + /** + * 设置对话框 UI 高度(单位:像素) + * + * @param height 高度(像素) + * @return CustomDialog实例 + */ + public GuideDialog setHeight(int height) { + this.height = height; + refreshUI(); + return this; + } + + public GuideDialog setOnBackgroundMaskClickListener(OnBackgroundMaskClickListener onBackgroundMaskClickListener) { + this.onBackgroundMaskClickListener = onBackgroundMaskClickListener; + return this; + } + + @Override + protected void onDialogShow() { + super.onDialogShow(); + if (baseView() == null) { + super.setMaskColor(maskColor == null ? getColor(R.color.black50) : maskColor); + } + } + + View stageLightPathStub; + + @Override + protected void onDialogRefreshUI() { + super.onDialogRefreshUI(); + if (onBindView == null && tipImage != null) { + getDialogImpl().boxCustom.setFocusable(false); + getDialogImpl().boxCustom.setFocusableInTouchMode(false); + getDialogImpl().boxCustom.setOnClickListener(null); + getDialogImpl().boxCustom.setClickable(false); + + ImageView imageView = new ImageView(getOwnActivity()); + imageView.setImageDrawable(tipImage); + imageView.setAdjustViewBounds(true); + imageView.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + onBindView = new OnBindView(imageView) { + @Override + public void onBind(CustomDialog dialog, View v) { + + } + }; + onBindView.bindParent(getDialogImpl().boxCustom, me); + } + if (getOnStageLightPathClickListener() != null && baseView() != null) { + stageLightPathStub = new View(getOwnActivity()); + stageLightPathStub.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (!getOnStageLightPathClickListener().onClick(GuideDialog.this, v)) { + dismiss(); + } + } + }); + getDialogImpl().boxRoot.addView(stageLightPathStub); + } else { + if (stageLightPathStub != null && stageLightPathStub.getParent() instanceof ViewGroup) { + ((ViewGroup) stageLightPathStub.getParent()).removeView(stageLightPathStub); + } + } + } + + int[] baseViewLocCache; + + @Override + protected void onGetBaseViewLoc(int[] baseViewLoc) { + if (Arrays.equals(baseViewLoc, baseViewLocCache)) { + return; + } + if (getDialogImpl() == null) { + return; + } + Bitmap bkg = Bitmap.createBitmap(getDialogImpl().boxRoot.getWidth(), getDialogImpl().boxRoot.getHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bkg); + + int x = baseViewLoc[0] + baseViewLocationCoordinateCompensation[0]; + int y = baseViewLoc[1] + baseViewLocationCoordinateCompensation[1]; + int w = baseViewLoc[2] + baseViewLocationCoordinateCompensation[2]; + int h = baseViewLoc[3] + baseViewLocationCoordinateCompensation[3]; + int hW = w / 2; + int hH = h / 2; + + if (stageLightPathStub != null && (stageLightPathStub.getX() != x || stageLightPathStub.getY() != y)) { + RelativeLayout.LayoutParams rLp = (RelativeLayout.LayoutParams) stageLightPathStub.getLayoutParams(); + if (rLp == null) { + rLp = new RelativeLayout.LayoutParams(w, h); + } else { + rLp.width = w; + rLp.height = h; + } + stageLightPathStub.setLayoutParams(rLp); + stageLightPathStub.setX(x); + stageLightPathStub.setY(y); + } + + switch (stageLightType) { + case CIRCLE_OUTSIDE: { + int r = (int) Math.sqrt(hW * hW + hH * hH); + canvas.drawCircle(x + hW, y + hH, r, getStageLightPaint()); + } + break; + case CIRCLE_INSIDE: { + int r = Math.min(w, h) / 2; + canvas.drawCircle(x + hW, y + hH, r, getStageLightPaint()); + } + break; + case RECTANGLE: { + canvas.drawRoundRect(new RectF(x, y, x + w, y + h), stageLightFilletRadius, stageLightFilletRadius, getStageLightPaint()); + } + break; + case SQUARE_INSIDE: { + int r = Math.min(w, h); + canvas.drawRoundRect(new RectF(x + hW - r / 2, y + hH - r / 2, x + hW - r / 2 + r, y + hH - r / 2 + r), stageLightFilletRadius, stageLightFilletRadius, getStageLightPaint()); + } + break; + case SQUARE_OUTSIDE: { + int r = Math.max(w, h); + canvas.drawRoundRect(new RectF(x + hW - r / 2, y + hH - r / 2, x + hW - r / 2 + r, y + hH - r / 2 + r), stageLightFilletRadius, stageLightFilletRadius, getStageLightPaint()); + } + break; + } + stageLightPaint.setXfermode(null); + canvas.drawColor(maskColor == null ? getColor(R.color.black50) : maskColor, PorterDuff.Mode.SRC_OUT); + + BitmapDrawable bkgDrawable = new BitmapDrawable(getResources(), bkg); + getDialogImpl().boxRoot.setBackground(bkgDrawable); + baseViewLocCache = Arrays.copyOf(baseViewLoc, 4); + } + + Paint stageLightPaint; + + private Paint getStageLightPaint() { + if (stageLightPaint == null) { + stageLightPaint = new Paint(); + stageLightPaint.setColor(Color.RED); + stageLightPaint.setStyle(Paint.Style.FILL); + stageLightPaint.setAntiAlias(true); + } + return stageLightPaint; + } + + public STAGE_LIGHT_TYPE getStageLightType() { + return stageLightType; + } + + public GuideDialog setStageLightType(STAGE_LIGHT_TYPE stageLightType) { + this.stageLightType = stageLightType; + refreshUI(); + return this; + } + + public Drawable getTipImage() { + return tipImage; + } + + public GuideDialog setTipImage(int tipImageResId) { + this.tipImage = getResources().getDrawable(tipImageResId); + refreshUI(); + return this; + } + + public GuideDialog setTipImage(Bitmap tipImage) { + this.tipImage = new BitmapDrawable(getResources(), tipImage); + refreshUI(); + return this; + } + + public GuideDialog setTipImage(Drawable tipImage) { + this.tipImage = tipImage; + refreshUI(); + return this; + } + + public float getStageLightFilletRadius() { + return stageLightFilletRadius; + } + + public GuideDialog setStageLightFilletRadius(float stageLightFilletRadius) { + this.stageLightFilletRadius = stageLightFilletRadius; + refreshUI(); + return this; + } + + public OnDialogButtonClickListener getOnStageLightPathClickListener() { + return onStageLightPathClickListener; + } + + public GuideDialog setOnStageLightPathClickListener(OnDialogButtonClickListener onStageLightPathClickListener) { + this.onStageLightPathClickListener = onStageLightPathClickListener; + refreshUI(); + return this; + } + + public DialogXAnimInterface getDialogXAnimImpl() { + return dialogXAnimImpl; + } + + public GuideDialog setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public GuideDialog setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public GuideDialog setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + public int[] getBaseViewLocationCoordinateCompensation() { + return baseViewLocationCoordinateCompensation; + } + + public GuideDialog setBaseViewLocationCoordinateCompensation(int[] baseViewLocationCoordinateCompensation) { + this.baseViewLocationCoordinateCompensation = baseViewLocationCoordinateCompensation; + return this; + } + + public GuideDialog setBaseViewLocationCoordinateCompensation(int px) { + this.baseViewLocationCoordinateCompensation = new int[]{px, px, px, px}; + refreshUI(); + return this; + } + + public GuideDialog setBaseViewLocationCoordinateCompensation(int pxX, int pxY, int pxR, int pxB) { + this.baseViewLocationCoordinateCompensation = new int[]{pxX, pxY, pxR, pxB}; + refreshUI(); + return this; + } + + public GuideDialog setBaseViewLocationCoordinateCompensationLeft(int pxX) { + this.baseViewLocationCoordinateCompensation[0] = pxX; + refreshUI(); + return this; + } + + public GuideDialog setBaseViewLocationCoordinateCompensationTop(int pxY) { + this.baseViewLocationCoordinateCompensation[1] = pxY; + refreshUI(); + return this; + } + + public GuideDialog setBaseViewLocationCoordinateCompensationRight(int pxR) { + this.baseViewLocationCoordinateCompensation[2] = pxR; + refreshUI(); + return this; + } + + public GuideDialog setBaseViewLocationCoordinateCompensationBottom(int pxB) { + this.baseViewLocationCoordinateCompensation[3] = pxB; + refreshUI(); + return this; + } + + public int getBaseViewLocationCoordinateCompensationLeft() { + return baseViewLocationCoordinateCompensation[0]; + } + + public int getBaseViewLocationCoordinateCompensationTop() { + return baseViewLocationCoordinateCompensation[1]; + } + + public int getBaseViewLocationCoordinateCompensationRight() { + return baseViewLocationCoordinateCompensation[2]; + } + + public int getBaseViewLocationCoordinateCompensationBottom() { + return baseViewLocationCoordinateCompensation[3]; + } + + public GuideDialog setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public GuideDialog onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public GuideDialog onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public GuideDialog setEnableImmersiveMode(boolean enableImmersiveMode) { + this.enableImmersiveMode = enableImmersiveMode; + refreshUI(); + return this; + } + + public GuideDialog setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex("+orderIndex+") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public GuideDialog bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public GuideDialog setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public GuideDialog cleanAction(int actionId){ + dialogActionRunnableMap.remove(actionId); + return this; + } + + public GuideDialog cleanAllAction(){ + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + public void callDialogDismiss(){ + dismiss(); + } + + public GuideDialog bindDismissWithLifecycleOwner(LifecycleOwner owner){ + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/InputDialog.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/InputDialog.java new file mode 100644 index 0000000..1a492e8 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/InputDialog.java @@ -0,0 +1,759 @@ +package com.kongzue.dialogx.dialogs; + +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.text.TextUtils; +import android.view.View; + +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.OnBackPressedListener; +import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener; +import com.kongzue.dialogx.interfaces.OnBindView; +import com.kongzue.dialogx.interfaces.OnInputDialogButtonClickListener; +import com.kongzue.dialogx.util.InputInfo; +import com.kongzue.dialogx.util.TextInfo; + +import java.util.HashMap; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/24 13:53 + */ +public class InputDialog extends MessageDialog { + + protected InputDialog() { + super(); + } + + public static InputDialog build() { + return new InputDialog(); + } + + public static InputDialog build(DialogXStyle style) { + InputDialog dialog = new InputDialog(); + dialog.setStyle(style); + return dialog; + } + + public static InputDialog build(OnBindView onBindView) { + return new InputDialog().setCustomView(onBindView); + } + + public InputDialog(CharSequence title, CharSequence message, CharSequence okText) { + cancelable = DialogX.cancelable; + this.title = title; + this.message = message; + this.okText = okText; + } + + public InputDialog(int titleResId, int messageResId, int okTextResId) { + cancelable = DialogX.cancelable; + this.title = getString(titleResId); + this.message = getString(messageResId); + this.okText = getString(okTextResId); + } + + public static InputDialog show(CharSequence title, CharSequence message, CharSequence okText) { + InputDialog inputDialog = new InputDialog(title, message, okText); + inputDialog.show(); + return inputDialog; + } + + public static InputDialog show(int titleResId, int messageResId, int okTextResId) { + InputDialog inputDialog = new InputDialog(titleResId, messageResId, okTextResId); + inputDialog.show(); + return inputDialog; + } + + public InputDialog(CharSequence title, CharSequence message, CharSequence okText, CharSequence cancelText) { + cancelable = DialogX.cancelable; + this.title = title; + this.message = message; + this.okText = okText; + this.cancelText = cancelText; + } + + public InputDialog(int titleResId, int messageResId, int okTextResId, int cancelTextResId) { + cancelable = DialogX.cancelable; + this.title = getString(titleResId); + this.message = getString(messageResId); + this.okText = getString(okTextResId); + this.cancelText = getString(cancelTextResId); + } + + public static InputDialog show(CharSequence title, CharSequence message, CharSequence okText, CharSequence cancelText) { + InputDialog inputDialog = new InputDialog(title, message, okText, cancelText); + inputDialog.show(); + return inputDialog; + } + + public static InputDialog show(int titleResId, int messageResId, int okTextResId, int cancelTextResId) { + InputDialog inputDialog = new InputDialog(titleResId, messageResId, okTextResId, cancelTextResId); + inputDialog.show(); + return inputDialog; + } + + public InputDialog(CharSequence title, CharSequence message, CharSequence okText, CharSequence cancelText, String inputText) { + cancelable = DialogX.cancelable; + this.title = title; + this.message = message; + this.okText = okText; + this.cancelText = cancelText; + this.inputText = inputText; + } + + public static InputDialog show(CharSequence title, CharSequence message, CharSequence okText, CharSequence cancelText, String inputText) { + InputDialog inputDialog = new InputDialog(title, message, okText, cancelText, inputText); + inputDialog.show(); + return inputDialog; + } + + public InputDialog(CharSequence title, CharSequence message, CharSequence okText, CharSequence cancelText, CharSequence otherText) { + cancelable = DialogX.cancelable; + this.title = title; + this.message = message; + this.okText = okText; + this.cancelText = cancelText; + this.otherText = otherText; + } + + public InputDialog(int titleResId, int messageResId, int okTextResId, int cancelTextResId, int otherTextResId) { + cancelable = DialogX.cancelable; + this.title = getString(titleResId); + this.message = getString(messageResId); + this.okText = getString(okTextResId); + this.cancelText = getString(cancelTextResId); + this.otherText = getString(otherTextResId); + } + + public static InputDialog show(CharSequence title, CharSequence message, CharSequence okText, CharSequence cancelText, CharSequence otherText) { + InputDialog inputDialog = new InputDialog(title, message, okText, cancelText, otherText); + inputDialog.show(); + return inputDialog; + } + + public static InputDialog show(int titleResId, int messageResId, int okTextResId, int cancelTextResId, int otherTextResId) { + InputDialog inputDialog = new InputDialog(titleResId, messageResId, okTextResId, cancelTextResId, otherTextResId); + inputDialog.show(); + return inputDialog; + } + + public InputDialog(CharSequence title, CharSequence message, CharSequence okText, CharSequence cancelText, CharSequence otherText, String inputText) { + cancelable = DialogX.cancelable; + this.title = title; + this.message = message; + this.okText = okText; + this.cancelText = cancelText; + this.otherText = otherText; + this.inputText = inputText; + } + + public InputDialog(int titleResId, int messageResId, int okTextResId, int cancelTextResId, int otherTextResId, int inputTextResId) { + cancelable = DialogX.cancelable; + this.title = getString(titleResId); + this.message = getString(messageResId); + this.okText = getString(okTextResId); + this.cancelText = getString(cancelTextResId); + this.otherText = getString(otherTextResId); + this.inputText = getString(inputTextResId); + } + + public static InputDialog show(CharSequence title, CharSequence message, CharSequence okText, CharSequence cancelText, CharSequence otherText, String inputText) { + InputDialog inputDialog = new InputDialog(title, message, okText, cancelText, otherText, inputText); + inputDialog.show(); + return inputDialog; + } + + public static InputDialog show(int titleResId, int messageResId, int okTextResId, int cancelTextResId, int otherTextResId, int inputTextResId) { + InputDialog inputDialog = new InputDialog(titleResId, messageResId, okTextResId, cancelTextResId, otherTextResId, inputTextResId); + inputDialog.show(); + return inputDialog; + } + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + public CharSequence getOkButton() { + return okText; + } + + public InputDialog setOkButton(CharSequence okText) { + this.okText = okText; + refreshUI(); + return this; + } + + public InputDialog setOkButton(int okTextResId) { + this.okText = getString(okTextResId); + refreshUI(); + return this; + } + + public InputDialog setOkButton(OnInputDialogButtonClickListener okButtonClickListener) { + this.okButtonClickListener = okButtonClickListener; + return this; + } + + public InputDialog setOkButton(CharSequence okText, OnInputDialogButtonClickListener okButtonClickListener) { + this.okText = okText; + this.okButtonClickListener = okButtonClickListener; + refreshUI(); + return this; + } + + public InputDialog setOkButton(int okTextResId, OnInputDialogButtonClickListener okButtonClickListener) { + this.okText = getString(okTextResId); + this.okButtonClickListener = okButtonClickListener; + refreshUI(); + return this; + } + + public CharSequence getCancelButton() { + return cancelText; + } + + public InputDialog setCancelButton(CharSequence cancelText) { + this.cancelText = cancelText; + refreshUI(); + return this; + } + + public InputDialog setCancelButton(int cancelTextResId) { + this.cancelText = getString(cancelTextResId); + refreshUI(); + return this; + } + + public InputDialog setCancelButton(OnInputDialogButtonClickListener cancelButtonClickListener) { + this.cancelButtonClickListener = cancelButtonClickListener; + return this; + } + + public InputDialog setCancelButton(CharSequence cancelText, OnInputDialogButtonClickListener cancelButtonClickListener) { + this.cancelText = cancelText; + this.cancelButtonClickListener = cancelButtonClickListener; + refreshUI(); + return this; + } + + public InputDialog setCancelButton(int cancelTextResId, OnInputDialogButtonClickListener cancelButtonClickListener) { + this.cancelText = getString(cancelTextResId); + this.cancelButtonClickListener = cancelButtonClickListener; + refreshUI(); + return this; + } + + public CharSequence getOtherButton() { + return otherText; + } + + public InputDialog setOtherButton(CharSequence otherText) { + this.otherText = otherText; + refreshUI(); + return this; + } + + public InputDialog setOtherButton(int otherTextResId) { + this.otherText = getString(otherTextResId); + refreshUI(); + return this; + } + + public InputDialog setOtherButton(OnInputDialogButtonClickListener otherButtonClickListener) { + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + public InputDialog setOtherButton(CharSequence otherText, OnInputDialogButtonClickListener otherButtonClickListener) { + this.otherText = otherText; + this.otherButtonClickListener = otherButtonClickListener; + refreshUI(); + return this; + } + + public InputDialog setOtherButton(int otherTextResId, OnInputDialogButtonClickListener otherButtonClickListener) { + this.otherText = getString(otherTextResId); + this.otherButtonClickListener = otherButtonClickListener; + refreshUI(); + return this; + } + + public OnInputDialogButtonClickListener getInputOkButtonClickListener() { + return (OnInputDialogButtonClickListener) okButtonClickListener; + } + + public InputDialog setOkButtonClickListener(OnInputDialogButtonClickListener okButtonClickListener) { + this.okButtonClickListener = okButtonClickListener; + return this; + } + + public OnInputDialogButtonClickListener getInputCancelButtonClickListener() { + return (OnInputDialogButtonClickListener) cancelButtonClickListener; + } + + public InputDialog setCancelButtonClickListener(OnInputDialogButtonClickListener cancelButtonClickListener) { + this.cancelButtonClickListener = cancelButtonClickListener; + return this; + } + + public OnInputDialogButtonClickListener getInputOtherButtonClickListener() { + return (OnInputDialogButtonClickListener) otherButtonClickListener; + } + + public InputDialog setOtherButtonClickListener(OnInputDialogButtonClickListener otherButtonClickListener) { + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + public CharSequence getTitle() { + return title; + } + + public InputDialog setTitle(CharSequence title) { + this.title = title; + refreshUI(); + return this; + } + + public InputDialog setTitle(int titleResId) { + this.title = getString(titleResId); + refreshUI(); + return this; + } + + public CharSequence getMessage() { + return message; + } + + public InputDialog setMessage(CharSequence message) { + this.message = message; + refreshUI(); + return this; + } + + public InputDialog setMessage(int messageResId) { + this.message = getString(messageResId); + refreshUI(); + return this; + } + + public String getInputText() { + if (getDialogImpl() != null && getDialogImpl().txtInput != null) { + return getDialogImpl().txtInput.getText().toString(); + } + return inputText; + } + + public InputDialog setInputText(String inputText) { + this.inputText = inputText; + refreshUI(); + return this; + } + + public InputDialog setInputText(int inputTextResId) { + this.inputText = getString(inputTextResId); + refreshUI(); + return this; + } + + public String getInputHintText() { + return inputHintText; + } + + public InputDialog setInputHintText(String inputHintText) { + this.inputHintText = inputHintText; + refreshUI(); + return this; + } + + public InputDialog setInputHintText(int inputHintTextResId) { + this.inputHintText = getString(inputHintTextResId); + refreshUI(); + return this; + } + + public TextInfo getTitleTextInfo() { + return titleTextInfo; + } + + public InputDialog setTitleTextInfo(TextInfo titleTextInfo) { + this.titleTextInfo = titleTextInfo; + refreshUI(); + return this; + } + + public TextInfo getMessageTextInfo() { + return messageTextInfo; + } + + public InputDialog setMessageTextInfo(TextInfo messageTextInfo) { + this.messageTextInfo = messageTextInfo; + refreshUI(); + return this; + } + + public TextInfo getOkTextInfo() { + return okTextInfo; + } + + public InputDialog setOkTextInfo(TextInfo okTextInfo) { + this.okTextInfo = okTextInfo; + refreshUI(); + return this; + } + + public TextInfo getCancelTextInfo() { + return cancelTextInfo; + } + + public InputDialog setCancelTextInfo(TextInfo cancelTextInfo) { + this.cancelTextInfo = cancelTextInfo; + refreshUI(); + return this; + } + + public TextInfo getOtherTextInfo() { + return otherTextInfo; + } + + public InputDialog setOtherTextInfo(TextInfo otherTextInfo) { + this.otherTextInfo = otherTextInfo; + refreshUI(); + return this; + } + + public InputInfo getInputInfo() { + return inputInfo; + } + + public InputDialog setInputInfo(InputInfo inputInfo) { + this.inputInfo = inputInfo; + refreshUI(); + return this; + } + + public int getButtonOrientation() { + return buttonOrientation; + } + + public InputDialog setButtonOrientation(int buttonOrientation) { + this.buttonOrientation = buttonOrientation; + refreshUI(); + return this; + } + + public boolean isCancelable() { + if (privateCancelable != null) { + return privateCancelable == BOOLEAN.TRUE; + } + if (overrideCancelable != null) { + return overrideCancelable == BOOLEAN.TRUE; + } + return cancelable; + } + + public InputDialog setCancelable(boolean cancelable) { + this.privateCancelable = cancelable ? BOOLEAN.TRUE : BOOLEAN.FALSE; + refreshUI(); + return this; + } + + public OnBackPressedListener getOnBackPressedListener() { + return (OnBackPressedListener) onBackPressedListener; + } + + public InputDialog setOnBackPressedListener(OnBackPressedListener onBackPressedListener) { + this.onBackPressedListener = onBackPressedListener; + return this; + } + + public boolean isAutoShowInputKeyboard() { + return autoShowInputKeyboard; + } + + public InputDialog setAutoShowInputKeyboard(boolean autoShowInputKeyboard) { + this.autoShowInputKeyboard = autoShowInputKeyboard; + return this; + } + + public InputDialog setCustomView(OnBindView onBindView) { + this.onBindView = onBindView; + refreshUI(); + return this; + } + + public View getCustomView() { + if (onBindView == null) return null; + return onBindView.getCustomView(); + } + + public InputDialog removeCustomView() { + this.onBindView.clean(); + refreshUI(); + return this; + } + + public int getBackgroundColor() { + return backgroundColor; + } + + public InputDialog setBackgroundColor(@ColorInt int backgroundColor) { + this.backgroundColor = backgroundColor; + refreshUI(); + return this; + } + + public InputDialog setBackgroundColorRes(@ColorRes int backgroundColorResId) { + this.backgroundColor = getColor(backgroundColorResId); + refreshUI(); + return this; + } + + public InputDialog setMaskColor(@ColorInt int maskColor) { + this.maskColor = maskColor; + refreshUI(); + return this; + } + + public long getEnterAnimDuration() { + return enterAnimDuration; + } + + public InputDialog setEnterAnimDuration(long enterAnimDuration) { + this.enterAnimDuration = enterAnimDuration; + return this; + } + + public long getExitAnimDuration() { + return exitAnimDuration; + } + + public InputDialog setExitAnimDuration(long exitAnimDuration) { + this.exitAnimDuration = exitAnimDuration; + return this; + } + + @Override + public void restartDialog() { + if (getDialogView() != null) { + dismiss(getDialogView()); + isShow = false; + } + if (getDialogImpl().boxCustom != null) { + getDialogImpl().boxCustom.removeAllViews(); + } + int layoutId = style.layout(isLightTheme()); + layoutId = layoutId == 0 ? (isLightTheme() ? R.layout.layout_dialogx_material : R.layout.layout_dialogx_material_dark) : layoutId; + + String inputText = getInputText(); + enterAnimDuration = 0; + View dialogView = createView(layoutId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + setInputText(inputText); + } + + public InputDialog setAnimResId(int enterResId, int exitResId) { + customEnterAnimResId = enterResId; + customExitAnimResId = exitResId; + return this; + } + + public InputDialog setEnterAnimResId(int enterResId) { + customEnterAnimResId = enterResId; + return this; + } + + public InputDialog setExitAnimResId(int exitResId) { + customExitAnimResId = exitResId; + return this; + } + + public InputDialog setMaxWidth(int maxWidth) { + this.maxWidth = maxWidth; + refreshUI(); + return this; + } + + public InputDialog setMaxHeight(int maxHeight) { + this.maxHeight = maxHeight; + refreshUI(); + return this; + } + + public InputDialog setMinHeight(int minHeight) { + this.minHeight = minHeight; + refreshUI(); + return this; + } + + public InputDialog setMinWidth(int minWidth) { + this.minWidth = minWidth; + refreshUI(); + return this; + } + + public InputDialog setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public boolean isBkgInterceptTouch() { + return bkgInterceptTouch; + } + + public InputDialog setBkgInterceptTouch(boolean bkgInterceptTouch) { + this.bkgInterceptTouch = bkgInterceptTouch; + return this; + } + + public OnBackgroundMaskClickListener getOnBackgroundMaskClickListener() { + return onBackgroundMaskClickListener; + } + + public InputDialog setOnBackgroundMaskClickListener(OnBackgroundMaskClickListener onBackgroundMaskClickListener) { + this.onBackgroundMaskClickListener = onBackgroundMaskClickListener; + return this; + } + + public InputDialog setRadius(float radiusPx) { + backgroundRadius = radiusPx; + refreshUI(); + return this; + } + + public InputDialog setTitleIcon(Bitmap titleIcon) { + this.titleIcon = new BitmapDrawable(getResources(), titleIcon); + refreshUI(); + return this; + } + + public InputDialog setTitleIcon(int titleIconResId) { + this.titleIcon = getResources().getDrawable(titleIconResId); + refreshUI(); + return this; + } + + public InputDialog setTitleIcon(Drawable titleIcon) { + this.titleIcon = titleIcon; + refreshUI(); + return this; + } + + public DialogXAnimInterface getDialogXAnimImpl() { + return dialogXAnimImpl; + } + + public InputDialog setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public InputDialog setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public InputDialog setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + public InputDialog setDialogLifecycleCallback(DialogLifecycleCallback dialogLifecycleCallback) { + this.dialogLifecycleCallback = dialogLifecycleCallback; + if (isShow) dialogLifecycleCallback.onShow(me); + return this; + } + + public InputDialog setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public InputDialog onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public InputDialog onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public InputDialog setEnableImmersiveMode(boolean enableImmersiveMode) { + this.enableImmersiveMode = enableImmersiveMode; + refreshUI(); + return this; + } + + public InputDialog appendMessage(CharSequence message){ + this.message = TextUtils.concat(this.message, message); + refreshUI(); + return this; + } + + public InputDialog setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex("+orderIndex+") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public InputDialog bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public InputDialog setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public InputDialog cleanAction(int actionId){ + dialogActionRunnableMap.remove(actionId); + return this; + } + + public InputDialog cleanAllAction(){ + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + public void callDialogDismiss(){ + dismiss(); + } + + public InputDialog bindDismissWithLifecycleOwner(LifecycleOwner owner){ + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/MessageDialog.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/MessageDialog.java new file mode 100644 index 0000000..ebafd6f --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/MessageDialog.java @@ -0,0 +1,1612 @@ +package com.kongzue.dialogx.dialogs; + +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.graphics.Bitmap; +import android.graphics.Outline; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.text.InputFilter; +import android.text.InputType; +import android.text.TextUtils; +import android.text.method.LinkMovementMethod; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.view.animation.DecelerateInterpolator; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.Space; +import android.widget.TextView; + +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.annotation.Nullable; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.interfaces.BaseOnDialogClickCallback; +import com.kongzue.dialogx.interfaces.BlurViewType; +import com.kongzue.dialogx.interfaces.DialogConvertViewInterface; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.OnBackPressedListener; +import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener; +import com.kongzue.dialogx.interfaces.OnBindView; +import com.kongzue.dialogx.interfaces.OnDialogButtonClickListener; +import com.kongzue.dialogx.interfaces.OnInputDialogButtonClickListener; +import com.kongzue.dialogx.interfaces.OnMenuButtonClickListener; +import com.kongzue.dialogx.interfaces.ScrollController; +import com.kongzue.dialogx.style.MaterialStyle; +import com.kongzue.dialogx.util.InputInfo; +import com.kongzue.dialogx.util.TextInfo; +import com.kongzue.dialogx.util.views.DialogScrollView; +import com.kongzue.dialogx.util.views.DialogXBaseRelativeLayout; +import com.kongzue.dialogx.util.views.MaxRelativeLayout; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.List; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/21 17:08 + */ +public class MessageDialog extends BaseDialog { + + public static int overrideEnterDuration = -1; + public static int overrideExitDuration = -1; + public static int overrideEnterAnimRes = 0; + public static int overrideExitAnimRes = 0; + public static BOOLEAN overrideCancelable; + protected boolean bkgInterceptTouch = true; + protected OnBindView onBindView; + protected MessageDialog me = this; + protected BOOLEAN privateCancelable; + protected int customEnterAnimResId; + protected int customExitAnimResId; + protected DialogXAnimInterface dialogXAnimImpl; + protected OnBackPressedListener onBackPressedListener; + protected BUTTON_SELECT_RESULT buttonSelectResult = BUTTON_SELECT_RESULT.NONE; + + protected DialogLifecycleCallback dialogLifecycleCallback; + protected OnBackgroundMaskClickListener onBackgroundMaskClickListener; + + protected MessageDialog() { + super(); + } + + protected CharSequence title; + protected CharSequence message; + protected CharSequence okText; + protected CharSequence cancelText; + protected CharSequence otherText; + protected String inputText; + protected String inputHintText; + protected Integer maskColor = null; + protected float backgroundRadius = DialogX.defaultMessageDialogBackgroundRadius; + protected Drawable titleIcon; + + protected TextInfo titleTextInfo; + protected TextInfo messageTextInfo; + protected TextInfo okTextInfo; + protected TextInfo cancelTextInfo; + protected TextInfo otherTextInfo; + protected InputInfo inputInfo; + + protected BaseOnDialogClickCallback okButtonClickListener; + protected BaseOnDialogClickCallback cancelButtonClickListener; + protected BaseOnDialogClickCallback otherButtonClickListener; + + protected int buttonOrientation; + + public static MessageDialog build() { + return new MessageDialog(); + } + + public static MessageDialog build(DialogXStyle style) { + return new MessageDialog().setStyle(style); + } + + public static MessageDialog build(OnBindView onBindView) { + return new MessageDialog().setCustomView(onBindView); + } + + public MessageDialog(CharSequence message) { + this.message = message; + } + + public MessageDialog(CharSequence title, CharSequence message) { + this.title = title; + this.message = message; + } + + public MessageDialog(CharSequence title, CharSequence message, CharSequence okText) { + this.title = title; + this.message = message; + this.okText = okText; + } + + public MessageDialog(int titleResId, int messageResId, int okTextResId) { + this.title = getString(titleResId); + this.message = getString(messageResId); + this.okText = getString(okTextResId); + } + + public MessageDialog(int titleResId, int messageResId) { + this.title = getString(titleResId); + this.message = getString(messageResId); + } + + public static MessageDialog show(CharSequence title, CharSequence message, CharSequence okText) { + MessageDialog messageDialog = new MessageDialog(title, message, okText); + messageDialog.show(); + return messageDialog; + } + + public static MessageDialog show(int titleResId, int messageResId, int okTextResId) { + MessageDialog messageDialog = new MessageDialog(titleResId, messageResId, okTextResId); + messageDialog.show(); + return messageDialog; + } + + public static MessageDialog show(CharSequence title, CharSequence message) { + MessageDialog messageDialog = new MessageDialog(title, message); + messageDialog.show(); + return messageDialog; + } + + public static MessageDialog show(int titleResId, int messageResId) { + MessageDialog messageDialog = new MessageDialog(titleResId, messageResId); + messageDialog.show(); + return messageDialog; + } + + public MessageDialog(CharSequence title, CharSequence message, CharSequence okText, CharSequence cancelText) { + this.title = title; + this.message = message; + this.okText = okText; + this.cancelText = cancelText; + } + + public MessageDialog(int titleResId, int messageResId, int okTextResId, int cancelTextResId) { + this.title = getString(titleResId); + this.message = getString(messageResId); + this.okText = getString(okTextResId); + this.cancelText = getString(cancelTextResId); + } + + public static MessageDialog show(CharSequence title, CharSequence message, CharSequence okText, CharSequence cancelText) { + MessageDialog messageDialog = new MessageDialog(title, message, okText, cancelText); + messageDialog.show(); + return messageDialog; + } + + public static MessageDialog show(int titleResId, int messageResId, int okTextResId, int cancelTextResId) { + MessageDialog messageDialog = new MessageDialog(titleResId, messageResId, okTextResId, cancelTextResId); + messageDialog.show(); + return messageDialog; + } + + public MessageDialog(CharSequence title, CharSequence message, CharSequence okText, CharSequence cancelText, CharSequence otherText) { + this.title = title; + this.message = message; + this.okText = okText; + this.cancelText = cancelText; + this.otherText = otherText; + } + + public MessageDialog(int titleResId, int messageResId, int okTextResId, int cancelTextResId, int otherTextResId) { + this.title = getString(titleResId); + this.message = getString(messageResId); + this.okText = getString(okTextResId); + this.cancelText = getString(cancelTextResId); + this.otherText = getString(otherTextResId); + } + + public static MessageDialog show(CharSequence title, CharSequence message, CharSequence okText, CharSequence cancelText, CharSequence otherText) { + MessageDialog messageDialog = new MessageDialog(title, message, okText, cancelText, otherText); + messageDialog.show(); + return messageDialog; + } + + public static MessageDialog show(int titleResId, int messageResId, int okTextResId, int cancelTextResId, int otherTextResId) { + MessageDialog messageDialog = new MessageDialog(titleResId, messageResId, okTextResId, cancelTextResId, otherTextResId); + messageDialog.show(); + return messageDialog; + } + + protected DialogImpl dialogImpl; + + public MessageDialog show() { + if (isHide && getDialogView() != null && isShow) { + if (hideWithExitAnim && getDialogImpl() != null) { + getDialogView().setVisibility(View.VISIBLE); + getDialogImpl().getDialogXAnimImpl().doShowAnim(me, getDialogImpl().bkg); + } else { + getDialogView().setVisibility(View.VISIBLE); + } + return this; + } + super.beforeShow(); + if (getDialogView() == null) { + int layoutId = style.layout(isLightTheme()); + layoutId = layoutId == 0 ? (isLightTheme() ? R.layout.layout_dialogx_material : R.layout.layout_dialogx_material_dark) : layoutId; + + View dialogView = createView(layoutId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + } else { + show(getDialogView()); + } + return this; + } + + public void show(Activity activity) { + super.beforeShow(); + if (getDialogView() == null) { + int layoutId = style.layout(isLightTheme()); + layoutId = layoutId == 0 ? (isLightTheme() ? R.layout.layout_dialogx_material : R.layout.layout_dialogx_material_dark) : layoutId; + + View dialogView = createView(layoutId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(activity, dialogView); + } else { + show(activity, getDialogView()); + } + } + + public void refreshUI() { + if (getDialogImpl() == null) return; + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl != null) dialogImpl.refreshView(); + } + }); + } + + public class DialogImpl implements DialogConvertViewInterface { + + private List blurViews; + + public DialogXBaseRelativeLayout boxRoot; + public MaxRelativeLayout bkg; + public TextView txtDialogTitle; + public ScrollController scrollView; + public TextView txtDialogTip; + public ViewGroup boxList; + public RelativeLayout boxCustom; + public EditText txtInput; + public LinearLayout boxButton; + public TextView btnSelectOther; + public View spaceOtherButton; + public View splitHorizontal; + public TextView btnSelectNegative; + public TextView btnSelectPositive; + + public DialogImpl(View convertView) { + if (convertView == null) return; + setDialogView(convertView); + boxRoot = convertView.findViewById(R.id.box_root); + bkg = convertView.findViewById(R.id.bkg); + txtDialogTitle = convertView.findViewById(R.id.txt_dialog_title); + scrollView = convertView.findViewById(R.id.scrollView); + txtDialogTip = convertView.findViewById(R.id.txt_dialog_tip); + boxList = convertView.findViewById(R.id.box_list); + boxCustom = convertView.findViewById(R.id.box_custom); + txtInput = convertView.findViewById(R.id.txt_input); + boxButton = convertView.findViewById(R.id.box_button); + btnSelectOther = convertView.findViewById(R.id.btn_selectOther); + spaceOtherButton = convertView.findViewById(R.id.space_other_button); + splitHorizontal = convertView.findViewWithTag("split"); + btnSelectNegative = convertView.findViewById(R.id.btn_selectNegative); + btnSelectPositive = convertView.findViewById(R.id.btn_selectPositive); + + blurViews = findAllBlurView(convertView); + + init(); + + dialogImpl = this; + refreshView(); + } + + public void init() { + buttonSelectResult = BUTTON_SELECT_RESULT.NONE; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(getThisOrderIndex()); + } + + if (titleTextInfo == null) titleTextInfo = DialogX.titleTextInfo; + if (messageTextInfo == null) messageTextInfo = DialogX.messageTextInfo; + if (okTextInfo == null) okTextInfo = DialogX.okButtonTextInfo; + if (okTextInfo == null) okTextInfo = DialogX.buttonTextInfo; + if (cancelTextInfo == null) cancelTextInfo = DialogX.buttonTextInfo; + if (otherTextInfo == null) otherTextInfo = DialogX.buttonTextInfo; + if (inputInfo == null) inputInfo = DialogX.inputInfo; + if (backgroundColor == null) backgroundColor = DialogX.backgroundColor; + + txtDialogTitle.getPaint().setFakeBoldText(true); + btnSelectNegative.getPaint().setFakeBoldText(true); + btnSelectPositive.getPaint().setFakeBoldText(true); + btnSelectOther.getPaint().setFakeBoldText(true); + + txtDialogTip.setMovementMethod(LinkMovementMethod.getInstance()); + + boxRoot.setBkgAlpha(0f); + boxRoot.setParentDialog(me); + boxRoot.setOnLifecycleCallBack(new DialogXBaseRelativeLayout.OnLifecycleCallBack() { + @Override + public void onShow() { + isShow = true; + preShow = false; + + setLifecycleState(Lifecycle.State.CREATED); + + onDialogShow(); + getDialogLifecycleCallback().onShow(me); + MessageDialog.this.onShow(me); + + getDialogXAnimImpl().doShowAnim(me, bkg); + + if (style.messageDialogBlurSettings() != null && style.messageDialogBlurSettings().blurBackground()) { + bkg.post(new Runnable() { + @Override + public void run() { + Integer blurFrontColor = null; + Float dialogXRadius = null; + if (style.messageDialogBlurSettings() != null) { + blurFrontColor = getColorNullable(getIntStyleAttr(style.messageDialogBlurSettings().blurForwardColorRes(isLightTheme()))); + dialogXRadius = getFloatStyleAttr((float) style.messageDialogBlurSettings().blurBackgroundRoundRadiusPx()); + } + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setOverlayColor(backgroundColor == null ? blurFrontColor : backgroundColor); + ((BlurViewType) blurView).setRadiusPx(dialogXRadius); + } + } + + setLifecycleState(Lifecycle.State.RESUMED); + } + }); + } + + if (autoShowInputKeyboard) { + txtInput.postDelayed(new Runnable() { + @Override + public void run() { + if (txtInput == null) return; + txtInput.requestFocus(); + txtInput.setFocusableInTouchMode(true); + imeShow(txtInput, true); + txtInput.setSelection(txtInput.getText().length()); + if (inputInfo != null && inputInfo.isSelectAllText()) { + txtInput.selectAll(); + } + } + }, 300); + } else { + if (inputInfo != null && inputInfo.isSelectAllText()) { + txtInput.clearFocus(); + txtInput.requestFocus(); + txtInput.selectAll(); + } + } + } + + @Override + public void onDismiss() { + isShow = false; + getDialogLifecycleCallback().onDismiss(me); + MessageDialog.this.onDismiss(me); + setLifecycleState(Lifecycle.State.DESTROYED); + dialogLifecycleCallback = null; + + System.gc(); + } + }); + + boxRoot.setOnBackPressedListener(new DialogXBaseRelativeLayout.PrivateBackPressedListener() { + @Override + public boolean onBackPressed() { + if (onBackPressedListener != null) { + if (onBackPressedListener.onBackPressed(me)) { + dismiss(); + } + } else { + if (isCancelable()) { + dismiss(); + } + } + return true; + } + }); + btnSelectPositive.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + buttonSelectResult = BUTTON_SELECT_RESULT.BUTTON_OK; + if (txtInput != null) { + imeShow(txtInput, false); + } + haptic(v); + if (okButtonClickListener != null) { + if (okButtonClickListener instanceof OnInputDialogButtonClickListener) { + String s = txtInput == null ? "" : txtInput.getText().toString(); + if (!((OnInputDialogButtonClickListener) okButtonClickListener).onClick(me, v, s)) { + doDismiss(v); + } + } else if (okButtonClickListener instanceof OnDialogButtonClickListener) { + if (!((OnDialogButtonClickListener) okButtonClickListener).onClick(me, v)) { + doDismiss(v); + } + } else if (okButtonClickListener instanceof OnMenuButtonClickListener) { + if (!((OnMenuButtonClickListener) okButtonClickListener).onClick(me, v)) { + doDismiss(v); + } + } + } else { + doDismiss(v); + } + } + }); + btnSelectNegative.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + buttonSelectResult = BUTTON_SELECT_RESULT.BUTTON_CANCEL; + if (txtInput != null) { + imeShow(txtInput, false); + } + haptic(v); + if (cancelButtonClickListener != null) { + if (cancelButtonClickListener instanceof OnInputDialogButtonClickListener) { + String s = txtInput == null ? "" : txtInput.getText().toString(); + if (!((OnInputDialogButtonClickListener) cancelButtonClickListener).onClick(me, v, s)) { + doDismiss(v); + } + } else if (cancelButtonClickListener instanceof OnMenuButtonClickListener) { + if (!((OnMenuButtonClickListener) cancelButtonClickListener).onClick(me, v)) { + doDismiss(v); + } + } else { + if (!((OnDialogButtonClickListener) cancelButtonClickListener).onClick(me, v)) { + doDismiss(v); + } + } + } else { + doDismiss(v); + } + } + }); + btnSelectOther.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + buttonSelectResult = BUTTON_SELECT_RESULT.BUTTON_OTHER; + if (txtInput != null) { + imeShow(txtInput, false); + } + haptic(v); + if (otherButtonClickListener != null) { + if (otherButtonClickListener instanceof OnInputDialogButtonClickListener) { + String s = txtInput == null ? "" : txtInput.getText().toString(); + if (!((OnInputDialogButtonClickListener) otherButtonClickListener).onClick(me, v, s)) { + doDismiss(v); + } + } else if (otherButtonClickListener instanceof OnMenuButtonClickListener) { + if (!((OnMenuButtonClickListener) otherButtonClickListener).onClick(me, v)) { + doDismiss(v); + } + } else { + if (!((OnDialogButtonClickListener) otherButtonClickListener).onClick(me, v)) { + doDismiss(v); + } + } + } else { + doDismiss(v); + } + } + }); + + onDialogInit(); + } + + public void refreshView() { + if (boxRoot == null || getOwnActivity() == null) { + return; + } + + boxRoot.setAutoUnsafePlacePadding(isEnableImmersiveMode()); + // 修改下划线颜色 + if (inputInfo != null && inputInfo.getBottomLineColor() != null) { + txtInput.getBackground().mutate().setColorFilter(inputInfo.getBottomLineColor(), PorterDuff.Mode.SRC_ATOP); + } + // 修改光标颜色 + if (inputInfo != null && inputInfo.getCursorColor() != null) { + int cursorColor = inputInfo.getCursorColor(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + if (txtInput.getTextCursorDrawable() != null) { + txtInput.getTextCursorDrawable().mutate().setColorFilter((new PorterDuffColorFilter(cursorColor, PorterDuff.Mode.SRC_ATOP))); + } else { + try { + @SuppressLint("SoonBlockedPrivateApi") Field field = TextView.class.getDeclaredField("mCursorDrawableRes"); + field.setAccessible(true); + field.set(txtInput, R.drawable.rect_dialogx_defalut_edittxt_cursor); + txtInput.getTextCursorDrawable().mutate().setColorFilter((new PorterDuffColorFilter(cursorColor, PorterDuff.Mode.SRC_ATOP))); + } catch (Throwable throwable) { + log("DialogX: 在对话框" + dialogKey() + "中设置光标颜色时发生错误!"); + if (DialogX.DEBUGMODE) { + throwable.printStackTrace(); + } + } + } + } else { + // Thanks for @Jared Rummler https://stackoverflow.com/questions/11554078/set-textcursordrawable-programmatically/57555148#57555148 + try { + Field fCursorDrawableRes = TextView.class.getDeclaredField("mCursorDrawableRes"); + fCursorDrawableRes.setAccessible(true); + int mCursorDrawableRes = fCursorDrawableRes.getInt(txtInput); + Field fEditor = TextView.class.getDeclaredField("mEditor"); + fEditor.setAccessible(true); + Object editor = fEditor.get(txtInput); + Class clazz = editor.getClass(); + Field fCursorDrawable = clazz.getDeclaredField("mCursorDrawable"); + fCursorDrawable.setAccessible(true); + Drawable[] drawables = new Drawable[2]; + drawables[0] = txtInput.getContext().getResources().getDrawable(mCursorDrawableRes); + drawables[1] = txtInput.getContext().getResources().getDrawable(mCursorDrawableRes); + drawables[0].setColorFilter(cursorColor, PorterDuff.Mode.SRC_IN); + drawables[1].setColorFilter(cursorColor, PorterDuff.Mode.SRC_IN); + fCursorDrawable.set(editor, drawables); + } catch (Throwable throwable) { + log("DialogX: 在对话框" + dialogKey() + "中设置光标颜色时发生错误!"); + if (DialogX.DEBUGMODE) { + throwable.printStackTrace(); + } + } + } + } + + boxRoot.setRootPadding(screenPaddings[0], screenPaddings[1], screenPaddings[2], screenPaddings[3]); + if (backgroundColor != null) { + tintColor(bkg, backgroundColor); + if (style.tintButtonBackground()) { + tintColor(btnSelectOther, backgroundColor); + tintColor(btnSelectNegative, backgroundColor); + tintColor(btnSelectPositive, backgroundColor); + } + + if (blurViews != null) { + log("#blurViews != null"); + for (View blurView : blurViews) { + log("#blurView: " + blurView); + ((BlurViewType) blurView).setOverlayColor(backgroundColor); + } + } + } + + bkg.setMaxWidth(getMaxWidth()); + bkg.setMaxHeight(getMaxHeight()); + bkg.setMinimumWidth(getMinWidth()); + bkg.setMinimumHeight(getMinHeight()); + + View inputBoxView = boxRoot.findViewWithTag("dialogx_editbox"); + if (me instanceof InputDialog) { + if (inputBoxView != null) { + inputBoxView.setVisibility(View.VISIBLE); + } + txtInput.setVisibility(View.VISIBLE); + boxRoot.bindFocusView(txtInput); + } else { + if (inputBoxView != null) { + inputBoxView.setVisibility(View.GONE); + } + txtInput.setVisibility(View.GONE); + } + boxRoot.setClickable(true); + if (maskColor != null) { + boxRoot.setBackgroundColor(maskColor); + } + if (backgroundRadius > -1) { + // GradientDrawable gradientDrawable = (GradientDrawable) bkg.getBackground(); + // if (gradientDrawable != null) gradientDrawable.setCornerRadius(backgroundRadius); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + bkg.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), backgroundRadius); + } + }); + bkg.setClipToOutline(true); + } + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setRadiusPx(backgroundRadius); + } + } + } + + showText(txtDialogTitle, title == null ? DialogX.defaultMessageDialogTitleText : title); + showText(txtDialogTip, message); + showText(btnSelectPositive, okText); + showText(btnSelectNegative, cancelText); + showText(btnSelectOther, otherText); + + txtInput.setText(inputText); + txtInput.setHint(inputHintText); + if (spaceOtherButton != null) { + if (otherText == null) { + spaceOtherButton.setVisibility(View.GONE); + } else { + spaceOtherButton.setVisibility(View.VISIBLE); + } + } + + useTextInfo(txtDialogTitle, titleTextInfo); + useTextInfo(txtDialogTip, messageTextInfo); + useTextInfo(btnSelectPositive, okTextInfo); + useTextInfo(btnSelectNegative, cancelTextInfo); + useTextInfo(btnSelectOther, otherTextInfo); + + if (boxButton != null) { + boxButton.setVisibility((btnSelectNegative != null && btnSelectNegative.getVisibility() == View.VISIBLE) || + (btnSelectOther != null && btnSelectOther.getVisibility() == View.VISIBLE) || + (btnSelectPositive != null && btnSelectPositive.getVisibility() == View.VISIBLE) ? + View.VISIBLE : View.GONE); + } + if (titleIcon != null) { + int size = (int) txtDialogTitle.getTextSize(); + titleIcon.setBounds(0, 0, size, size); + txtDialogTitle.setCompoundDrawablePadding(dip2px(10)); + txtDialogTitle.setCompoundDrawables(titleIcon, null, null, null); + } + + if (inputInfo != null) { + int inputType = inputInfo.getInputType(); + if (inputInfo.getMAX_LENGTH() != -1) { + int inputClass = inputType & InputType.TYPE_MASK_CLASS; + if (inputClass != InputType.TYPE_CLASS_TEXT && + inputClass != InputType.TYPE_CLASS_NUMBER && + inputClass != InputType.TYPE_CLASS_PHONE && + inputClass != InputType.TYPE_CLASS_DATETIME) { + inputType = (inputType & ~InputType.TYPE_MASK_CLASS) | InputType.TYPE_CLASS_TEXT; + } + txtInput.setFilters(new InputFilter[]{new InputFilter.LengthFilter(inputInfo.getMAX_LENGTH())}); + } + if (inputInfo.isMultipleLines()) { + inputType = inputType | InputType.TYPE_TEXT_FLAG_MULTI_LINE; + } + txtInput.setInputType(inputType); + if (inputInfo.getTextInfo() != null) { + useTextInfo(txtInput, inputInfo.getTextInfo()); + } + if (inputInfo.getInputFilters() != null && inputInfo.getInputFilters().length > 0) { + txtInput.setFilters(inputInfo.getInputFilters()); + } + } + + int visibleButtonCount = 0; + if (!isNull(okText)) { + visibleButtonCount = visibleButtonCount + 1; + } + if (!isNull(cancelText)) { + visibleButtonCount = visibleButtonCount + 1; + } + if (!isNull(otherText)) { + visibleButtonCount = visibleButtonCount + 1; + } + + if (splitHorizontal != null) { + splitHorizontal.setBackgroundColor(getColor(style.splitColorRes(isLightTheme()))); + } + + boxButton.setOrientation(buttonOrientation); + if (buttonOrientation == LinearLayout.VERTICAL) { + // 纵向 + if (style.verticalButtonOrder() != null && style.verticalButtonOrder().length != 0) { + boxButton.removeAllViews(); + for (int buttonType : style.verticalButtonOrder()) { + switch (buttonType) { + case DialogXStyle.BUTTON_OK: + boxButton.addView(btnSelectPositive); + if (style.overrideVerticalButtonRes() != null) { + btnSelectPositive.setBackgroundResource(style.overrideVerticalButtonRes().overrideVerticalOkButtonBackgroundRes(visibleButtonCount, isLightTheme())); + } + break; + case DialogXStyle.BUTTON_OTHER: + boxButton.addView(btnSelectOther); + if (style.overrideVerticalButtonRes() != null) { + btnSelectOther.setBackgroundResource(style.overrideVerticalButtonRes().overrideVerticalOtherButtonBackgroundRes(visibleButtonCount, isLightTheme())); + } + break; + case DialogXStyle.BUTTON_CANCEL: + boxButton.addView(btnSelectNegative); + if (style.overrideVerticalButtonRes() != null) { + btnSelectNegative.setBackgroundResource(style.overrideVerticalButtonRes().overrideVerticalCancelButtonBackgroundRes(visibleButtonCount, isLightTheme())); + } + break; + case DialogXStyle.SPACE: + Space space = new Space(getOwnActivity()); + LinearLayout.LayoutParams spaceLp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + spaceLp.weight = 1; + boxButton.addView(space, spaceLp); + break; + case DialogXStyle.SPLIT: + View splitView = new View(getOwnActivity()); + splitView.setBackgroundColor(getResources().getColor(style.splitColorRes(isLightTheme()))); + LinearLayout.LayoutParams viewLp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, style.splitWidthPx()); + boxButton.addView(splitView, viewLp); + break; + } + } + } + } else { + // 横向 + if (style.horizontalButtonOrder() != null && style.horizontalButtonOrder().length != 0) { + boxButton.removeAllViews(); + for (int buttonType : style.horizontalButtonOrder()) { + switch (buttonType) { + case DialogXStyle.BUTTON_OK: + boxButton.addView(btnSelectPositive); + if (style.overrideHorizontalButtonRes() != null) { + btnSelectPositive.setBackgroundResource(style.overrideHorizontalButtonRes().overrideHorizontalOkButtonBackgroundRes(visibleButtonCount, isLightTheme())); + } + break; + case DialogXStyle.BUTTON_OTHER: + boxButton.addView(btnSelectOther); + if (style.overrideHorizontalButtonRes() != null) { + btnSelectOther.setBackgroundResource(style.overrideHorizontalButtonRes().overrideHorizontalOtherButtonBackgroundRes(visibleButtonCount, isLightTheme())); + } + break; + case DialogXStyle.BUTTON_CANCEL: + boxButton.addView(btnSelectNegative); + if (style.overrideHorizontalButtonRes() != null) { + btnSelectNegative.setBackgroundResource(style.overrideHorizontalButtonRes().overrideHorizontalCancelButtonBackgroundRes(visibleButtonCount, isLightTheme())); + } + break; + case DialogXStyle.SPACE: + if (boxButton.getChildCount() >= 1) { + if (boxButton.getChildAt(boxButton.getChildCount() - 1).getVisibility() == View.GONE) { + break; + } + } else { + break; + } + Space space = new Space(getOwnActivity()); + LinearLayout.LayoutParams spaceLp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + spaceLp.weight = 1; + boxButton.addView(space, spaceLp); + break; + case DialogXStyle.SPLIT: + if (boxButton.getChildCount() >= 1) { + if (boxButton.getChildAt(boxButton.getChildCount() - 1).getVisibility() == View.GONE) { + break; + } + } else { + break; + } + View splitView = new View(getOwnActivity()); + splitView.setBackgroundColor(getResources().getColor(style.splitColorRes(isLightTheme()))); + LinearLayout.LayoutParams viewLp = new LinearLayout.LayoutParams(style.splitWidthPx(), ViewGroup.LayoutParams.MATCH_PARENT); + boxButton.addView(splitView, viewLp); + break; + } + } + } + } + + // Events + if (bkgInterceptTouch) { + if (isCancelable()) { + boxRoot.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onBackgroundMaskClickListener == null || !onBackgroundMaskClickListener.onClick(me, v)) { + doDismiss(v); + } + } + }); + } else { + boxRoot.setOnClickListener(null); + } + } else { + boxRoot.setClickable(false); + } + + if (onBindView != null && onBindView.getCustomView() != null) { + onBindView.bindParent(boxCustom, me); + boxCustom.setVisibility(View.VISIBLE); + if (onBindView.getCustomView() instanceof ScrollController) { + if (scrollView instanceof DialogScrollView) { + ((DialogScrollView) scrollView).setVerticalScrollBarEnabled(false); + } + scrollView = (ScrollController) onBindView.getCustomView(); + } else { + View scrollController = onBindView.getCustomView().findViewWithTag("ScrollController"); + if (scrollController instanceof ScrollController) { + if (scrollView instanceof DialogScrollView) { + ((DialogScrollView) scrollView).setVerticalScrollBarEnabled(false); + } + scrollView = (ScrollController) scrollController; + } + } + } else { + boxCustom.setVisibility(View.GONE); + } + onDialogRefreshUI(); + } + + public void doDismiss(View v) { + if (MessageDialog.this.preDismiss(MessageDialog.this)) { + return; + } + if (v != null) v.setEnabled(false); + if (getOwnActivity() == null) return; + + if (!dismissAnimFlag && getDialogXAnimImpl() != null) { + dismissAnimFlag = true; + getDialogXAnimImpl().doExitAnim(MessageDialog.this, bkg); + runOnMainDelay(new Runnable() { + @Override + public void run() { + if (boxRoot != null) { + boxRoot.setVisibility(View.GONE); + } + dismiss(getDialogView()); + } + }, getExitAnimationDuration(null)); + } + } + + protected DialogXAnimInterface getDialogXAnimImpl() { + if (dialogXAnimImpl == null) { + dialogXAnimImpl = new DialogXAnimInterface() { + @Override + public void doShowAnim(MessageDialog dialog, ViewGroup dialogBodyView) { + int enterAnimResId = style.enterAnimResId() == 0 ? R.anim.anim_dialogx_default_enter : style.enterAnimResId(); + if (overrideEnterAnimRes != 0) { + enterAnimResId = overrideEnterAnimRes; + } + if (customEnterAnimResId != 0) { + enterAnimResId = customEnterAnimResId; + } + Animation enterAnim = AnimationUtils.loadAnimation(getOwnActivity(), enterAnimResId); + long enterAnimationDuration = getEnterAnimationDuration(enterAnim); + enterAnim.setDuration(enterAnimationDuration); + enterAnim.setInterpolator(new DecelerateInterpolator()); + bkg.startAnimation(enterAnim); + + ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f); + bkgAlpha.setDuration(enterAnimationDuration); + bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + boxRoot.setBkgAlpha((Float) animation.getAnimatedValue()); + } + }); + bkgAlpha.start(); + } + + @Override + public void doExitAnim(MessageDialog dialog, ViewGroup dialogBodyView) { + int exitAnimResId = style.exitAnimResId() == 0 ? R.anim.anim_dialogx_default_exit : style.exitAnimResId(); + if (overrideExitAnimRes != 0) { + exitAnimResId = overrideExitAnimRes; + } + if (customExitAnimResId != 0) { + exitAnimResId = customExitAnimResId; + } + Animation exitAnim = AnimationUtils.loadAnimation(getOwnActivity(), exitAnimResId); + long exitAnimationDuration = getExitAnimationDuration(exitAnim); + exitAnim.setInterpolator(new AccelerateInterpolator()); + exitAnim.setDuration(exitAnimationDuration); + bkg.startAnimation(exitAnim); + + ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1f, 0f); + bkgAlpha.setDuration(exitAnimationDuration); + bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + boxRoot.setBkgAlpha((Float) animation.getAnimatedValue()); + } + }); + bkgAlpha.start(); + } + }; + } + return dialogXAnimImpl; + } + + public long getExitAnimationDuration(@Nullable Animation defaultExitAnim) { + if (defaultExitAnim == null && bkg.getAnimation() != null) { + defaultExitAnim = bkg.getAnimation(); + } + long exitAnimDurationTemp = (defaultExitAnim == null || defaultExitAnim.getDuration() == 0) ? 300 : defaultExitAnim.getDuration(); + if (overrideExitDuration >= 0) { + exitAnimDurationTemp = overrideExitDuration; + } + if (exitAnimDuration != -1) { + exitAnimDurationTemp = exitAnimDuration; + } + return exitAnimDurationTemp; + } + + public long getEnterAnimationDuration(@Nullable Animation defaultEnterAnim) { + if (defaultEnterAnim == null && bkg.getAnimation() != null) { + defaultEnterAnim = bkg.getAnimation(); + } + long enterAnimDurationTemp = (defaultEnterAnim == null || defaultEnterAnim.getDuration() == 0) ? 300 : defaultEnterAnim.getDuration(); + if (overrideEnterDuration >= 0) { + enterAnimDurationTemp = overrideEnterDuration; + } + if (enterAnimDuration >= 0) { + enterAnimDurationTemp = enterAnimDuration; + } + return enterAnimDurationTemp; + } + } + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + public void dismiss() { + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl == null) return; + dialogImpl.doDismiss(dialogImpl.bkg); + } + }); + } + + public DialogLifecycleCallback getDialogLifecycleCallback() { + return dialogLifecycleCallback == null ? new DialogLifecycleCallback() { + } : dialogLifecycleCallback; + } + + public MessageDialog setDialogLifecycleCallback(DialogLifecycleCallback dialogLifecycleCallback) { + this.dialogLifecycleCallback = dialogLifecycleCallback; + if (isShow) dialogLifecycleCallback.onShow(me); + return this; + } + + public MessageDialog setStyle(DialogXStyle style) { + this.style = style; + return this; + } + + public MessageDialog setTheme(DialogX.THEME theme) { + this.theme = theme; + return this; + } + + public CharSequence getOkButton() { + return okText; + } + + public MessageDialog setOkButton(CharSequence okText) { + this.okText = okText; + refreshUI(); + return this; + } + + public MessageDialog setOkButton(int okTextRedId) { + this.okText = getString(okTextRedId); + refreshUI(); + return this; + } + + public MessageDialog setOkButton(OnDialogButtonClickListener okButtonClickListener) { + this.okButtonClickListener = okButtonClickListener; + return this; + } + + public MessageDialog setOkButton(CharSequence okText, OnDialogButtonClickListener okButtonClickListener) { + this.okText = okText; + this.okButtonClickListener = okButtonClickListener; + refreshUI(); + return this; + } + + public MessageDialog setOkButton(int okTextRedId, OnDialogButtonClickListener okButtonClickListener) { + this.okText = getString(okTextRedId); + this.okButtonClickListener = okButtonClickListener; + refreshUI(); + return this; + } + + public CharSequence getCancelButton() { + return cancelText; + } + + public MessageDialog setCancelButton(CharSequence cancelText) { + this.cancelText = cancelText; + refreshUI(); + return this; + } + + public MessageDialog setCancelButton(int cancelTextResId) { + this.cancelText = getString(cancelTextResId); + refreshUI(); + return this; + } + + public MessageDialog setCancelButton(OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelButtonClickListener = cancelButtonClickListener; + return this; + } + + public MessageDialog setCancelButton(CharSequence cancelText, OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelText = cancelText; + this.cancelButtonClickListener = cancelButtonClickListener; + refreshUI(); + return this; + } + + public MessageDialog setCancelButton(int cancelTextResId, OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelText = getString(cancelTextResId); + this.cancelButtonClickListener = cancelButtonClickListener; + refreshUI(); + return this; + } + + public CharSequence getOtherButton() { + return otherText; + } + + public MessageDialog setOtherButton(CharSequence otherText) { + this.otherText = otherText; + refreshUI(); + return this; + } + + public MessageDialog setOtherButton(int otherTextResId) { + this.otherText = getString(otherTextResId); + refreshUI(); + return this; + } + + public MessageDialog setOtherButton(OnDialogButtonClickListener otherButtonClickListener) { + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + public MessageDialog setOtherButton(CharSequence otherText, OnDialogButtonClickListener otherButtonClickListener) { + this.otherText = otherText; + this.otherButtonClickListener = otherButtonClickListener; + refreshUI(); + return this; + } + + public MessageDialog setOtherButton(int otherTextResId, OnDialogButtonClickListener otherButtonClickListener) { + this.otherText = getString(otherTextResId); + this.otherButtonClickListener = otherButtonClickListener; + refreshUI(); + return this; + } + + public OnDialogButtonClickListener getOkButtonClickListener() { + return (OnDialogButtonClickListener) okButtonClickListener; + } + + public MessageDialog setOkButtonClickListener(OnDialogButtonClickListener okButtonClickListener) { + this.okButtonClickListener = okButtonClickListener; + return this; + } + + public OnDialogButtonClickListener getCancelButtonClickListener() { + return (OnDialogButtonClickListener) cancelButtonClickListener; + } + + public MessageDialog setCancelButtonClickListener(OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelButtonClickListener = cancelButtonClickListener; + return this; + } + + public OnDialogButtonClickListener getOtherButtonClickListener() { + return (OnDialogButtonClickListener) otherButtonClickListener; + } + + public MessageDialog setOtherButtonClickListener(OnDialogButtonClickListener otherButtonClickListener) { + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + public CharSequence getTitle() { + return title; + } + + public MessageDialog setTitle(CharSequence title) { + this.title = title; + refreshUI(); + return this; + } + + public MessageDialog setTitle(int titleResId) { + this.title = getString(titleResId); + refreshUI(); + return this; + } + + public CharSequence getMessage() { + return message; + } + + public MessageDialog setMessage(CharSequence message) { + this.message = message; + refreshUI(); + return this; + } + + public MessageDialog setMessage(int messageResId) { + this.message = getString(messageResId); + refreshUI(); + return this; + } + + public TextInfo getTitleTextInfo() { + return titleTextInfo; + } + + public MessageDialog setTitleTextInfo(TextInfo titleTextInfo) { + this.titleTextInfo = titleTextInfo; + refreshUI(); + return this; + } + + public TextInfo getMessageTextInfo() { + return messageTextInfo; + } + + public MessageDialog setMessageTextInfo(TextInfo messageTextInfo) { + this.messageTextInfo = messageTextInfo; + refreshUI(); + return this; + } + + public TextInfo getOkTextInfo() { + return okTextInfo; + } + + public MessageDialog setOkTextInfo(TextInfo okTextInfo) { + this.okTextInfo = okTextInfo; + refreshUI(); + return this; + } + + public MessageDialog setHapticFeedbackEnabled(boolean isHapticFeedbackEnabled) { + this.isHapticFeedbackEnabled = isHapticFeedbackEnabled ? 1 : 0; + return this; + } + + public TextInfo getCancelTextInfo() { + return cancelTextInfo; + } + + public MessageDialog setCancelTextInfo(TextInfo cancelTextInfo) { + this.cancelTextInfo = cancelTextInfo; + refreshUI(); + return this; + } + + public TextInfo getOtherTextInfo() { + return otherTextInfo; + } + + public MessageDialog setOtherTextInfo(TextInfo otherTextInfo) { + this.otherTextInfo = otherTextInfo; + refreshUI(); + return this; + } + + public int getButtonOrientation() { + return buttonOrientation; + } + + public MessageDialog setButtonOrientation(int buttonOrientation) { + this.buttonOrientation = buttonOrientation; + refreshUI(); + return this; + } + + public boolean isCancelable() { + if (privateCancelable != null) { + return privateCancelable == BOOLEAN.TRUE; + } + if (overrideCancelable != null) { + return overrideCancelable == BOOLEAN.TRUE; + } + return cancelable; + } + + public MessageDialog setCancelable(boolean cancelable) { + this.privateCancelable = cancelable ? BOOLEAN.TRUE : BOOLEAN.FALSE; + refreshUI(); + return this; + } + + public OnBackPressedListener getOnBackPressedListener() { + return (OnBackPressedListener) onBackPressedListener; + } + + public MessageDialog setOnBackPressedListener(OnBackPressedListener onBackPressedListener) { + this.onBackPressedListener = onBackPressedListener; + return this; + } + + public DialogImpl getDialogImpl() { + return dialogImpl; + } + + public MessageDialog setCustomView(OnBindView onBindView) { + this.onBindView = onBindView; + refreshUI(); + return this; + } + + public View getCustomView() { + if (onBindView == null) return null; + return onBindView.getCustomView(); + } + + public MessageDialog removeCustomView() { + this.onBindView.clean(); + refreshUI(); + return this; + } + + public int getBackgroundColor() { + return backgroundColor; + } + + public MessageDialog setBackgroundColor(@ColorInt int backgroundColor) { + this.backgroundColor = backgroundColor; + refreshUI(); + return this; + } + + public String getInputText() { + if (dialogImpl.txtInput != null) { + return dialogImpl.txtInput.getText().toString(); + } else { + return ""; + } + } + + public MessageDialog setBackgroundColorRes(@ColorRes int backgroundColorResId) { + this.backgroundColor = getColor(backgroundColorResId); + refreshUI(); + return this; + } + + public MessageDialog setMaskColor(@ColorInt int maskColor) { + this.maskColor = maskColor; + refreshUI(); + return this; + } + + public long getEnterAnimDuration() { + return enterAnimDuration; + } + + public MessageDialog setEnterAnimDuration(long enterAnimDuration) { + this.enterAnimDuration = enterAnimDuration; + return this; + } + + public long getExitAnimDuration() { + return exitAnimDuration; + } + + public MessageDialog setExitAnimDuration(long exitAnimDuration) { + this.exitAnimDuration = exitAnimDuration; + return this; + } + + @Override + public void restartDialog() { + if (getDialogView() != null) { + dismiss(getDialogView()); + isShow = false; + } + if (getDialogImpl().boxCustom != null) { + getDialogImpl().boxCustom.removeAllViews(); + } + if (getDialogImpl().boxList != null) { + getDialogImpl().boxList.removeAllViews(); + } + int layoutId = style.layout(isLightTheme()); + layoutId = layoutId == 0 ? (isLightTheme() ? R.layout.layout_dialogx_material : R.layout.layout_dialogx_material_dark) : layoutId; + + enterAnimDuration = 0; + View dialogView = createView(layoutId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + } + + public void hide() { + isHide = true; + hideWithExitAnim = false; + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + } + + protected boolean hideWithExitAnim; + + public void hideWithExitAnim() { + hideWithExitAnim = true; + isHide = true; + if (getDialogImpl() != null) { + getDialogImpl().getDialogXAnimImpl().doExitAnim(me, getDialogImpl().bkg); + + runOnMainDelay(new Runnable() { + @Override + public void run() { + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + } + }, getDialogImpl().getExitAnimationDuration(null)); + } + } + + public MessageDialog setAnimResId(int enterResId, int exitResId) { + customEnterAnimResId = enterResId; + customExitAnimResId = exitResId; + return this; + } + + public MessageDialog setEnterAnimResId(int enterResId) { + customEnterAnimResId = enterResId; + return this; + } + + public MessageDialog setExitAnimResId(int exitResId) { + customExitAnimResId = exitResId; + return this; + } + + @Override + protected void shutdown() { + dismiss(); + } + + public MessageDialog setMaxWidth(int maxWidth) { + this.maxWidth = maxWidth; + refreshUI(); + return this; + } + + public MessageDialog setMaxHeight(int maxHeight) { + this.maxHeight = maxHeight; + refreshUI(); + return this; + } + + public MessageDialog setMinHeight(int minHeight) { + this.minHeight = minHeight; + refreshUI(); + return this; + } + + public MessageDialog setMinWidth(int minWidth) { + this.minWidth = minWidth; + refreshUI(); + return this; + } + + public MessageDialog setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public boolean isBkgInterceptTouch() { + return bkgInterceptTouch; + } + + public MessageDialog setBkgInterceptTouch(boolean bkgInterceptTouch) { + this.bkgInterceptTouch = bkgInterceptTouch; + return this; + } + + public OnBackgroundMaskClickListener getOnBackgroundMaskClickListener() { + return (OnBackgroundMaskClickListener) onBackgroundMaskClickListener; + } + + public MessageDialog setOnBackgroundMaskClickListener(OnBackgroundMaskClickListener onBackgroundMaskClickListener) { + this.onBackgroundMaskClickListener = onBackgroundMaskClickListener; + return this; + } + + public MessageDialog setRadius(float radiusPx) { + backgroundRadius = radiusPx; + refreshUI(); + return this; + } + + public float getRadius() { + return backgroundRadius; + } + + public Drawable getTitleIcon() { + return titleIcon; + } + + public MessageDialog setTitleIcon(Bitmap titleIcon) { + this.titleIcon = new BitmapDrawable(getResources(), titleIcon); + refreshUI(); + return this; + } + + public MessageDialog setTitleIcon(int titleIconResId) { + this.titleIcon = getResources().getDrawable(titleIconResId); + refreshUI(); + return this; + } + + public MessageDialog setTitleIcon(Drawable titleIcon) { + this.titleIcon = titleIcon; + refreshUI(); + return this; + } + + public DialogXAnimInterface getDialogXAnimImpl() { + return dialogXAnimImpl; + } + + public MessageDialog setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public MessageDialog setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public MessageDialog setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + public BUTTON_SELECT_RESULT getButtonSelectResult() { + return buttonSelectResult; + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new MessageDialog() { + * + * @param dialog self + * @Override public void onShow(MessageDialog dialog) { + * //... + * } + * } + */ + protected void onShow(MessageDialog dialog) { + + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new MessageDialog() { + * + * @param dialog self + * @Override public boolean onDismiss(MessageDialog dialog) { + * WaitDialog.show("Please Wait..."); + * if (dialog.getButtonSelectResult() == BUTTON_SELECT_RESULT.BUTTON_OK) { + * //点击了OK的情况 + * //... + * } else { + * //其他按钮点击、对话框dismiss的情况 + * //... + * } + * return false; + * } + * } + */ + // 用于使用 new 构建实例时,override 的生命周期事件 + protected void onDismiss(MessageDialog dialog) { + + } + + public MessageDialog setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public MessageDialog onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public MessageDialog onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public MessageDialog setEnableImmersiveMode(boolean enableImmersiveMode) { + this.enableImmersiveMode = enableImmersiveMode; + refreshUI(); + return this; + } + + public MessageDialog appendMessage(CharSequence message) { + this.message = TextUtils.concat(this.message, message); + refreshUI(); + return this; + } + + public MessageDialog setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex(" + orderIndex + ") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public MessageDialog bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public MessageDialog setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public MessageDialog cleanAction(int actionId){ + dialogActionRunnableMap.remove(actionId); + return this; + } + + public MessageDialog cleanAllAction(){ + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + public void callDialogDismiss(){ + dismiss(); + } + + public MessageDialog bindDismissWithLifecycleOwner(LifecycleOwner owner){ + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/MessageMenu.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/MessageMenu.java new file mode 100644 index 0000000..ab51bf5 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/MessageMenu.java @@ -0,0 +1,1601 @@ +package com.kongzue.dialogx.dialogs; + +import static android.view.View.OVER_SCROLL_NEVER; + +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.text.TextUtils; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.LinearLayout; + +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BottomMenuListViewTouchEvent; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.MenuItemLayoutRefreshCallback; +import com.kongzue.dialogx.interfaces.MenuItemTextInfoInterceptor; +import com.kongzue.dialogx.interfaces.OnBackPressedListener; +import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener; +import com.kongzue.dialogx.interfaces.OnBindView; +import com.kongzue.dialogx.interfaces.OnMenuButtonClickListener; +import com.kongzue.dialogx.interfaces.OnDialogButtonClickListener; +import com.kongzue.dialogx.interfaces.OnIconChangeCallBack; +import com.kongzue.dialogx.interfaces.OnMenuItemClickListener; +import com.kongzue.dialogx.interfaces.OnMenuItemSelectListener; +import com.kongzue.dialogx.interfaces.SELECT_MODE; +import com.kongzue.dialogx.util.BottomMenuArrayAdapter; +import com.kongzue.dialogx.util.ItemDivider; +import com.kongzue.dialogx.util.MessageMenuArrayAdapter; +import com.kongzue.dialogx.util.TextInfo; +import com.kongzue.dialogx.util.views.DialogListView; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class MessageMenu extends MessageDialog { + + protected MessageMenu me = this; + protected boolean allowInterceptTouch = true; + protected int selectionIndex = -1; + protected SELECT_MODE selectMode = SELECT_MODE.NONE; + protected ArrayList selectionItems; + + protected boolean showSelectedBackgroundTips = false; + protected MenuItemLayoutRefreshCallback menuMenuItemLayoutRefreshCallback; + protected Map menuUsability = new HashMap(); + protected ItemDivider itemDivider; + + protected OnMenuItemClickListener onMenuItemClickListener; + + public static MessageMenu build() { + return new MessageMenu(); + } + + public static MessageMenu build(DialogXStyle style) { + return new MessageMenu().setStyle(style); + } + + public static MessageMenu build(OnBindView onBindView) { + return new MessageMenu().setCustomView(onBindView); + } + + protected MessageMenu() { + super(); + } + + protected OnIconChangeCallBack onIconChangeCallBack; + protected MenuItemTextInfoInterceptor menuItemTextInfoInterceptor; + protected DialogListView listView; + protected TextInfo menuTextInfo; + protected BaseAdapter menuListAdapter; + protected List menuList; + protected List iconResIds; + protected boolean autoTintIconInLightOrDarkMode = true; + + public static MessageMenu show(List menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(List menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu showStringList(List menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.setMenuStringList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu showStringList(List menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.setMenuStringList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(String... menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(String[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence[] menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence title, CharSequence message, List menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence title, CharSequence message, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence title, List menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence title, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu showStringList(CharSequence title, CharSequence message, List menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuStringList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu showStringList(CharSequence title, CharSequence message, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuStringList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence title, CharSequence message, String[] menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence title, CharSequence message, String[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence title, CharSequence message, CharSequence[] menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence title, CharSequence message, CharSequence[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(String title, String message, List menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(String title, String message, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu showStringList(String title, String message, List menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuStringList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu showStringList(String title, String message, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuStringList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(String title, String message, String[] menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(String title, String message, String[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(String title, String message, CharSequence[] menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(String title, String message, CharSequence[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(int titleResId, int messageResId, List menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.message = messageMenu.getString(messageResId); + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(int titleResId, List menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu showStringList(int titleResId, int messageResId, List menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.message = messageMenu.getString(messageResId); + messageMenu.setMenuStringList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(int titleResId, int messageResId, String[] menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.message = messageMenu.getString(messageResId); + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(int titleResId, int messageResId, CharSequence[] menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.message = messageMenu.getString(messageResId); + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(int titleResId, int messageResId, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.message = messageMenu.getString(messageResId); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(int titleResId, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu showStringList(int titleResId, int messageResId, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.message = messageMenu.getString(messageResId); + messageMenu.setMenuStringList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(int titleResId, int messageResId, String[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.message = messageMenu.getString(messageResId); + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(int titleResId, int messageResId, CharSequence[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.message = messageMenu.getString(messageResId); + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence title, CharSequence[] menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence title, String[] menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence title, CharSequence[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence title, String[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(int titleResId, CharSequence[] menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(int titleResId, String[] menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(int titleResId, CharSequence[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(int titleResId, String[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + + private float touchDownY; + + public static final int ITEM_CLICK_DELAY = 100; + private long lastClickTime = 0; + private int[] resultArray; + private CharSequence[] selectTextArray; + + @Override + protected void onDialogShow() { + if (getDialogImpl() != null) { + getDialogImpl().boxList.setVisibility(View.VISIBLE); + + if (!isAllowInterceptTouch()) { + getDialogImpl().bkg.setMaxHeight(maxHeight); + if (maxHeight != 0) { + dialogImpl.scrollView.lockScroll(true); + } + } + + int dividerDrawableResId = 0; + int dividerHeight = 1; + if (style.overrideBottomDialogRes() != null) { + dividerDrawableResId = style.overrideBottomDialogRes().overrideMenuDividerDrawableRes(isLightTheme()); + dividerHeight = style.overrideBottomDialogRes().overrideMenuDividerHeight(isLightTheme()); + } + if (dividerDrawableResId == 0) { + dividerDrawableResId = isLightTheme() ? R.drawable.rect_dialogx_material_menu_split_divider : R.drawable.rect_dialogx_material_menu_split_divider_night; + } + + if (!isLightTheme()) { + listView = new DialogListView(getDialogImpl(), getOwnActivity(), R.style.DialogXCompatThemeDark); + } else { + listView = new DialogListView(getDialogImpl(), getOwnActivity()); + } + listView.setOverScrollMode(OVER_SCROLL_NEVER); + listView.setDivider(getResources().getDrawable(dividerDrawableResId)); + listView.setDividerHeight(dividerHeight); + + listView.setBottomMenuListViewTouchEvent(new BottomMenuListViewTouchEvent() { + @Override + public void down(MotionEvent event) { + touchDownY = getDialogImpl().bkg.getY(); + } + }); + + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + if (!isMenuItemEnable(position)) { + return; + } + haptic(view); + long currentTime = System.currentTimeMillis(); + if (currentTime - lastClickTime > ITEM_CLICK_DELAY) { + lastClickTime = currentTime; + float deltaY = Math.abs(touchDownY - getDialogImpl().bkg.getY()); + if (deltaY > dip2px(15)) { + return; + } + selectionIndex = position; + switch (selectMode) { + case NONE: + if (onMenuItemClickListener != null) { + if (!onMenuItemClickListener.onClick(me, menuList.get(position), position)) { + dismiss(); + } + } else { + dismiss(); + } + break; + case SINGLE: + if (onMenuItemClickListener instanceof OnMenuItemSelectListener) { + OnMenuItemSelectListener onMenuItemSelectListener = (OnMenuItemSelectListener) onMenuItemClickListener; + if (!onMenuItemSelectListener.onClick(me, menuList.get(position), position)) { + dismiss(); + } else { + menuListAdapter.notifyDataSetInvalidated(); + onMenuItemSelectListener.onOneItemSelect(me, menuList.get(position), position, true); + } + } else { + if (onMenuItemClickListener != null) { + if (!onMenuItemClickListener.onClick(me, menuList.get(position), position)) { + dismiss(); + } + } else { + menuListAdapter.notifyDataSetInvalidated(); + } + } + break; + case MULTIPLE: + if (onMenuItemClickListener instanceof OnMenuItemSelectListener) { + OnMenuItemSelectListener onMenuItemSelectListener = (OnMenuItemSelectListener) onMenuItemClickListener; + if (!onMenuItemSelectListener.onClick(me, menuList.get(position), position)) { + dismiss(); + } else { + if (selectionItems.contains(position)) { + selectionItems.remove(new Integer(position)); + } else { + selectionItems.add(position); + } + menuListAdapter.notifyDataSetInvalidated(); + resultArray = new int[selectionItems.size()]; + selectTextArray = new CharSequence[selectionItems.size()]; + for (int i = 0; i < selectionItems.size(); i++) { + resultArray[i] = selectionItems.get(i); + selectTextArray[i] = menuList.get(resultArray[i]); + } + onMenuItemSelectListener.onMultiItemSelect(me, selectTextArray, resultArray); + } + } else { + if (onMenuItemClickListener != null) { + if (!onMenuItemClickListener.onClick(me, menuList.get(position), position)) { + dismiss(); + } + } else { + if (selectionItems.contains(position)) { + selectionItems.remove(new Integer(position)); + } else { + selectionItems.add(position); + } + menuListAdapter.notifyDataSetInvalidated(); + resultArray = new int[selectionItems.size()]; + selectTextArray = new CharSequence[selectionItems.size()]; + for (int i = 0; i < selectionItems.size(); i++) { + resultArray[i] = selectionItems.get(i); + selectTextArray[i] = menuList.get(resultArray[i]); + } + } + } + break; + } + } + } + }); + if (style.overrideBottomDialogRes() != null) { + if (style.overrideBottomDialogRes().overrideMenuItemLayout(true, 0, 0, false) != 0) { + listView.setSelector(R.color.empty); + } + } + + ViewGroup.LayoutParams listViewLp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + getDialogImpl().boxList.addView(listView, listViewLp); + refreshUI(); + } + } + + @Override + public void refreshUI() { + if (getDialogImpl() == null) return; + if (listView != null) { + if (menuListAdapter == null) { + menuListAdapter = new MessageMenuArrayAdapter(me, getOwnActivity(), menuList); + } + if (listView.getAdapter() == null) { + listView.setAdapter(menuListAdapter); + } else { + if (listView.getAdapter() != menuListAdapter) { + listView.setAdapter(menuListAdapter); + } else { + menuListAdapter.notifyDataSetChanged(); + } + } + } + + // 部分主题下选中项默认按下效果 + if (showSelectedBackgroundTips) { + if (listView != null) { + listView.post(new Runnable() { + @Override + public void run() { + if (menuListAdapter instanceof BottomMenuArrayAdapter && showSelectedBackgroundTips) { + BottomMenuArrayAdapter bottomMenuArrayAdapter = ((BottomMenuArrayAdapter) menuListAdapter); + + View selectItemView = listView.getChildAt(getSelection()); + if (selectItemView != null) { + selectItemView.post(new Runnable() { + @Override + public void run() { + selectItemView.setPressed(true); + } + }); + } + } + } + }); + } + + } + + if (itemDivider != null && listView != null) { + listView.setDivider(itemDivider.createDividerDrawable(getOwnActivity(), isLightTheme())); + listView.setDividerHeight(itemDivider.getWidth()); + } + super.refreshUI(); + } + + public void preRefreshUI() { + if (getDialogImpl() == null) return; + runOnMain(new Runnable() { + @Override + public void run() { + refreshUI(); + } + }); + } + + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + public List getMenuList() { + return menuList; + } + + public MessageMenu setMenuList(List menuList) { + this.menuList = menuList; + this.menuListAdapter = null; + preRefreshUI(); + return this; + } + + private boolean isSameSize(int menuListSize) { + if (this.menuList == null || this.menuList.size() == 0) { + return true; + } + return this.menuList.size() == menuListSize; + } + + public MessageMenu setMenuStringList(List menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(menuList); + this.menuListAdapter = null; + preRefreshUI(); + return this; + } + + public MessageMenu setMenuList(String[] menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(Arrays.asList(menuList)); + this.menuListAdapter = null; + preRefreshUI(); + return this; + } + + public MessageMenu setMenuList(CharSequence[] menuList) { + this.menuList = Arrays.asList(menuList); + this.menuListAdapter = null; + preRefreshUI(); + return this; + } + + public MessageMenu setMenus(CharSequence... menuList) { + this.menuList = Arrays.asList(menuList); + this.menuListAdapter = null; + preRefreshUI(); + return this; + } + + public MessageMenu setMenus(String... menuList) { + this.menuList = Arrays.asList(menuList); + this.menuListAdapter = null; + preRefreshUI(); + return this; + } + + public OnIconChangeCallBack getOnIconChangeCallBack() { + return onIconChangeCallBack; + } + + public MessageMenu setOnIconChangeCallBack(OnIconChangeCallBack onIconChangeCallBack) { + this.onIconChangeCallBack = onIconChangeCallBack; + return this; + } + + public OnBackPressedListener getOnBackPressedListener() { + return (OnBackPressedListener) onBackPressedListener; + } + + public MessageMenu setOnBackPressedListener(OnBackPressedListener onBackPressedListener) { + this.onBackPressedListener = onBackPressedListener; + preRefreshUI(); + return this; + } + + public MessageMenu setDialogLifecycleCallback(DialogLifecycleCallback dialogLifecycleCallback) { + this.dialogLifecycleCallback = dialogLifecycleCallback; + if (isShow) dialogLifecycleCallback.onShow(me); + return this; + } + + public MessageMenu setStyle(DialogXStyle style) { + this.style = style; + return this; + } + + public MessageMenu setTheme(DialogX.THEME theme) { + this.theme = theme; + return this; + } + + public boolean isCancelable() { + if (privateCancelable != null) { + return privateCancelable == BOOLEAN.TRUE; + } + if (overrideCancelable != null) { + return overrideCancelable == BOOLEAN.TRUE; + } + return cancelable; + } + + public MessageMenu setCancelable(boolean cancelable) { + this.privateCancelable = cancelable ? BOOLEAN.TRUE : BOOLEAN.FALSE; + preRefreshUI(); + return this; + } + + public MessageDialog.DialogImpl getDialogImpl() { + return dialogImpl; + } + + public CharSequence getTitle() { + return title; + } + + public MessageMenu setTitle(CharSequence title) { + this.title = title; + preRefreshUI(); + return this; + } + + public MessageMenu setTitle(int titleResId) { + this.title = getString(titleResId); + preRefreshUI(); + return this; + } + + public CharSequence getMessage() { + return message; + } + + public MessageMenu setMessage(CharSequence message) { + this.message = message; + preRefreshUI(); + return this; + } + + public MessageMenu setMessage(int messageResId) { + this.message = getString(messageResId); + preRefreshUI(); + return this; + } + + public CharSequence getCancelButton() { + return cancelText; + } + + public MessageMenu setCancelButton(CharSequence cancelText) { + this.cancelText = cancelText; + preRefreshUI(); + return this; + } + + public MessageMenu setCancelButton(int cancelTextResId) { + this.cancelText = getString(cancelTextResId); + preRefreshUI(); + return this; + } + + public MessageMenu setCancelButton(OnMenuButtonClickListener cancelButtonClickListener) { + this.cancelButtonClickListener = cancelButtonClickListener; + return this; + } + + public MessageMenu setCancelButton(CharSequence cancelText, OnMenuButtonClickListener cancelButtonClickListener) { + this.cancelText = cancelText; + this.cancelButtonClickListener = cancelButtonClickListener; + preRefreshUI(); + return this; + } + + public MessageMenu setCancelButton(int cancelTextResId, OnMenuButtonClickListener cancelButtonClickListener) { + this.cancelText = getString(cancelTextResId); + this.cancelButtonClickListener = cancelButtonClickListener; + preRefreshUI(); + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.MessageMenu#setCancelButton(OnMenuButtonClickListener)} + */ + @Deprecated + public MessageMenu setCancelButton(OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelButtonClickListener = cancelButtonClickListener; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.MessageMenu#setCancelButton(CharSequence cancelText, OnMenuButtonClickListener)} + */ + @Deprecated + public MessageMenu setCancelButton(CharSequence cancelText, OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelText = cancelText; + this.cancelButtonClickListener = cancelButtonClickListener; + preRefreshUI(); + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.MessageMenu#setCancelButton(int cancelTextResId, OnMenuButtonClickListener)} + */ + @Deprecated + public MessageMenu setCancelButton(int cancelTextResId, OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelText = getString(cancelTextResId); + this.cancelButtonClickListener = cancelButtonClickListener; + preRefreshUI(); + return this; + } + + public MessageMenu setCustomView(OnBindView onBindView) { + this.onBindView = onBindView; + preRefreshUI(); + return this; + } + + public View getCustomView() { + if (onBindView == null) return null; + return onBindView.getCustomView(); + } + + public MessageMenu removeCustomView() { + this.onBindView.clean(); + preRefreshUI(); + return this; + } + + public boolean isAllowInterceptTouch() { + if (style.overrideBottomDialogRes() == null) { + return false; + } else { + return allowInterceptTouch && style.overrideBottomDialogRes().touchSlide(); + } + } + + public MessageMenu setAllowInterceptTouch(boolean allowInterceptTouch) { + this.allowInterceptTouch = allowInterceptTouch; + preRefreshUI(); + return this; + } + + public float getMessageDialogMaxHeight() { + return maxHeight; + } + + public MessageMenu setMessageDialogMaxHeight(float dialogMaxHeight) { + this.maxHeight = (int) dialogMaxHeight; + return this; + } + + public OnMenuItemClickListener getOnMenuItemClickListener() { + return onMenuItemClickListener; + } + + public MessageMenu setOnMenuItemClickListener(OnMenuItemClickListener onMenuItemClickListener) { + this.onMenuItemClickListener = onMenuItemClickListener; + return this; + } + + public BaseAdapter getMenuListAdapter() { + return menuListAdapter; + } + + public MessageMenu setMenuListAdapter(BaseAdapter menuListAdapter) { + this.menuListAdapter = menuListAdapter; + return this; + } + + public OnMenuButtonClickListener getMessageMenuCancelButtonClickListener() { + return (OnMenuButtonClickListener) cancelButtonClickListener; + } + + public MessageMenu setCancelButtonClickListener(OnMenuButtonClickListener cancelButtonClickListener) { + this.cancelButtonClickListener = cancelButtonClickListener; + return this; + } + + public TextInfo getTitleTextInfo() { + return titleTextInfo; + } + + public MessageMenu setTitleTextInfo(TextInfo titleTextInfo) { + this.titleTextInfo = titleTextInfo; + preRefreshUI(); + return this; + } + + public TextInfo getMessageTextInfo() { + return messageTextInfo; + } + + public MessageMenu setMessageTextInfo(TextInfo messageTextInfo) { + this.messageTextInfo = messageTextInfo; + preRefreshUI(); + return this; + } + + public TextInfo getCancelTextInfo() { + return cancelTextInfo; + } + + public MessageMenu setCancelTextInfo(TextInfo cancelTextInfo) { + this.cancelTextInfo = cancelTextInfo; + preRefreshUI(); + return this; + } + + public int getBackgroundColor() { + return backgroundColor; + } + + public MessageMenu setBackgroundColor(@ColorInt int backgroundColor) { + this.backgroundColor = backgroundColor; + preRefreshUI(); + return this; + } + + public int getSelection() { + return selectionIndex; + } + + public ArrayList getSelectionList() { + return selectionItems; + } + + public MessageMenu setSelection(int selectionIndex) { + this.selectMode = SELECT_MODE.SINGLE; + this.selectionIndex = selectionIndex; + this.selectionItems = null; + menuListAdapter = null; + preRefreshUI(); + return this; + } + + public MessageMenu setSingleSelection() { + this.selectMode = SELECT_MODE.SINGLE; + this.selectionIndex = -1; + this.selectionItems = null; + menuListAdapter = null; + preRefreshUI(); + return this; + } + + public MessageMenu setSelection(int[] selectionItems) { + this.selectMode = SELECT_MODE.MULTIPLE; + this.selectionIndex = -1; + this.selectionItems = new ArrayList<>(); + if (selectionItems != null) { + for (int itemIndex : selectionItems) { + this.selectionItems.add(itemIndex); + } + } + menuListAdapter = null; + preRefreshUI(); + return this; + } + + public MessageMenu setMultiSelection() { + this.selectMode = SELECT_MODE.MULTIPLE; + this.selectionIndex = -1; + this.selectionItems = new ArrayList<>(); + menuListAdapter = null; + preRefreshUI(); + return this; + } + + public MessageMenu setSelection(List selectionItems) { + this.selectMode = SELECT_MODE.MULTIPLE; + this.selectionIndex = -1; + this.selectionItems = new ArrayList<>(selectionItems); + menuListAdapter = null; + preRefreshUI(); + return this; + } + + public MessageMenu setNoSelect() { + this.selectMode = SELECT_MODE.NONE; + this.selectionIndex = -1; + this.selectionItems = null; + menuListAdapter = null; + preRefreshUI(); + return this; + } + + public MessageMenu setBackgroundColorRes(@ColorRes int backgroundRes) { + this.backgroundColor = getColor(backgroundRes); + preRefreshUI(); + return this; + } + + public CharSequence getOkButton() { + return okText; + } + + public MessageMenu setOkButton(CharSequence okText) { + this.okText = okText; + preRefreshUI(); + return this; + } + + public MessageMenu setOkButton(int OkTextResId) { + this.okText = getString(OkTextResId); + preRefreshUI(); + return this; + } + + public MessageMenu setOkButton(OnMenuButtonClickListener okButtonClickListener) { + this.okButtonClickListener = okButtonClickListener; + return this; + } + + public MessageMenu setOkButton(CharSequence okText, OnMenuButtonClickListener okButtonClickListener) { + this.okText = okText; + this.okButtonClickListener = okButtonClickListener; + return this; + } + + public MessageMenu setOkButton(int okTextResId, OnMenuButtonClickListener okButtonClickListener) { + this.okText = getString(okTextResId); + this.okButtonClickListener = okButtonClickListener; + return this; + } + + public MessageMenu setHapticFeedbackEnabled(boolean isHapticFeedbackEnabled) { + this.isHapticFeedbackEnabled = isHapticFeedbackEnabled ? 1 : 0; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.MessageMenu#setOkButton(OnMenuButtonClickListener)} + */ + @Deprecated + public MessageMenu setOkButton(OnDialogButtonClickListener okButtonClickListener) { + this.okButtonClickListener = okButtonClickListener; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.MessageMenu#setOkButton(CharSequence okText, OnMenuButtonClickListener)} + */ + @Deprecated + public MessageMenu setOkButton(CharSequence okText, OnDialogButtonClickListener okButtonClickListener) { + this.okText = okText; + this.okButtonClickListener = okButtonClickListener; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.MessageMenu#setOkButton(int okTextResId, OnMenuButtonClickListener)} + */ + @Deprecated + public MessageMenu setOkButton(int okTextResId, OnDialogButtonClickListener okButtonClickListener) { + this.okText = getString(okTextResId); + this.okButtonClickListener = okButtonClickListener; + return this; + } + + public CharSequence getOtherButton() { + return otherText; + } + + public MessageMenu setOtherButton(CharSequence otherText) { + this.otherText = otherText; + preRefreshUI(); + return this; + } + + public MessageMenu setOtherButton(int OtherTextResId) { + this.otherText = getString(OtherTextResId); + preRefreshUI(); + return this; + } + + public MessageMenu setOtherButton(OnMenuButtonClickListener otherButtonClickListener) { + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + public MessageMenu setOtherButton(CharSequence otherText, OnMenuButtonClickListener otherButtonClickListener) { + this.otherText = otherText; + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + public MessageMenu setOtherButton(int otherTextResId, OnMenuButtonClickListener otherButtonClickListener) { + this.otherText = getString(otherTextResId); + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.MessageMenu#setOtherButton(OnMenuButtonClickListener)} + */ + @Deprecated + public MessageMenu setOtherButton(OnDialogButtonClickListener otherButtonClickListener) { + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.MessageMenu#setOtherButton(CharSequence otherText, OnMenuButtonClickListener)} + */ + @Deprecated + public MessageMenu setOtherButton(CharSequence otherText, OnDialogButtonClickListener otherButtonClickListener) { + this.otherText = otherText; + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.MessageMenu#setOtherButton(int otherTextResId, OnMenuButtonClickListener)} + */ + @Deprecated + public MessageMenu setOtherButton(int otherTextResId, OnDialogButtonClickListener otherButtonClickListener) { + this.otherText = getString(otherTextResId); + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + public MessageMenu setMaskColor(@ColorInt int maskColor) { + this.maskColor = maskColor; + preRefreshUI(); + return this; + } + + public long getEnterAnimDuration() { + return enterAnimDuration; + } + + public MessageMenu setEnterAnimDuration(long enterAnimDuration) { + this.enterAnimDuration = enterAnimDuration; + return this; + } + + public long getExitAnimDuration() { + return exitAnimDuration; + } + + public MessageMenu setExitAnimDuration(long exitAnimDuration) { + this.exitAnimDuration = exitAnimDuration; + return this; + } + + public SELECT_MODE getSelectMode() { + return selectMode; + } + + @Override + protected void shutdown() { + dismiss(); + } + + public MessageMenu setMaxWidth(int maxWidth) { + this.maxWidth = maxWidth; + refreshUI(); + return this; + } + + public MessageMenu setMaxHeight(int maxHeight) { + this.maxHeight = maxHeight; + refreshUI(); + return this; + } + + public MessageMenu setMinHeight(int minHeight) { + this.minHeight = minHeight; + refreshUI(); + return this; + } + + public MessageMenu setMinWidth(int minWidth) { + this.minWidth = minWidth; + refreshUI(); + return this; + } + + public MessageMenu setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public TextInfo getMenuTextInfo() { + if (menuTextInfo == null) return DialogX.menuTextInfo; + return menuTextInfo; + } + + public MessageMenu setMenuTextInfo(TextInfo menuTextInfo) { + this.menuTextInfo = menuTextInfo; + return this; + } + + public MenuItemTextInfoInterceptor getMenuItemTextInfoInterceptor() { + return menuItemTextInfoInterceptor; + } + + public MessageMenu setMenuItemTextInfoInterceptor(MenuItemTextInfoInterceptor menuItemTextInfoInterceptor) { + this.menuItemTextInfoInterceptor = menuItemTextInfoInterceptor; + return this; + } + + public boolean isBkgInterceptTouch() { + return bkgInterceptTouch; + } + + public MessageMenu setBkgInterceptTouch(boolean bkgInterceptTouch) { + this.bkgInterceptTouch = bkgInterceptTouch; + return this; + } + + public OnBackgroundMaskClickListener getOnBackgroundMaskClickListener() { + return onBackgroundMaskClickListener; + } + + public MessageMenu setOnBackgroundMaskClickListener(OnBackgroundMaskClickListener onBackgroundMaskClickListener) { + this.onBackgroundMaskClickListener = onBackgroundMaskClickListener; + return this; + } + + public MessageMenu setRadius(float radiusPx) { + backgroundRadius = radiusPx; + refreshUI(); + return this; + } + + public float getRadius() { + return backgroundRadius; + } + + public MessageMenu setTitleIcon(Bitmap titleIcon) { + this.titleIcon = new BitmapDrawable(getResources(), titleIcon); + refreshUI(); + return this; + } + + public MessageMenu setTitleIcon(int titleIconResId) { + this.titleIcon = getResources().getDrawable(titleIconResId); + refreshUI(); + return this; + } + + public MessageMenu setTitleIcon(Drawable titleIcon) { + this.titleIcon = titleIcon; + refreshUI(); + return this; + } + + public DialogXAnimInterface getDialogXAnimImpl() { + return dialogXAnimImpl; + } + + public MessageMenu setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public MessageMenu setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public MessageMenu setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + public boolean isShowSelectedBackgroundTips() { + return showSelectedBackgroundTips; + } + + public MessageMenu setShowSelectedBackgroundTips(boolean showSelectedBackgroundTips) { + this.showSelectedBackgroundTips = showSelectedBackgroundTips; + refreshUI(); + return this; + } + + // 返回点击的菜单索引 + public int getSelectionIndex() { + return selectionIndex; + } + + // 返回多选时,选择的菜单索引集合 + public int[] getSelectionIndexArray() { + return resultArray; + } + + // 返回多选时,选择的菜单文本集合 + public CharSequence[] getSelectTextArray() { + return selectTextArray; + } + + public MenuItemLayoutRefreshCallback getMenuMenuItemLayoutRefreshCallback() { + return menuMenuItemLayoutRefreshCallback; + } + + public MessageMenu setMenuMenuItemLayoutRefreshCallback(MenuItemLayoutRefreshCallback menuMenuItemLayoutRefreshCallback) { + this.menuMenuItemLayoutRefreshCallback = menuMenuItemLayoutRefreshCallback; + return this; + } + + public TextInfo getOkTextInfo() { + return okTextInfo; + } + + public MessageMenu setOkTextInfo(TextInfo okTextInfo) { + this.okTextInfo = okTextInfo; + return this; + } + + public TextInfo getOtherTextInfo() { + return otherTextInfo; + } + + public MessageMenu setOtherTextInfo(TextInfo otherTextInfo) { + this.otherTextInfo = otherTextInfo; + return this; + } + + public MessageMenu setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public MessageMenu onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public MessageMenu onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public MessageMenu setEnableImmersiveMode(boolean enableImmersiveMode) { + this.enableImmersiveMode = enableImmersiveMode; + refreshUI(); + return this; + } + + public List getIconResIds() { + return iconResIds; + } + + public int getIconResIds(int position) { + if (iconResIds != null && position >= 0 && position < iconResIds.size()) { + return iconResIds.get(position); + } + return 0; + } + + public MessageMenu setIconResIds(List iconResIds) { + this.iconResIds = iconResIds; + refreshUI(); + return this; + } + + public MessageMenu setIconResIds(int... resIds) { + if (iconResIds == null) { + iconResIds = new ArrayList<>(); + } + for (int id : resIds) { + iconResIds.add(id); + } + refreshUI(); + return this; + } + + public boolean isAutoTintIconInLightOrDarkMode() { + return autoTintIconInLightOrDarkMode; + } + + public MessageMenu setAutoTintIconInLightOrDarkMode(boolean autoTintIconInLightOrDarkMode) { + this.autoTintIconInLightOrDarkMode = autoTintIconInLightOrDarkMode; + refreshUI(); + return this; + } + + public MessageMenu appendMessage(CharSequence message) { + this.message = TextUtils.concat(this.message, message); + refreshUI(); + return this; + } + + public MessageMenu setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex(" + orderIndex + ") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public MessageMenu bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public MessageMenu enableMenu(int... menuIndex) { + for (int i : menuIndex) { + menuUsability.put(i, true); + } + return this; + } + + public MessageMenu enableMenu(CharSequence... menuText) { + if (menuList != null && !menuList.isEmpty()) { + for (CharSequence c : menuText) { + int index = menuList.indexOf(c); + menuUsability.put(index, true); + } + } else { + error("DialogX: " + dialogKey() + " .enableMenu(" + menuText + ")执行失败,请先初始化菜单项 menuList"); + } + return this; + } + + public MessageMenu enableMenu(String... menuText) { + if (menuList != null && !menuList.isEmpty()) { + for (String c : menuText) { + int index = menuList.indexOf(c); + menuUsability.put(index, true); + } + } else { + error("DialogX: " + dialogKey() + " .enableMenu(" + menuText + ")执行失败,请先初始化菜单项 menuList"); + } + return this; + } + + public MessageMenu disableMenu(int... menuIndex) { + for (int i : menuIndex) { + menuUsability.put(i, false); + } + return this; + } + + public MessageMenu disableMenu(CharSequence... menuText) { + if (menuList != null && !menuList.isEmpty()) { + for (CharSequence c : menuText) { + int index = menuList.indexOf(c); + menuUsability.put(index, false); + } + } else { + error("DialogX: " + dialogKey() + " .disableMenu(" + menuText + ")执行失败,请先初始化菜单项 menuList"); + } + return this; + } + + public MessageMenu disableMenu(String... menuText) { + if (menuList != null && !menuList.isEmpty()) { + for (String c : menuText) { + int index = menuList.indexOf(c); + menuUsability.put(index, false); + } + } else { + error("DialogX: " + dialogKey() + " .disableMenu(" + menuText + ")执行失败,请先初始化菜单项 menuList"); + } + return this; + } + + public boolean isMenuItemEnable(int index) { + Boolean enabled = menuUsability.get(index); + if (enabled == null) { + return true; + } + return enabled; + } + + public MessageMenu setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public MessageMenu cleanAction(int actionId) { + dialogActionRunnableMap.remove(actionId); + return this; + } + + public MessageMenu cleanAllAction() { + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + public void callDialogDismiss() { + dismiss(); + } + + public MessageMenu bindDismissWithLifecycleOwner(LifecycleOwner owner) { + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } + + public ItemDivider getItemDivider() { + return itemDivider; + } + + public MessageMenu setItemDivider(ItemDivider itemDivider) { + this.itemDivider = itemDivider; + refreshUI(); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/PopMenu.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/PopMenu.java new file mode 100644 index 0000000..c8fbeb3 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/PopMenu.java @@ -0,0 +1,1632 @@ +package com.kongzue.dialogx.dialogs; + +import static android.view.View.OVER_SCROLL_NEVER; +import static android.view.View.VISIBLE; + +import android.animation.ValueAnimator; +import android.app.Activity; +import android.graphics.Outline; +import android.graphics.drawable.GradientDrawable; +import android.os.Build; +import android.text.TextUtils; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.view.ViewTreeObserver; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.view.animation.DecelerateInterpolator; +import android.widget.AdapterView; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.annotation.Nullable; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.interfaces.BlurViewType; +import com.kongzue.dialogx.interfaces.DialogConvertViewInterface; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.MenuItemLayoutRefreshCallback; +import com.kongzue.dialogx.interfaces.OnBackPressedListener; +import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener; +import com.kongzue.dialogx.interfaces.OnBindView; +import com.kongzue.dialogx.interfaces.OnIconChangeCallBack; +import com.kongzue.dialogx.interfaces.OnMenuItemClickListener; +import com.kongzue.dialogx.util.DialogXViewLoc; +import com.kongzue.dialogx.util.ItemDivider; +import com.kongzue.dialogx.util.PopMenuArrayAdapter; +import com.kongzue.dialogx.util.TextInfo; +import com.kongzue.dialogx.util.views.DialogXBaseRelativeLayout; +import com.kongzue.dialogx.util.views.MaxRelativeLayout; +import com.kongzue.dialogx.util.views.PopMenuListView; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2021/8/18 14:27 + */ +public class PopMenu extends BaseDialog { + + public static long overrideEnterDuration = -1; + public static long overrideExitDuration = -1; + + protected PopMenu me = this; + protected boolean bkgInterceptTouch = true; + protected OnBindView onBindView; // 自定义布局 + protected DialogLifecycleCallback dialogLifecycleCallback; // 对话框生命周期 + protected OnBackgroundMaskClickListener onBackgroundMaskClickListener; + protected List menuList; + protected List iconResIds; + protected boolean autoTintIconInLightOrDarkMode = true; + protected DialogImpl dialogImpl; + protected WeakReference baseViewWeakReference; + protected boolean overlayBaseView = true; // 允许菜单覆盖在 baseView 上 + protected OnMenuItemClickListener onMenuItemClickListener; + protected OnIconChangeCallBack onIconChangeCallBack; // 设置图标 + protected int width = -1; // 指定菜单宽度 + protected int height = -1; // 指定菜单高度 + protected TextInfo menuTextInfo; + protected boolean offScreen = false; // 超出屏幕 + protected float backgroundRadius = DialogX.defaultPopMenuBackgroundRadius; + protected DialogXAnimInterface dialogXAnimImpl; + protected OnBackPressedListener onBackPressedListener; + protected MenuItemLayoutRefreshCallback menuMenuItemLayoutRefreshCallback; + protected int pressedIndex = -1; + protected Map menuUsability = new HashMap(); + protected ItemDivider itemDivider; + + protected int alignGravity = -1; // 指定菜单相对 baseView 的位置 + + // 记录 baseView 位置 + protected DialogXViewLoc baseViewLoc = new DialogXViewLoc(); + private int selectIndex; + public boolean notCheckHash = false; + public int lastHash = -1; + + public PopMenu() { + super(); + } + + public PopMenu(View baseView, List menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(menuList); + this.baseView(baseView); + } + + public PopMenu(View baseView, CharSequence[] menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(Arrays.asList(menuList)); + this.baseView(baseView); + } + + public PopMenu(List menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(menuList); + } + + public PopMenu(CharSequence... menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(Arrays.asList(menuList)); + } + + public PopMenu(String... menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(Arrays.asList(menuList)); + } + + public PopMenu(OnBindView onBindView) { + this.onBindView = onBindView; + } + + public PopMenu(View baseView, OnBindView onBindView) { + this.baseView(baseView); + this.onBindView = onBindView; + } + + public PopMenu(View baseView, List menuList, OnBindView onBindView) { + this.baseView(baseView); + this.menuList = new ArrayList<>(); + this.menuList.addAll(menuList); + this.onBindView = onBindView; + } + + public PopMenu(View baseView, CharSequence[] menuList, OnBindView onBindView) { + this.baseView(baseView); + this.menuList = new ArrayList<>(); + this.menuList.addAll(Arrays.asList(menuList)); + this.onBindView = onBindView; + } + + public PopMenu(List menuList, OnBindView onBindView) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(menuList); + this.onBindView = onBindView; + } + + public PopMenu(CharSequence[] menuList, OnBindView onBindView) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(Arrays.asList(menuList)); + this.onBindView = onBindView; + } + + public static PopMenu build() { + return new PopMenu(); + } + + public static PopMenu build(DialogXStyle style) { + return new PopMenu().setStyle(style); + } + + public static PopMenu show(CharSequence... menus) { + PopMenu popMenu = new PopMenu(menus); + popMenu.show(); + return popMenu; + } + + public static PopMenu show(String... menus) { + PopMenu popMenu = new PopMenu(menus); + popMenu.show(); + return popMenu; + } + + public static PopMenu show(List menuList) { + PopMenu popMenu = new PopMenu(menuList); + popMenu.show(); + return popMenu; + } + + public static PopMenu show(View baseView, CharSequence[] menus) { + PopMenu popMenu = new PopMenu(baseView, menus); + popMenu.show(); + return popMenu; + } + + public static PopMenu show(View baseView, List menuList) { + PopMenu popMenu = new PopMenu(baseView, menuList); + popMenu.show(); + return popMenu; + } + + public static PopMenu show(View baseView, CharSequence[] menus, OnBindView onBindView) { + PopMenu popMenu = new PopMenu(baseView, menus, onBindView); + popMenu.show(); + return popMenu; + } + + public static PopMenu show(View baseView, List menuList, OnBindView onBindView) { + PopMenu popMenu = new PopMenu(baseView, menuList, onBindView); + popMenu.show(); + return popMenu; + } + + public static PopMenu show(CharSequence[] menus, OnBindView onBindView) { + PopMenu popMenu = new PopMenu(menus, onBindView); + popMenu.show(); + return popMenu; + } + + public static PopMenu show(List menuList, OnBindView onBindView) { + PopMenu popMenu = new PopMenu(menuList, onBindView); + popMenu.show(); + return popMenu; + } + + private ViewTreeObserver viewTreeObserver; + private ViewTreeObserver.OnDrawListener baseViewDrawListener; + + public PopMenu show() { + if (isHide && getDialogView() != null && isShow) { + if (hideWithExitAnim && getDialogImpl() != null) { + getDialogImpl().boxBody.clearAnimation(); + getDialogView().setVisibility(View.VISIBLE); + getDialogImpl().boxRoot.animate().alpha(1f); + getDialogImpl().getDialogXAnimImpl().doShowAnim(me, getDialogImpl().boxBody); + } else { + getDialogView().setVisibility(View.VISIBLE); + } + return this; + } + + super.beforeShow(); + if (getDialogView() == null) { + int layoutId = isLightTheme() ? R.layout.layout_dialogx_popmenu_material : R.layout.layout_dialogx_popmenu_material_dark; + if (getStyle().popMenuSettings() != null) { + if (getStyle().popMenuSettings().layout(isLightTheme()) != 0) { + layoutId = getStyle().popMenuSettings().layout(isLightTheme()); + } + } + + View dialogView = createView(layoutId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) { + dialogView.setTag(me); + } + show(dialogView); + } else { + show(getDialogView()); + } + if (baseView() != null) { + viewTreeObserver = baseView().getViewTreeObserver(); + viewTreeObserver.addOnDrawListener(baseViewDrawListener = new ViewTreeObserver.OnDrawListener() { + @Override + public void onDraw() { + int[] baseViewLocCache = new int[2]; + if (baseView() != null) { + baseView().getLocationInWindow(baseViewLocCache); + if (getDialogImpl() != null && !baseViewLoc.isSameLoc(baseViewLocCache) && baseView().getVisibility() == VISIBLE) { + baseViewLoc.set(baseViewLocCache); + refreshMenuLoc(); + } + } else { + if (viewTreeObserver != null) { + removeDrawListener(viewTreeObserver, this); + viewTreeObserver = null; + baseViewDrawListener = null; + } + } + } + }); + } + return this; + } + + public PopMenu show(Activity activity) { + if (isHide && getDialogView() != null && isShow) { + if (hideWithExitAnim && getDialogImpl() != null) { + getDialogImpl().boxBody.clearAnimation(); + getDialogView().setVisibility(View.VISIBLE); + getDialogImpl().boxRoot.animate().alpha(1f); + getDialogImpl().getDialogXAnimImpl().doShowAnim(me, getDialogImpl().boxBody); + } else { + getDialogView().setVisibility(View.VISIBLE); + } + return this; + } + + super.beforeShow(); + if (getDialogView() == null) { + int layoutId = isLightTheme() ? R.layout.layout_dialogx_popmenu_material : R.layout.layout_dialogx_popmenu_material_dark; + if (getStyle().popMenuSettings() != null) { + if (getStyle().popMenuSettings().layout(isLightTheme()) != 0) { + layoutId = getStyle().popMenuSettings().layout(isLightTheme()); + } + } + + View dialogView = createView(layoutId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) { + dialogView.setTag(me); + } + show(activity, dialogView); + } else { + show(activity, getDialogView()); + } + if (baseView() != null) { + viewTreeObserver = baseView().getViewTreeObserver(); + viewTreeObserver.addOnDrawListener(baseViewDrawListener = new ViewTreeObserver.OnDrawListener() { + @Override + public void onDraw() { + int[] baseViewLocCache = new int[2]; + if (baseView() != null) { + baseView().getLocationInWindow(baseViewLocCache); + if (getDialogImpl() != null && !baseViewLoc.isSameLoc(baseViewLocCache) && baseView().getVisibility() == VISIBLE) { + baseViewLoc.set(baseViewLocCache); + refreshMenuLoc(); + } + } else { + if (viewTreeObserver != null) { + removeDrawListener(viewTreeObserver, this); + viewTreeObserver = null; + baseViewDrawListener = null; + } + } + } + }); + } + return this; + } + + private void refreshMenuLoc() { + if (getDialogImpl() == null || getDialogImpl().boxRoot == null || baseView() == null) { + return; + } + getDialogImpl().boxBody.setTag(null); + DialogXViewLoc loc = getMenuLoc(); + getDialogImpl().boxBody.setTag(loc); + if (!isEnterAnimRunning) { + if (loc.getX() != getDialogImpl().boxBody.getX()) { + getDialogImpl().boxBody.setX(loc.getX()); + } + if (loc.getY() != getDialogImpl().boxBody.getY()) { + getDialogImpl().boxBody.setY(loc.getY()); + } + } + if (getDialogImpl().boxBody.getWidth() != loc.getW()) { + RelativeLayout.LayoutParams rLp = new RelativeLayout.LayoutParams((int) loc.getW(), ViewGroup.LayoutParams.WRAP_CONTENT); + getDialogImpl().boxBody.setLayoutParams(rLp); + } + } + + protected DialogXViewLoc getMenuLoc() { + if (getDialogImpl().boxBody.getTag() instanceof DialogXViewLoc) { + return (DialogXViewLoc) getDialogImpl().boxBody.getTag(); + } + DialogXViewLoc result = new DialogXViewLoc(); + + MaxRelativeLayout boxBody = getDialogImpl().boxBody; + DialogXBaseRelativeLayout boxRoot = getDialogImpl().boxRoot; + // 菜单位置计算逻辑 + int baseViewLeft = (int) baseViewLoc.getX(); + int baseViewTop = (int) baseViewLoc.getY(); + int calX = 0, calY = 0; + if (alignGravity != -1) { + if (isAlignGravity(Gravity.CENTER_VERTICAL)) { + calY = (Math.max(0, baseViewTop + baseView().getMeasuredHeight() / 2 - boxBody.getHeight() / 2)); + } + if (isAlignGravity(Gravity.CENTER_HORIZONTAL)) { + calX = (Math.max(0, baseViewLeft + ( + getWidth() > 0 ? baseView().getMeasuredWidth() / 2 - getWidth() / 2 : 0 + ))); + } + if (isAlignGravity(Gravity.CENTER)) { + calX = (Math.max(0, baseViewLeft + ( + getWidth() > 0 ? (baseView().getMeasuredWidth() / 2 - getWidth() / 2) : 0 + ))); + calY = (Math.max(0, baseViewTop + baseView().getMeasuredHeight() / 2 - boxBody.getHeight() / 2)); + } + if (overlayBaseView) { + // 菜单覆盖在 baseView 上时 + if (isAlignGravity(Gravity.TOP)) { + calY = (baseViewTop + baseView().getMeasuredHeight() - boxBody.getHeight()); + if (calX == 0) { + calX = (Math.max(0, baseViewLeft + ( + getWidth() > 0 ? baseView().getMeasuredWidth() / 2 - getWidth() / 2 : 0 + ))); + } + } + if (isAlignGravity(Gravity.LEFT)) { + calX = Math.max(0, (baseViewLeft + baseView().getMeasuredWidth() - boxBody.getWidth())); + if (calY == 0) { + calY = (Math.max(0, baseViewTop + baseView().getMeasuredHeight() / 2 - boxBody.getHeight() / 2)); + } + } + if (isAlignGravity(Gravity.RIGHT)) { + calX = baseViewLeft; + if (calY == 0) { + calY = (Math.max(0, baseViewTop + baseView().getMeasuredHeight() / 2 - boxBody.getHeight() / 2)); + } + } + if (isAlignGravity(Gravity.BOTTOM)) { + calY = baseViewTop; + if (calX == 0) { + calX = (Math.max(0, baseViewLeft + ( + getWidth() > 0 ? baseView().getMeasuredWidth() / 2 - getWidth() / 2 : 0 + ))); + } + } + } else { + if (isAlignGravity(Gravity.TOP)) { + calY = (Math.max(0, baseViewTop - boxBody.getHeight())); + if (calX == 0) { + calX = (Math.max(0, baseViewLeft + ( + getWidth() > 0 ? baseView().getMeasuredWidth() / 2 - getWidth() / 2 : 0 + ))); + } + } + if (isAlignGravity(Gravity.LEFT)) { + calX = Math.max(0, (baseViewLeft - boxBody.getWidth())); + if (calY == 0) { + calY = (Math.max(0, baseViewTop + baseView().getMeasuredHeight() / 2 - boxBody.getHeight() / 2)); + } + } + if (isAlignGravity(Gravity.RIGHT)) { + calX = (Math.max(0, baseViewLeft + baseView().getWidth())); + if (calY == 0) { + calY = (Math.max(0, baseViewTop + baseView().getMeasuredHeight() / 2 - boxBody.getHeight() / 2)); + } + } + if (isAlignGravity(Gravity.BOTTOM)) { + calY = (Math.max(0, baseViewTop + baseView().getHeight())); + if (calX == 0) { + calX = (Math.max(0, baseViewLeft + ( + getWidth() > 0 ? baseView().getMeasuredWidth() / 2 - getWidth() / 2 : 0 + ))); + } + } + } + if (!offScreen) { + if (calX < 0) { + calX = 0; + } + if ((calX + boxBody.getWidth()) > boxRoot.getUseAreaWidth()) { + calX = boxRoot.getUseAreaWidth() - boxBody.getWidth(); + } + if (calY < 0) { + calY = 0; + } + if ((calY + boxBody.getHeight()) > boxRoot.getUseAreaHeight()) { + calY = boxRoot.getUseAreaHeight() - boxBody.getHeight(); + } + } + result.setX(calX).setY(calY); + } else { + int mHeight = PopMenu.this.height == -1 ? baseView().getHeight() : PopMenu.this.height; + int left = (int) baseViewLoc.getX(); + int top = (int) (baseViewLoc.getY() + (overlayBaseView ? 0 : mHeight) + selectItemYDeviation); + + if (!offScreen) { + if (left < 0) { + left = 0; + } + if ((left + boxBody.getWidth()) > boxRoot.getUseAreaWidth()) { + left = boxRoot.getUseAreaWidth() - boxBody.getWidth(); + } + if (top < 0) { + top = 0; + } + if ((top + boxBody.getHeight()) > boxRoot.getUseAreaHeight()) { + top = boxRoot.getUseAreaHeight() - boxBody.getHeight(); + } + } + result.setX(left).setY(top); + } + + int mWidth = PopMenu.this.width == -1 ? baseView().getWidth() : PopMenu.this.width; + int mHeight = PopMenu.this.height == -1 ? baseView().getHeight() : PopMenu.this.height; + result.setW(mWidth).setH(mHeight); + return result; + } + + protected PopMenuArrayAdapter menuListAdapter; + protected int selectItemYDeviation; // 如果找到了选中菜单,这里记录的是其位置的 Y 偏差值 + protected boolean isEnterAnimRunning; + + public class DialogImpl implements DialogConvertViewInterface { + + private List blurViews; + + public DialogXBaseRelativeLayout boxRoot; + public MaxRelativeLayout boxBody; + public RelativeLayout boxCustom; + public PopMenuListView listMenu; + + public DialogImpl(View convertView) { + if (convertView == null) return; + setDialogView(convertView); + boxRoot = convertView.findViewById(R.id.box_root); + boxBody = convertView.findViewById(R.id.box_body); + boxCustom = convertView.findViewById(R.id.box_custom); + listMenu = convertView.findViewById(R.id.listMenu); + boxBody.setVisibility(View.INVISIBLE); + + blurViews = findAllBlurView(convertView); + + // 先设置为 -1 表示未初始化位置 + boxBody.setX(-1); + boxBody.setY(-1); + init(); + } + + @Override + public void init() { + closing = false; + if (menuListAdapter == null) { + menuListAdapter = new PopMenuArrayAdapter(me, getOwnActivity(), menuList); + } + + boxRoot.setParentDialog(me); + boxRoot.setOnLifecycleCallBack(new DialogXBaseRelativeLayout.OnLifecycleCallBack() { + @Override + public void onShow() { + isShow = true; + preShow = false; + setLifecycleState(Lifecycle.State.CREATED); + onDialogShow(); + getDialogLifecycleCallback().onShow(me); + PopMenu.this.onShow(me); + refreshUI(); + } + + @Override + public void onDismiss() { + isShow = false; + getDialogLifecycleCallback().onDismiss(me); + PopMenu.this.onDismiss(me); + setLifecycleState(Lifecycle.State.DESTROYED); + menuListAdapter = null; + dialogImpl = null; + baseView(null); + dialogLifecycleCallback = null; + System.gc(); + } + }); + + boxRoot.setOnBackPressedListener(new DialogXBaseRelativeLayout.PrivateBackPressedListener() { + @Override + public boolean onBackPressed() { + if (onBackPressedListener != null) { + if (onBackPressedListener.onBackPressed(me)) { + dismiss(); + } + } else { + if (isCancelable()) { + dismiss(); + } + } + return true; + } + }); + listMenu.setMaxHeight(getRootFrameLayout() == null ? dip2px(500) : getRootFrameLayout().getMeasuredHeight() - dip2px(150)); + + boxBody.setVisibility(View.INVISIBLE); + boxBody.post(new Runnable() { + + @Override + public void run() { + getDialogXAnimImpl().doShowAnim(me, boxBody); + setLifecycleState(Lifecycle.State.RESUMED); + + Integer blurFrontColor = null; + Float dialogXRadius = null; + if (style.popMenuSettings() != null && style.popMenuSettings().blurBackgroundSettings() != null) { + blurFrontColor = getColorNullable(getIntStyleAttr(style.popMenuSettings().blurBackgroundSettings().blurForwardColorRes(isLightTheme()))); + dialogXRadius = getFloatStyleAttr((float) style.popMenuSettings().blurBackgroundSettings().blurBackgroundRoundRadiusPx()); + } + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setOverlayColor(backgroundColor == null ? blurFrontColor : backgroundColor); + ((BlurViewType) blurView).setRadiusPx(dialogXRadius); + } + } + } + }); + + int dividerDrawableResId = 0; + int dividerHeight = 0; + if (style.popMenuSettings() != null) { + dividerDrawableResId = style.popMenuSettings().overrideMenuDividerDrawableRes(isLightTheme()); + dividerHeight = style.popMenuSettings().overrideMenuDividerHeight(isLightTheme()); + } + if (dividerDrawableResId == 0) { + dividerDrawableResId = isLightTheme() ? R.drawable.rect_dialogx_material_menu_split_divider : R.drawable.rect_dialogx_material_menu_split_divider_night; + } + + listMenu.setOverScrollMode(OVER_SCROLL_NEVER); + listMenu.setVerticalScrollBarEnabled(false); + listMenu.setDivider(getResources().getDrawable(dividerDrawableResId)); + listMenu.setDividerHeight(dividerHeight); + + listMenu.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + if (!isMenuItemEnable(position)) { + return; + } + haptic(view); + selectIndex = position; + if (!closing) { + lastHash = menuList.hashCode(); + boolean callBack = getOnMenuItemClickListener().onClick(me, menuList.get(position), position); + if (!notCheckHash) { + if (lastHash == menuList.hashCode()) { + if (callBack) { + callBack = false; + } + } + } + if (!callBack) { + dismiss(); + } + } + } + }); + onDialogInit(); + } + + @Override + public void refreshView() { + if (boxRoot == null || getOwnActivity() == null) { + return; + } + boxRoot.setAutoUnsafePlacePadding(isEnableImmersiveMode()); + boxRoot.setRootPadding(screenPaddings[0], screenPaddings[1], screenPaddings[2], screenPaddings[3]); + if (listMenu.getAdapter() == null) { + listMenu.setAdapter(menuListAdapter); + } else { + if (menuListAdapter.getMenuList() != menuList) { + menuListAdapter = new PopMenuArrayAdapter(me, getOwnActivity(), menuList); + listMenu.setAdapter(menuListAdapter); + } else { + menuListAdapter.notifyDataSetChanged(); + } + } + + if (bkgInterceptTouch) { + if (isCancelable()) { + boxRoot.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onBackgroundMaskClickListener == null || !onBackgroundMaskClickListener.onClick(me, v)) { + doDismiss(v); + } + } + }); + } else { + boxRoot.setOnClickListener(null); + } + } else { + boxRoot.setClickable(false); + } + if (backgroundColor != null) { + tintColor(boxBody, backgroundColor); + } + + if (backgroundRadius > -1) { + if (boxBody.getBackground() instanceof GradientDrawable) { + GradientDrawable gradientDrawable = (GradientDrawable) boxBody.getBackground(); + if (gradientDrawable != null) + gradientDrawable.setCornerRadius(backgroundRadius); + } + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + boxBody.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), backgroundRadius); + } + }); + boxBody.setClipToOutline(true); + } + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setRadiusPx(backgroundRadius); + } + } + } + + if (onBindView != null && onBindView.getCustomView() != null) { + onBindView.bindParent(boxCustom, me); + boxCustom.setVisibility(View.VISIBLE); + } else { + boxCustom.setVisibility(View.GONE); + } + + if (width != -1) { + boxBody.setMaxWidth(width); + boxBody.setMinimumWidth(width); + } + + if (height != -1) { + boxBody.setMaxHeight(height); + boxBody.setMinimumHeight(height); + } + + if (backgroundColor != null) { + tintColor(boxBody, backgroundColor); + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setOverlayColor(backgroundColor); + } + } + } + + if (itemDivider != null) { + listMenu.setDivider(itemDivider.createDividerDrawable(getOwnActivity(), isLightTheme())); + listMenu.setDividerHeight(itemDivider.getWidth()); + } + + onDialogRefreshUI(); + } + + @Override + public void doDismiss(View v) { + if (preDismiss(PopMenu.this)) { + return; + } + if (v != null) { + v.setEnabled(false); + } + + if (!dismissAnimFlag && boxRoot != null) { + dismissAnimFlag = true; + boxRoot.post(new Runnable() { + @Override + public void run() { + getDialogXAnimImpl().doExitAnim(me, boxBody); + + runOnMainDelay(new Runnable() { + @Override + public void run() { + if (baseViewDrawListener != null) { + if (viewTreeObserver != null) { + removeDrawListener(viewTreeObserver, baseViewDrawListener); + } else { + if (baseView() != null) { + removeDrawListener(baseView().getViewTreeObserver(), baseViewDrawListener); + } + } + baseViewDrawListener = null; + viewTreeObserver = null; + } + dismiss(getDialogView()); + } + }, getExitAnimationDuration(null)); + } + }); + } + } + + protected DialogXAnimInterface getDialogXAnimImpl() { + if (dialogXAnimImpl == null) { + dialogXAnimImpl = new DialogXAnimInterface() { + + int selectMenuIndex = -1; + + @Override + public void doShowAnim(PopMenu dialog, ViewGroup dialogBodyView) { + long enterAnimDurationTemp = getEnterAnimationDuration(null); + + if (baseView() != null) { + // 有绑定按钮的情况下 + int targetHeight = getBodyRealHeight(); + boxBody.getLayoutParams().height = 1; + + if (overlayBaseView && !listMenu.isCanScroll()) { + if (baseView() instanceof TextView) { + String baseText = ((TextView) baseView()).getText().toString(); + for (CharSequence c : menuList) { + if (TextUtils.equals(c.toString(), baseText)) { + selectMenuIndex = menuList.indexOf(c); + break; + } + } + } + // 找到已选中的项目 + if (selectMenuIndex != -1) { + int[] viewLoc = new int[2]; + if (listMenu.getChildAt(selectMenuIndex) != null) { + int itemHeight = listMenu.getChildAt(selectMenuIndex).getMeasuredHeight(); + listMenu.getChildAt(selectMenuIndex).getLocationInWindow(viewLoc); + selectItemYDeviation = (int) ((baseView().getMeasuredHeight() / 2f) - (viewLoc[1] - boxBody.getY()) - (itemHeight / 2f)); + } + } + } + + refreshMenuLoc(); + selectItemYDeviation = (int) (getMenuLoc().getY() - baseViewLoc.getY()); + + // 展开动画 + ValueAnimator enterAnim = ValueAnimator.ofFloat(0f, 1f); + enterAnim.setInterpolator(new DecelerateInterpolator()); + enterAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + if (!isShow || getDialogImpl() == null || getDialogImpl().boxBody == null) + return; + float animatedValue = (float) animation.getAnimatedValue(); + isEnterAnimRunning = !(animatedValue == 1f); + DialogXViewLoc loc = getMenuLoc(); + + int aimHeight = animatedValue == 1f ? ViewGroup.LayoutParams.WRAP_CONTENT : (int) (targetHeight * animatedValue); + boxBody.getLayoutParams().height = aimHeight; + boxBody.getLayoutParams().width = getWidth() == -1 ? baseView().getWidth() : getWidth(); + if ((boxBody.getY() + aimHeight) > boxRoot.getSafeHeight()) { + boxBody.setY(boxRoot.getSafeHeight() - aimHeight); + } + float calX = loc.getX() != -1 ? loc.getX() : baseViewLoc.getX(); + float calY = baseViewLoc.getY() + selectItemYDeviation * animatedValue; + + if (!offScreen) { + if (calX < 0) { + calX = 0; + } + if (calY < 0) { + calY = 0; + } + if ((calX + boxBody.getWidth()) > boxRoot.getUseAreaWidth()) { + calX = boxRoot.getUseAreaWidth() - boxBody.getWidth(); + } + if ((calY + boxBody.getHeight()) > boxRoot.getUseAreaHeight()) { + calY = boxRoot.getUseAreaHeight() - boxBody.getHeight(); + } + } + boxBody.setX(calX); + boxBody.setY(calY); + + boxBody.requestLayout(); + if (boxBody.getVisibility() != VISIBLE) { + boxBody.setVisibility(View.VISIBLE); + } + + if (isUseBlurBackground()) { + boxRoot.setBkgAlpha((Float) animation.getAnimatedValue()); + } + } + }); + enterAnim.setInterpolator(new DecelerateInterpolator(2f)); + enterAnim.setDuration(enterAnimDurationTemp); + enterAnim.start(); + } else { + // 无绑定按钮的情况下 + RelativeLayout.LayoutParams rLp = (RelativeLayout.LayoutParams) boxBody.getLayoutParams(); + rLp.addRule(RelativeLayout.CENTER_IN_PARENT); + rLp.width = getWidth() == -1 ? RelativeLayout.LayoutParams.MATCH_PARENT : getWidth(); + rLp.leftMargin = dip2px(50); + rLp.rightMargin = dip2px(50); + boxBody.setLayoutParams(rLp); + boxBody.setAlpha(0f); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !isUseBlurBackground()) { + boxBody.setElevation(dip2px(20)); + } + boxBody.setVisibility(View.VISIBLE); + boxBody.animate().alpha(1f).setDuration(enterAnimDurationTemp); + + ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f); + bkgAlpha.setDuration(enterAnimDurationTemp); + bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + boxRoot.setBkgAlpha((Float) animation.getAnimatedValue()); + } + }); + bkgAlpha.start(); + } + } + + @Override + public void doExitAnim(PopMenu dialog, ViewGroup dialogBodyView) { + Animation exitAnim = AnimationUtils.loadAnimation(getOwnActivity() == null ? boxRoot.getContext() : getOwnActivity(), R.anim.anim_dialogx_default_exit); + long exitAnimDuration = getExitAnimationDuration(exitAnim); + exitAnim.setDuration(exitAnimDuration); + boxBody.startAnimation(exitAnim); + + boxRoot.animate() + .alpha(0f) + .setInterpolator(new AccelerateInterpolator()) + .setDuration(exitAnimDuration); + + ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1, 0f); + bkgAlpha.setDuration(exitAnimDuration); + bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + if (boxRoot != null && baseView() == null) { + boxRoot.setBkgAlpha((Float) animation.getAnimatedValue()); + } + } + }); + bkgAlpha.start(); + } + }; + } + return dialogXAnimImpl; + } + + private boolean isUseBlurBackground() { + return style.popMenuSettings() != null && style.popMenuSettings().blurBackgroundSettings() != null && style.popMenuSettings().blurBackgroundSettings().blurBackground(); + } + + public long getExitAnimationDuration(@Nullable Animation defaultExitAnim) { + if (defaultExitAnim == null && boxBody.getAnimation() != null) { + defaultExitAnim = boxBody.getAnimation(); + } + long exitAnimDurationTemp = (defaultExitAnim == null || defaultExitAnim.getDuration() == 0) ? 150 : defaultExitAnim.getDuration(); + if (overrideExitDuration >= 0) { + exitAnimDurationTemp = overrideExitDuration; + } + if (exitAnimDuration != -1) { + exitAnimDurationTemp = exitAnimDuration; + } + return exitAnimDurationTemp; + } + + public long getEnterAnimationDuration(@Nullable Animation defaultEnterAnim) { + if (defaultEnterAnim == null && boxBody.getAnimation() != null) { + defaultEnterAnim = boxBody.getAnimation(); + } + long enterAnimDurationTemp = (defaultEnterAnim == null || defaultEnterAnim.getDuration() == 0) ? 150 : defaultEnterAnim.getDuration(); + if (overrideEnterDuration >= 0) { + enterAnimDurationTemp = overrideEnterDuration; + } + if (enterAnimDuration >= 0) { + enterAnimDurationTemp = enterAnimDuration; + } + return enterAnimDurationTemp; + } + } + + private void removeDrawListener(ViewTreeObserver viewTreeObserver, ViewTreeObserver.OnDrawListener listener) { + if (viewTreeObserver == null || listener == null || !viewTreeObserver.isAlive()) { + return; + } + try { + viewTreeObserver.removeOnDrawListener(listener); + } catch (Exception e) { + } + } + + private int getBodyRealHeight() { + if (getDialogImpl() == null) { + return 0; + } + + int matchParentMeasureSpec = View.MeasureSpec.makeMeasureSpec(((View) getDialogImpl().boxBody.getParent()).getWidth(), View.MeasureSpec.EXACTLY); + int wrapContentMeasureSpec = View.MeasureSpec.makeMeasureSpec(((View) getDialogImpl().boxBody.getParent()).getHeight(), View.MeasureSpec.AT_MOST); + getDialogImpl().boxBody.measure(matchParentMeasureSpec, wrapContentMeasureSpec); + + return getDialogImpl().boxBody.getMeasuredHeight(); + } + + private boolean closing; + + public void dismiss() { + closing = true; + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl == null) { + return; + } + dialogImpl.doDismiss(null); + } + }); + } + + @Override + public void restartDialog() { + if (getDialogView() != null) { + if (baseViewDrawListener != null) { + if (viewTreeObserver != null) { + removeDrawListener(viewTreeObserver, baseViewDrawListener); + } else { + if (baseView() != null) { + removeDrawListener(baseView().getViewTreeObserver(), baseViewDrawListener); + } + } + baseViewDrawListener = null; + } + dismiss(getDialogView()); + isShow = false; + } + if (getDialogImpl().boxCustom != null) { + getDialogImpl().boxCustom.removeAllViews(); + } + show(); + } + + @Override + protected void shutdown() { + + } + + public List getMenuList() { + return menuList; + } + + public PopMenu setMenuList(List menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(menuList); + refreshUI(); + return this; + } + + public PopMenu setMenuList(String[] menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(Arrays.asList(menuList)); + refreshUI(); + return this; + } + + public PopMenu setMenuList(CharSequence[] menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(Arrays.asList(menuList)); + refreshUI(); + return this; + } + + public PopMenu setMenus(String... menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(Arrays.asList(menuList)); + refreshUI(); + return this; + } + + public PopMenu setMenus(CharSequence... menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(Arrays.asList(menuList)); + refreshUI(); + return this; + } + + public void refreshUI() { + if (getDialogImpl() == null) { + return; + } + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl != null) { + dialogImpl.refreshView(); + } + } + }); + } + + @Override + public boolean isCancelable() { + return true; + } + + public DialogImpl getDialogImpl() { + return dialogImpl; + } + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + public DialogLifecycleCallback getDialogLifecycleCallback() { + return dialogLifecycleCallback == null ? new DialogLifecycleCallback() { + } : dialogLifecycleCallback; + } + + public PopMenu setDialogLifecycleCallback(DialogLifecycleCallback dialogLifecycleCallback) { + this.dialogLifecycleCallback = dialogLifecycleCallback; + if (isShow) { + dialogLifecycleCallback.onShow(me); + } + return this; + } + + public boolean isOverlayBaseView() { + return overlayBaseView; + } + + public PopMenu setOverlayBaseView(boolean overlayBaseView) { + this.overlayBaseView = overlayBaseView; + refreshUI(); + return this; + } + + public OnMenuItemClickListener getOnMenuItemClickListener() { + return onMenuItemClickListener == null ? new OnMenuItemClickListener() { + @Override + public boolean onClick(PopMenu dialog, CharSequence text, int index) { + return false; + } + } : onMenuItemClickListener; + } + + public PopMenu setOnMenuItemClickListener(OnMenuItemClickListener onMenuItemClickListener) { + this.onMenuItemClickListener = onMenuItemClickListener; + return this; + } + + public OnIconChangeCallBack getOnIconChangeCallBack() { + return onIconChangeCallBack; + } + + public PopMenu setOnIconChangeCallBack(OnIconChangeCallBack onIconChangeCallBack) { + this.onIconChangeCallBack = onIconChangeCallBack; + return this; + } + + public PopMenu setCustomView(OnBindView onBindView) { + this.onBindView = onBindView; + refreshUI(); + return this; + } + + public View getCustomView() { + if (onBindView == null) { + return null; + } + return onBindView.getCustomView(); + } + + public PopMenu removeCustomView() { + this.onBindView.clean(); + refreshUI(); + return this; + } + + public int getWidth() { + return width; + } + + /** + * 设置菜单 UI 宽度(单位:像素) + * + * @param width 宽度(像素) + * @return PopMenu实例 + */ + public PopMenu setWidth(int width) { + this.width = width; + refreshUI(); + return this; + } + + public int getHeight() { + return height; + } + + /** + * 设置菜单 UI 高度(单位:像素) + * + * @param height 高度(像素) + * @return PopMenu实例 + */ + public PopMenu setHeight(int height) { + this.height = height; + refreshUI(); + return this; + } + + public int getAlignGravity() { + return alignGravity; + } + + /** + * 判断是否有设置对应的位置关系 + * + * @param gravity 位置关系 + * @return 是否具备位置关系 + */ + public boolean isAlignGravity(int gravity) { + return (alignGravity & gravity) == gravity; + } + + public PopMenu setAlignGravity(int alignGravity) { + this.alignGravity = alignGravity; + refreshMenuLoc(); + return this; + } + + public PopMenu setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public TextInfo getMenuTextInfo() { + if (menuTextInfo == null) { + return DialogX.menuTextInfo; + } + return menuTextInfo; + } + + public PopMenu setMenuTextInfo(TextInfo menuTextInfo) { + this.menuTextInfo = menuTextInfo; + return this; + } + + public boolean isOffScreen() { + return offScreen; + } + + /** + * 是否允许超出屏幕显示 + * PopMenu 默认位置显示在屏幕内,开启后将无视屏幕范围限制。 + * + * @param offScreen 超出屏幕 + * @return PopMenu实例 + */ + public PopMenu setOffScreen(boolean offScreen) { + this.offScreen = offScreen; + return this; + } + + public boolean isBkgInterceptTouch() { + return bkgInterceptTouch; + } + + public PopMenu setBkgInterceptTouch(boolean bkgInterceptTouch) { + this.bkgInterceptTouch = bkgInterceptTouch; + return this; + } + + public OnBackgroundMaskClickListener getOnBackgroundMaskClickListener() { + return onBackgroundMaskClickListener; + } + + public PopMenu setOnBackgroundMaskClickListener(OnBackgroundMaskClickListener onBackgroundMaskClickListener) { + this.onBackgroundMaskClickListener = onBackgroundMaskClickListener; + refreshUI(); + return this; + } + + public PopMenu setStyle(DialogXStyle style) { + this.style = style; + return this; + } + + public PopMenu setTheme(DialogX.THEME theme) { + this.theme = theme; + return this; + } + + public PopMenu setRadius(float radiusPx) { + backgroundRadius = radiusPx; + refreshUI(); + return this; + } + + public PopMenu setHapticFeedbackEnabled(boolean isHapticFeedbackEnabled) { + this.isHapticFeedbackEnabled = isHapticFeedbackEnabled ? 1 : 0; + return this; + } + + public float getRadius() { + return backgroundRadius; + } + + public void hide() { + isHide = true; + hideWithExitAnim = false; + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + } + + protected boolean hideWithExitAnim; + + public void hideWithExitAnim() { + hideWithExitAnim = true; + isHide = true; + if (getDialogImpl() != null) { + getDialogImpl().getDialogXAnimImpl().doExitAnim(me, getDialogImpl().boxBody); + runOnMainDelay(new Runnable() { + @Override + public void run() { + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + } + }, getDialogImpl().getExitAnimationDuration(null)); + } + } + + public DialogXAnimInterface getDialogXAnimImpl() { + return dialogXAnimImpl; + } + + public PopMenu setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public OnBackPressedListener getOnBackPressedListener() { + return onBackPressedListener; + } + + public PopMenu setOnBackPressedListener(OnBackPressedListener onBackPressedListener) { + this.onBackPressedListener = onBackPressedListener; + return this; + } + + public View getBaseView() { + return baseView(); + } + + public PopMenu setBaseView(View baseView) { + baseView(baseView); + refreshUI(); + return this; + } + + public PopMenu setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public PopMenu setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + public int getPressedIndex() { + return pressedIndex; + } + + // 设置已选择的菜单项(菜单背景会有选中状态的显示) + public PopMenu setPressedIndex(int pressedIndex) { + this.pressedIndex = pressedIndex; + refreshUI(); + return this; + } + + public int getSelectIndex() { + return selectIndex; + } + + public CharSequence getSelectMenuText() { + if (menuList == null) return ""; + return menuList.get(selectIndex); + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new PopMenu() { + * + * @param dialog self + * @Override public void onShow(PopMenu dialog) { + * //... + * } + * } + */ + protected void onShow(PopMenu dialog) { + + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new PopMenu() { + * + * @param dialog self + * @Override public boolean onDismiss(PopMenu dialog) { + * WaitDialog.show("Please Wait..."); + * if (dialog.getButtonSelectResult() == BUTTON_SELECT_RESULT.BUTTON_OK) { + * //点击了OK的情况 + * //... + * } else { + * //其他按钮点击、对话框dismiss的情况 + * //... + * } + * return false; + * } + * } + */ + // 用于使用 new 构建实例时,override 的生命周期事件 + protected void onDismiss(PopMenu dialog) { + + } + + public MenuItemLayoutRefreshCallback getMenuMenuItemLayoutRefreshCallback() { + return menuMenuItemLayoutRefreshCallback; + } + + public PopMenu setMenuMenuItemLayoutRefreshCallback(MenuItemLayoutRefreshCallback menuMenuItemLayoutRefreshCallback) { + this.menuMenuItemLayoutRefreshCallback = menuMenuItemLayoutRefreshCallback; + return this; + } + + protected PopMenu baseView(View view) { + if (view == null && baseViewWeakReference != null) { + baseViewWeakReference.clear(); + baseViewWeakReference = null; + } else { + baseViewWeakReference = new WeakReference<>(view); + } + return this; + } + + protected View baseView() { + return baseViewWeakReference == null ? null : baseViewWeakReference.get(); + } + + public PopMenu setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public PopMenu onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public PopMenu onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public PopMenu setEnableImmersiveMode(boolean enableImmersiveMode) { + this.enableImmersiveMode = enableImmersiveMode; + refreshUI(); + return this; + } + + public List getIconResIds() { + return iconResIds; + } + + public int getIconResIds(int position) { + if (iconResIds != null && position >= 0 && position < iconResIds.size()) { + return iconResIds.get(position); + } + return 0; + } + + public PopMenu setIconResIds(int... resIds) { + if (iconResIds == null) { + iconResIds = new ArrayList<>(); + } + for (int id : resIds) { + iconResIds.add(id); + } + refreshUI(); + return this; + } + + public PopMenu setIconResIds(List iconResIds) { + this.iconResIds = iconResIds; + refreshUI(); + return this; + } + + public boolean isAutoTintIconInLightOrDarkMode() { + return autoTintIconInLightOrDarkMode; + } + + public PopMenu setAutoTintIconInLightOrDarkMode(boolean autoTintIconInLightOrDarkMode) { + this.autoTintIconInLightOrDarkMode = autoTintIconInLightOrDarkMode; + return this; + } + + public PopMenu setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex(" + orderIndex + ") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public PopMenu bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public PopMenu enableMenu(int... menuIndex) { + for (int i : menuIndex) { + menuUsability.put(i, true); + } + return this; + } + + public PopMenu enableMenu(CharSequence... menuText) { + if (menuList != null && !menuList.isEmpty()) { + for (CharSequence c : menuText) { + int index = menuList.indexOf(c); + menuUsability.put(index, true); + } + } else { + error("DialogX: " + dialogKey() + " .enableMenu(" + menuText + ")执行失败,请先初始化菜单项 menuList"); + } + return this; + } + + public PopMenu enableMenu(String... menuText) { + if (menuList != null && !menuList.isEmpty()) { + for (String c : menuText) { + int index = menuList.indexOf(c); + menuUsability.put(index, true); + } + } else { + error("DialogX: " + dialogKey() + " .enableMenu(" + menuText + ")执行失败,请先初始化菜单项 menuList"); + } + return this; + } + + public PopMenu disableMenu(int... menuIndex) { + for (int i : menuIndex) { + menuUsability.put(i, false); + } + return this; + } + + public PopMenu disableMenu(CharSequence... menuText) { + if (menuList != null && !menuList.isEmpty()) { + for (CharSequence c : menuText) { + int index = menuList.indexOf(c); + menuUsability.put(index, false); + } + } else { + error("DialogX: " + dialogKey() + " .disableMenu(" + menuText + ")执行失败,请先初始化菜单项 menuList"); + } + return this; + } + + public PopMenu disableMenu(String... menuText) { + if (menuList != null && !menuList.isEmpty()) { + for (String c : menuText) { + int index = menuList.indexOf(c); + menuUsability.put(index, false); + } + } else { + error("DialogX: " + dialogKey() + " .disableMenu(" + menuText + ")执行失败,请先初始化菜单项 menuList"); + } + return this; + } + + public boolean isMenuItemEnable(int index) { + Boolean enabled = menuUsability.get(index); + if (enabled == null) { + return true; + } + return enabled; + } + + public int getBackgroundColor() { + return backgroundColor; + } + + public PopMenu setBackgroundColor(@ColorInt int backgroundColor) { + this.backgroundColor = backgroundColor; + refreshUI(); + return this; + } + + public PopMenu setBackgroundColorRes(@ColorRes int backgroundColorResId) { + this.backgroundColor = getColor(backgroundColorResId); + refreshUI(); + return this; + } + + public PopMenu setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public PopMenu cleanAction(int actionId) { + dialogActionRunnableMap.remove(actionId); + return this; + } + + public PopMenu cleanAllAction() { + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + public void callDialogDismiss() { + dismiss(); + } + + public PopMenu bindDismissWithLifecycleOwner(LifecycleOwner owner) { + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } + + public ItemDivider getItemDivider() { + return itemDivider; + } + + public PopMenu setItemDivider(ItemDivider itemDivider) { + this.itemDivider = itemDivider; + refreshUI(); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/PopNotification.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/PopNotification.java new file mode 100644 index 0000000..8d067e0 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/PopNotification.java @@ -0,0 +1,1732 @@ +package com.kongzue.dialogx.dialogs; + +import android.animation.ValueAnimator; +import android.app.Activity; +import android.graphics.Bitmap; +import android.graphics.Outline; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.os.Build; +import android.text.TextUtils; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.view.animation.DecelerateInterpolator; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.annotation.Nullable; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.interfaces.BlurViewType; +import com.kongzue.dialogx.interfaces.DialogConvertViewInterface; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.NoTouchInterface; +import com.kongzue.dialogx.interfaces.OnBindView; +import com.kongzue.dialogx.interfaces.OnDialogButtonClickListener; +import com.kongzue.dialogx.interfaces.OnSafeInsetsChangeListener; +import com.kongzue.dialogx.interfaces.PopMoveDisplacementInterceptor; +import com.kongzue.dialogx.util.PopValueAnimator; +import com.kongzue.dialogx.util.TextInfo; +import com.kongzue.dialogx.util.views.DialogXBaseRelativeLayout; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/20 11:59 + */ +public class PopNotification extends BaseDialog implements NoTouchInterface { + + public static final int TIME_NO_AUTO_DISMISS_DELAY = -1; + protected static List popNotificationList; + + public static long overrideEnterDuration = -1; + public static long overrideExitDuration = -1; + public static int overrideEnterAnimRes = 0; + public static int overrideExitAnimRes = 0; + + public static int maxShowCount = Integer.MAX_VALUE; + public static PopMoveDisplacementInterceptor moveDisplacementInterceptor; + + protected OnBindView onBindView; + protected DialogLifecycleCallback dialogLifecycleCallback; + protected PopNotification me = this; + protected DialogImpl dialogImpl; + protected int enterAnimResId = 0; + protected int exitAnimResId = 0; + protected DialogXStyle.PopNotificationSettings.ALIGN align; + protected OnDialogButtonClickListener onButtonClickListener; + protected OnDialogButtonClickListener onPopNotificationClickListener; + protected boolean autoTintIconInLightOrDarkMode = true; + protected BOOLEAN tintIcon; + protected float backgroundRadius = DialogX.defaultPopNotificationBackgroundRadius; + protected DialogXAnimInterface dialogXAnimImpl; + + protected int iconResId; + protected Bitmap iconBitmap; + protected Drawable iconDrawable; + protected CharSequence title; + protected CharSequence message; + protected CharSequence buttonText; + protected int iconSize; + protected boolean slideToClose = true; + + protected TextInfo titleTextInfo; + protected TextInfo messageTextInfo; + protected TextInfo buttonTextInfo = new TextInfo().setBold(true); + protected int[] bodyMargin = new int[]{-1, -1, -1, -1}; + + protected PopNotification() { + super(); + } + + @Override + public boolean isCancelable() { + return false; + } + + public static PopNotification build() { + return new PopNotification(); + } + + public static PopNotification build(DialogXStyle style) { + return new PopNotification().setStyle(style); + } + + public static PopNotification build(OnBindView onBindView) { + return new PopNotification().setCustomView(onBindView); + } + + public PopNotification(OnBindView onBindView) { + this.onBindView = onBindView; + } + + public PopNotification(CharSequence title) { + this.title = title; + } + + public PopNotification(CharSequence title, CharSequence message) { + this.title = title; + this.message = message; + } + + public PopNotification(int titleResId) { + this.title = getString(titleResId); + } + + public PopNotification(int titleResId, int messageResId) { + this.title = getString(titleResId); + this.message = getString(messageResId); + } + + public PopNotification(int iconResId, CharSequence title) { + this.iconResId = iconResId; + this.title = title; + } + + public PopNotification(int iconResId, CharSequence title, CharSequence message) { + this.iconResId = iconResId; + this.title = title; + this.message = message; + } + + public PopNotification(int iconResId, int titleResId, int messageResId) { + this.iconResId = iconResId; + this.title = getString(titleResId); + this.message = getString(messageResId); + } + + public PopNotification(CharSequence title, OnBindView onBindView) { + this.title = title; + this.onBindView = onBindView; + } + + public PopNotification(CharSequence title, CharSequence message, OnBindView onBindView) { + this.title = title; + this.message = message; + this.onBindView = onBindView; + } + + public PopNotification(int titleResId, OnBindView onBindView) { + this.title = getString(titleResId); + this.onBindView = onBindView; + } + + public PopNotification(int titleResId, int messageResId, OnBindView onBindView) { + this.title = getString(titleResId); + this.message = getString(messageResId); + this.onBindView = onBindView; + } + + public PopNotification(int iconResId, CharSequence title, OnBindView onBindView) { + this.iconResId = iconResId; + this.title = title; + this.onBindView = onBindView; + } + + public PopNotification(int iconResId, CharSequence title, CharSequence message, OnBindView onBindView) { + this.iconResId = iconResId; + this.title = title; + this.message = message; + this.onBindView = onBindView; + } + + public PopNotification(int iconResId, int titleResId, int messageResId, OnBindView onBindView) { + this.iconResId = iconResId; + this.title = getString(titleResId); + this.message = getString(messageResId); + this.onBindView = onBindView; + } + + public PopNotification(int iconResId, int titleResId, int messageResId, int buttonTextResId) { + this.iconResId = iconResId; + this.title = getString(titleResId); + this.message = getString(messageResId); + this.buttonText = getString(buttonTextResId); + } + + public PopNotification(int iconResId, int titleResId, int messageResId, int buttonTextResId, OnBindView onBindView) { + this.iconResId = iconResId; + this.title = getString(titleResId); + this.message = getString(messageResId); + this.buttonText = getString(buttonTextResId); + this.onBindView = onBindView; + } + + public PopNotification(int iconResId, CharSequence title, CharSequence message, CharSequence buttonText) { + this.iconResId = iconResId; + this.title = title; + this.message = message; + this.buttonText = buttonText; + } + + public PopNotification(int iconResId, CharSequence title, CharSequence message, CharSequence buttonText, OnBindView onBindView) { + this.iconResId = iconResId; + this.title = title; + this.message = message; + this.buttonText = buttonText; + this.onBindView = onBindView; + } + + public static PopNotification show(OnBindView onBindView) { + PopNotification popNotification = new PopNotification(onBindView); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(CharSequence title) { + PopNotification popNotification = new PopNotification(title); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(CharSequence title, CharSequence message) { + PopNotification popNotification = new PopNotification(title, message); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int titleResId) { + PopNotification popNotification = new PopNotification(titleResId); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int titleResId, int messageResId) { + PopNotification popNotification = new PopNotification(titleResId, messageResId); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(CharSequence title, OnBindView onBindView) { + PopNotification popNotification = new PopNotification(title, onBindView); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(CharSequence title, CharSequence message, OnBindView onBindView) { + PopNotification popNotification = new PopNotification(title, message, onBindView); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int titleResId, OnBindView onBindView) { + PopNotification popNotification = new PopNotification(titleResId, onBindView); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int titleResId, int messageResId, OnBindView onBindView) { + PopNotification popNotification = new PopNotification(titleResId, messageResId, onBindView); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int iconResId, CharSequence title, OnBindView onBindView) { + PopNotification popNotification = new PopNotification(iconResId, title, onBindView); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int iconResId, CharSequence title, CharSequence message, OnBindView onBindView) { + PopNotification popNotification = new PopNotification(iconResId, title, message, onBindView); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int iconResId, CharSequence title) { + PopNotification popNotification = new PopNotification(iconResId, title); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int iconResId, CharSequence title, CharSequence message) { + PopNotification popNotification = new PopNotification(iconResId, title, message); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int iconResId, int titleResId, int messageResId) { + PopNotification popNotification = new PopNotification(iconResId, titleResId, messageResId); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int iconResId, int titleResId, int messageResId, OnBindView onBindView) { + PopNotification popNotification = new PopNotification(iconResId, titleResId, messageResId, onBindView); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int iconResId, int titleResId, int messageResId, int buttonTextResId) { + PopNotification popNotification = new PopNotification(iconResId, titleResId, messageResId, buttonTextResId); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int iconResId, int titleResId, int messageResId, int buttonTextResId, OnBindView onBindView) { + PopNotification popNotification = new PopNotification(iconResId, titleResId, messageResId, buttonTextResId, onBindView); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int iconResId, CharSequence title, CharSequence message, CharSequence buttonText) { + PopNotification popNotification = new PopNotification(iconResId, title, message, buttonText); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int iconResId, CharSequence title, CharSequence message, CharSequence buttonText, OnBindView onBindView) { + PopNotification popNotification = new PopNotification(iconResId, title, message, buttonText, onBindView); + popNotification.show(); + return popNotification; + } + + public PopNotification show() { + if (isHide && getDialogView() != null) { + getDialogView().setVisibility(View.VISIBLE); + return this; + } + super.beforeShow(); + if (getDialogView() == null) { + if (DialogX.onlyOnePopNotification) { + if (popNotificationList != null && !popNotificationList.isEmpty()) { + popNotificationList.get(popNotificationList.size() - 1).dismiss(); + } + } else { + if (popNotificationList != null) { + CopyOnWriteArrayList popNotificationCopyOnWriteArrayList = new CopyOnWriteArrayList<>(popNotificationList); + for (int i = 0; i < popNotificationCopyOnWriteArrayList.size(); i++) { + PopNotification popInstance = popNotificationCopyOnWriteArrayList.get(i); + if (popNotificationCopyOnWriteArrayList.size() >= maxShowCount) { + if (i <= popNotificationCopyOnWriteArrayList.size() - maxShowCount) { + popInstance.dismiss(); + popNotificationList.remove(popInstance); + } + } + } + } + } + if (popNotificationList == null) popNotificationList = new ArrayList<>(); + popNotificationList.add(PopNotification.this); + int layoutResId = isLightTheme() ? R.layout.layout_dialogx_popnotification_material : R.layout.layout_dialogx_popnotification_material_dark; + if (style.popNotificationSettings() != null) { + if (style.popNotificationSettings().layout(isLightTheme()) != 0) { + layoutResId = style.popNotificationSettings().layout(isLightTheme()); + } + align = style.popNotificationSettings().align(); + if (align == null) align = DialogXStyle.PopNotificationSettings.ALIGN.TOP; + int styleEnterAnimResId = style.popNotificationSettings().enterAnimResId(isLightTheme()); + int styleExitAnimResId = style.popNotificationSettings().exitAnimResId(isLightTheme()); + enterAnimResId = enterAnimResId == 0 ? ( + overrideEnterAnimRes == 0 ? (styleEnterAnimResId != 0 ? styleEnterAnimResId : R.anim.anim_dialogx_notification_enter) : overrideEnterAnimRes + ) : enterAnimResId; + exitAnimResId = exitAnimResId == 0 ? ( + overrideExitAnimRes == 0 ? (styleExitAnimResId != 0 ? styleExitAnimResId : R.anim.anim_dialogx_notification_exit) : overrideExitAnimRes + ) : exitAnimResId; + enterAnimDuration = enterAnimDuration == -1 ? ( + overrideEnterDuration + ) : enterAnimDuration; + exitAnimDuration = exitAnimDuration == -1 ? ( + overrideExitDuration + ) : exitAnimDuration; + } + View dialogView = createView(layoutResId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + } else { + show(getDialogView()); + } + return this; + } + + public PopNotification show(Activity activity) { + super.beforeShow(); + if (getDialogView() == null) { + if (DialogX.onlyOnePopNotification) { + PopNotification oldInstance = null; + if (popNotificationList != null && !popNotificationList.isEmpty()) { + oldInstance = popNotificationList.get(popNotificationList.size() - 1); + } + if (oldInstance != null) { + oldInstance.dismiss(); + } + } + if (popNotificationList == null) popNotificationList = new ArrayList<>(); + popNotificationList.add(PopNotification.this); + int layoutResId = isLightTheme() ? R.layout.layout_dialogx_popnotification_material : R.layout.layout_dialogx_popnotification_material_dark; + if (style.popNotificationSettings() != null) { + if (style.popNotificationSettings().layout(isLightTheme()) != 0) { + layoutResId = style.popNotificationSettings().layout(isLightTheme()); + } + align = style.popNotificationSettings().align(); + if (align == null) align = DialogXStyle.PopNotificationSettings.ALIGN.TOP; + int styleEnterAnimResId = style.popNotificationSettings().enterAnimResId(isLightTheme()); + int styleExitAnimResId = style.popNotificationSettings().exitAnimResId(isLightTheme()); + enterAnimResId = enterAnimResId == 0 ? ( + overrideEnterAnimRes == 0 ? (styleEnterAnimResId != 0 ? styleEnterAnimResId : R.anim.anim_dialogx_notification_enter) : overrideEnterAnimRes + ) : enterAnimResId; + exitAnimResId = exitAnimResId == 0 ? ( + overrideExitAnimRes == 0 ? (styleExitAnimResId != 0 ? styleExitAnimResId : R.anim.anim_dialogx_notification_exit) : overrideExitAnimRes + ) : exitAnimResId; + enterAnimDuration = enterAnimDuration == -1 ? ( + overrideEnterDuration + ) : enterAnimDuration; + exitAnimDuration = exitAnimDuration == -1 ? ( + overrideExitDuration + ) : exitAnimDuration; + } + View dialogView = createView(layoutResId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(activity, dialogView); + } else { + show(activity, getDialogView()); + } + return this; + } + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + protected Timer autoDismissTimer; + protected long autoDismissDelay = Long.MIN_VALUE; + + public PopNotification autoDismiss(long delay) { + autoDismissDelay = delay; + if (autoDismissTimer != null) { + autoDismissTimer.cancel(); + } + if (delay < 0) return this; + autoDismissTimer = new Timer(); + autoDismissTimer.schedule(new TimerTask() { + @Override + public void run() { + dismiss(); + } + }, delay); + return this; + } + + public void resetAutoDismissTimer() { + autoDismiss(autoDismissDelay); + } + + private boolean isNoSetCustomDelay() { + return autoDismissDelay == Long.MIN_VALUE; + } + + public PopNotification showShort() { + if (isNoSetCustomDelay()) autoDismiss(2000); + if (!preShow && !isShow) { + show(); + } + return this; + } + + public PopNotification showLong() { + autoDismiss(3500); + if (!preShow && !isShow) { + show(); + } + return this; + } + + public PopNotification showAlways() { + return noAutoDismiss(); + } + + public PopNotification noAutoDismiss() { + autoDismiss(TIME_NO_AUTO_DISMISS_DELAY); + return this; + } + + private float defaultTop; + + public class DialogImpl implements DialogConvertViewInterface { + + public DialogXBaseRelativeLayout boxRoot; + public ViewGroup boxBody; + public ImageView imgDialogxPopIcon; + public TextView txtDialogxPopTitle; + public TextView txtDialogxPopMessage; + public TextView txtDialogxButton; + public RelativeLayout boxCustom; + + private List blurViews; + + public DialogImpl(View convertView) { + if (convertView == null) return; + setDialogView(convertView); + boxRoot = convertView.findViewById(R.id.box_root); + boxBody = convertView.findViewById(R.id.box_body); + imgDialogxPopIcon = convertView.findViewById(R.id.img_dialogx_pop_icon); + txtDialogxPopTitle = convertView.findViewById(R.id.txt_dialogx_pop_title); + txtDialogxPopMessage = convertView.findViewById(R.id.txt_dialogx_pop_message); + txtDialogxButton = convertView.findViewById(R.id.txt_dialogx_button); + boxCustom = convertView.findViewById(R.id.box_custom); + + blurViews = findAllBlurView(convertView); + + init(); + dialogImpl = this; + refreshView(); + } + + @Override + public void init() { + if (titleTextInfo == null) titleTextInfo = DialogX.titleTextInfo; + if (messageTextInfo == null) messageTextInfo = DialogX.messageTextInfo; + if (buttonTextInfo == null) buttonTextInfo = DialogX.buttonTextInfo; + if (backgroundColor == null) backgroundColor = DialogX.backgroundColor; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(getThisOrderIndex()); + } + + if (autoDismissTimer == null) { + showShort(); + } + + boxRoot.setClickable(false); + boxRoot.setFocusable(false); + boxRoot.setParentDialog(me); + boxRoot.setAutoUnsafePlacePadding(false); + boxRoot.setOnLifecycleCallBack(new DialogXBaseRelativeLayout.OnLifecycleCallBack() { + @Override + public void onShow() { + isShow = true; + preShow = false; + + setLifecycleState(Lifecycle.State.CREATED); + boxRoot.setAlpha(0f); + + onDialogShow(); + getDialogLifecycleCallback().onShow(me); + PopNotification.this.onShow(me); + } + + @Override + public void onDismiss() { + if (popNotificationList != null) { + popNotificationList.remove(PopNotification.this); + if (popNotificationList.isEmpty()) { + popNotificationList = null; + } + } + if (autoDismissTimer != null) { + autoDismissTimer.cancel(); + } + isShow = false; + getDialogLifecycleCallback().onDismiss(me); + PopNotification.this.onDismiss(me); + setLifecycleState(Lifecycle.State.DESTROYED); + dialogImpl = null; + System.gc(); + } + }); + + RelativeLayout.LayoutParams rlp; + rlp = ((RelativeLayout.LayoutParams) boxBody.getLayoutParams()); + if (align == null) align = DialogXStyle.PopNotificationSettings.ALIGN.TOP; + switch (align) { + case TOP: + rlp.removeRule(RelativeLayout.CENTER_IN_PARENT); + rlp.addRule(RelativeLayout.ALIGN_PARENT_TOP); + break; + case BOTTOM: + rlp.removeRule(RelativeLayout.CENTER_IN_PARENT); + rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + boxRoot.setAutoUnsafePlacePadding(true); + break; + case CENTER: + rlp.removeRule(RelativeLayout.ALIGN_PARENT_TOP); + rlp.removeRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + rlp.addRule(RelativeLayout.CENTER_IN_PARENT); + break; + } + boxBody.setLayoutParams(rlp); + + boxRoot.setOnSafeInsetsChangeListener(new OnSafeInsetsChangeListener() { + @Override + public void onChange(Rect unsafeRect) { + if (align == DialogXStyle.PopNotificationSettings.ALIGN.TOP) { + boxBody.setY(defaultTop <= 0 ? defaultTop = (unsafeRect.top + bodyMargin[1]) : defaultTop); + } else if (align == DialogXStyle.PopNotificationSettings.ALIGN.TOP_INSIDE) { + boxBody.setPadding(0, unsafeRect.top, 0, 0); + } + } + }); + + boxRoot.setOnBackPressedListener(new DialogXBaseRelativeLayout.PrivateBackPressedListener() { + @Override + public boolean onBackPressed() { + return false; + } + }); + + boxRoot.post(new Runnable() { + @Override + public void run() { + getDialogXAnimImpl().doShowAnim(me, boxBody); + + if (!DialogX.onlyOnePopNotification) { + if (popNotificationList != null) { + for (int i = 0; i < popNotificationList.size() - 1; i++) { + PopNotification popInstance = popNotificationList.get(i); + popInstance.moveBack(boxBody.getHeight()); + } + } + } + + Integer blurFrontColor = null; + Float popNotificationRadius = null; + if (getStyle().popNotificationSettings() != null && getStyle().popNotificationSettings().blurBackgroundSettings() != null && + getStyle().popNotificationSettings().blurBackgroundSettings().blurBackground()) { + blurFrontColor = backgroundColor == null ? + getColorNullable(getIntStyleAttr(getStyle().popNotificationSettings().blurBackgroundSettings().blurForwardColorRes(isLightTheme()))) : + backgroundColor; + popNotificationRadius = getFloatStyleAttr((float) getStyle().popNotificationSettings().blurBackgroundSettings().blurBackgroundRoundRadiusPx()); + } + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setOverlayColor(backgroundColor == null ? blurFrontColor : backgroundColor); + ((BlurViewType) blurView).setRadiusPx(popNotificationRadius); + } + } + setLifecycleState(Lifecycle.State.RESUMED); + } + }); + + boxBody.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (dismissAnimFlag) { + log("skip click @ A"); + return; + } + haptic(v); + if (onPopNotificationClickListener != null) { + if (!onPopNotificationClickListener.onClick(me, v)) { + dismiss(); + } + } else { + dismiss(); + } + } + }); + + txtDialogxButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (dismissAnimFlag) { + return; + } + haptic(v); + if (onButtonClickListener != null) { + if (!onButtonClickListener.onClick(me, v)) { + doDismiss(v); + } + } else { + doDismiss(v); + } + } + }); + onDialogInit(); + } + + @Override + public void refreshView() { + if (boxRoot == null) { + return; + } + boxRoot.setRootPadding(screenPaddings[0], screenPaddings[1], screenPaddings[2], screenPaddings[3]); + if (backgroundColor != null) { + tintColor(boxBody, backgroundColor); + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setOverlayColor(backgroundColor); + } + } + } + + if (onBindView != null && onBindView.getCustomView() != null) { + onBindView.bindParent(boxCustom, me); + boxCustom.setVisibility(View.VISIBLE); + } else { + boxCustom.setVisibility(View.GONE); + } + + if (backgroundRadius > -1) { + if (boxBody.getBackground() instanceof GradientDrawable) { + GradientDrawable gradientDrawable = (GradientDrawable) boxBody.getBackground(); + if (gradientDrawable != null) + gradientDrawable.setCornerRadius(backgroundRadius); + } + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + boxBody.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), backgroundRadius); + } + }); + boxBody.setClipToOutline(true); + } + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setRadiusPx(backgroundRadius); + } + } + } + + showText(txtDialogxPopTitle, title); + showText(txtDialogxPopMessage, message); + showText(txtDialogxButton, buttonText); + + useTextInfo(txtDialogxPopTitle, titleTextInfo); + useTextInfo(txtDialogxPopMessage, messageTextInfo); + useTextInfo(txtDialogxButton, buttonTextInfo); + + if (iconBitmap != null && !iconBitmap.isRecycled()) { + imgDialogxPopIcon.setVisibility(View.VISIBLE); + imgDialogxPopIcon.setImageBitmap(iconBitmap); + } else { + if (iconDrawable != null) { + imgDialogxPopIcon.setVisibility(View.VISIBLE); + imgDialogxPopIcon.setImageDrawable(iconDrawable); + } else { + if (iconResId != 0) { + imgDialogxPopIcon.setVisibility(View.VISIBLE); + imgDialogxPopIcon.setImageResource(iconResId); + } else { + imgDialogxPopIcon.setVisibility(View.GONE); + } + } + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && tintIcon == BOOLEAN.TRUE) { + if (autoTintIconInLightOrDarkMode) { + imgDialogxPopIcon.setImageTintList(txtDialogxPopTitle.getTextColors()); + } else { + imgDialogxPopIcon.setImageTintList(null); + } + } else { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + imgDialogxPopIcon.setImageTintList(null); + } + } + if (iconSize > 0) { + ViewGroup.LayoutParams iLp = imgDialogxPopIcon.getLayoutParams(); + iLp.width = iconSize; + iLp.height = iconSize; + imgDialogxPopIcon.setLayoutParams(iLp); + } + + if (slideToClose) { + boxBody.setOnTouchListener(new View.OnTouchListener() { + + boolean touchDown; + float touchY; + + @Override + public boolean onTouch(View v, MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + touchDown = true; + touchY = event.getY(); + break; + case MotionEvent.ACTION_MOVE: + if (touchDown) { + float deltaY = event.getY() - touchY; + if (boxBody.getY() + deltaY < defaultTop) { + boxBody.setY(boxBody.getY() + deltaY); + } else { + boxBody.setY(defaultTop); + } + } + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + if (boxBody.getY() < defaultTop - DialogX.touchSlideTriggerThreshold) { + doDismiss(v); + } else { + PopValueAnimator valueAnimator = PopValueAnimator.ofFloat(boxBody.getY(), defaultTop); + boxBody.setTag(valueAnimator); + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + if (getDialogImpl() == null || !isShow) { + animation.cancel(); + return; + } + View bodyView = getDialogImpl().boxBody; + if (bodyView != null && bodyView.isAttachedToWindow()) { + bodyView.setY((Float) animation.getAnimatedValue()); + } + } + }); + valueAnimator.setDuration(enterAnimDuration == -1 ? 300 : enterAnimDuration).setInterpolator(new DecelerateInterpolator(2f)); + valueAnimator.start(); + + if (Math.abs(boxBody.getY() - defaultTop) <= 1 && event.getY() <= touchY) { + boxBody.callOnClick(); + } + } + break; + } + return true; + } + }); + } else { + boxBody.setOnTouchListener(null); + } + + RelativeLayout.LayoutParams rlp = ((RelativeLayout.LayoutParams) boxBody.getLayoutParams()); + if (bodyMargin[0] != -1) rlp.leftMargin = bodyMargin[0]; + if (bodyMargin[1] != -1) rlp.topMargin = bodyMargin[1]; + if (bodyMargin[2] != -1) rlp.rightMargin = bodyMargin[2]; + if (bodyMargin[3] != -1) rlp.bottomMargin = bodyMargin[3]; + boxBody.setLayoutParams(rlp); + onDialogRefreshUI(); + } + + @Override + public void doDismiss(final View v) { + if (PopNotification.this.preDismiss(PopNotification.this)) { + return; + } + if (v != null) v.setEnabled(false); + + if (!dismissAnimFlag && boxRoot != null) { + dismissAnimFlag = true; + boxCustom.setVisibility(View.GONE); + boxBody.setFocusable(false); + boxBody.setClickable(false); + boxBody.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + return false; + } + }); + txtDialogxButton.setFocusable(false); + txtDialogxButton.setClickable(false); + boxRoot.post(new Runnable() { + @Override + public void run() { + getDialogXAnimImpl().doExitAnim(me, boxBody); + + preRecycle = true; + runOnMainDelay(new Runnable() { + @Override + public void run() { + waitForDismiss(); + } + }, getExitAnimationDuration(null)); + + if (popNotificationList != null) { + //使位于自己之前的PopTip moveDown + int index = popNotificationList.indexOf(me); + for (int i = 0; i < index; i++) { + PopNotification popNotification = popNotificationList.get(i); + popNotification.moveFront(boxBody.getHeight()); + } + } + } + }); + } + } + + protected DialogXAnimInterface getDialogXAnimImpl() { + if (dialogXAnimImpl == null) { + dialogXAnimImpl = new DialogXAnimInterface() { + @Override + public void doShowAnim(PopNotification dialog, ViewGroup dialogBodyView) { + Animation enterAnim = AnimationUtils.loadAnimation(getApplicationContext(), enterAnimResId == 0 ? R.anim.anim_dialogx_notification_enter : enterAnimResId); + long enterAnimDuration = getEnterAnimationDuration(enterAnim); + enterAnim.setInterpolator(new DecelerateInterpolator(2f)); + enterAnim.setDuration(enterAnimDuration); + enterAnim.setFillAfter(true); + boxBody.startAnimation(enterAnim); + + boxRoot.animate() + .setDuration(enterAnimDuration) + .alpha(1f) + .setInterpolator(new DecelerateInterpolator()) + .setListener(null); + } + + @Override + public void doExitAnim(PopNotification dialog, ViewGroup dialogBodyView) { + Animation exitAnim = AnimationUtils.loadAnimation(getApplicationContext() == null ? boxRoot.getContext() : getApplicationContext(), exitAnimResId == 0 ? R.anim.anim_dialogx_notification_exit : exitAnimResId); + long exitAnimDuration = getExitAnimationDuration(exitAnim); + exitAnim.setDuration(exitAnimDuration); + exitAnim.setFillAfter(true); + boxBody.startAnimation(exitAnim); + + boxRoot.animate() + .alpha(0f) + .setInterpolator(new AccelerateInterpolator()) + .setDuration(exitAnimDuration); + } + }; + } + return dialogXAnimImpl; + } + + public long getExitAnimationDuration(@Nullable Animation defaultExitAnim) { + if (defaultExitAnim == null && boxBody.getAnimation() != null) { + defaultExitAnim = boxBody.getAnimation(); + } + long exitAnimDurationTemp = (defaultExitAnim == null || defaultExitAnim.getDuration() == 0) ? 300 : defaultExitAnim.getDuration(); + if (overrideExitDuration >= 0) { + exitAnimDurationTemp = overrideExitDuration; + } + if (exitAnimDuration != -1) { + exitAnimDurationTemp = exitAnimDuration; + } + return exitAnimDurationTemp; + } + + public long getEnterAnimationDuration(@Nullable Animation defaultEnterAnim) { + if (defaultEnterAnim == null && boxBody.getAnimation() != null) { + defaultEnterAnim = boxBody.getAnimation(); + } + long enterAnimDurationTemp = (defaultEnterAnim == null || defaultEnterAnim.getDuration() == 0) ? 300 : defaultEnterAnim.getDuration(); + if (overrideEnterDuration >= 0) { + enterAnimDurationTemp = overrideEnterDuration; + } + if (enterAnimDuration >= 0) { + enterAnimDurationTemp = enterAnimDuration; + } + return enterAnimDurationTemp; + } + } + + protected boolean preRecycle = false; + + /** + * 之所以这样处理,在较为频繁的启停 popNotification 时可能存在 popNotification 关闭动画位置错误无法计算的问题 + * 使用 preRecycle 标记记录是否需要回收,而不是立即销毁 + * 等待所有 popNotification 处于待回收状态时一并回收可以避免此问题 + */ + private void waitForDismiss() { + if (popNotificationList == null || popNotificationList.isEmpty()) { + return; + } + preRecycle = true; + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + CopyOnWriteArrayList copyPopNotificationList = new CopyOnWriteArrayList<>(popNotificationList); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + copyPopNotificationList.removeIf(Objects::isNull); + } else { + Iterator iterator = copyPopNotificationList.iterator(); + while (iterator.hasNext()) { + if (iterator.next() == null) { + iterator.remove(); + } + } + } +// boolean allPreRecycled = true; +// for (PopNotification popTip : copyPopNotificationList) { +// if (!popTip.preRecycle) { +// allPreRecycled = false; +// break; +// } +// } +// if (allPreRecycled) { +// for (PopNotification popTip : copyPopNotificationList) { +// dismiss(popTip.getDialogView()); +// } +// } + dismiss(getDialogView()); + } + + private void moveBack(int newDialogHeight) { + if (getDialogImpl() != null && getDialogImpl().boxBody != null) { + View bodyView = getDialogImpl().boxBody; + if (getDialogImpl() == null || bodyView == null) return; + if (style.popNotificationSettings() != null) + align = style.popNotificationSettings().align(); + if (align == null) align = DialogXStyle.PopNotificationSettings.ALIGN.TOP; + float moveAimTop = 0; + float y = bodyView.getY(); + if (bodyView.getTag() instanceof PopValueAnimator) { + ((PopValueAnimator) bodyView.getTag()).end(); + y = ((PopValueAnimator) bodyView.getTag()).getEndValue(); + } + switch (align) { + case TOP: + moveAimTop = y + newDialogHeight * 1.1f; + break; + case TOP_INSIDE: + moveAimTop = y + newDialogHeight - bodyView.getPaddingTop(); + break; + case CENTER: + case BOTTOM: + case BOTTOM_INSIDE: + moveAimTop = y - newDialogHeight * 1.1f; + break; + } + if (moveDisplacementInterceptor != null) { + moveAimTop = moveDisplacementInterceptor.resetAnimY(popNotificationList == null ? 0 : popNotificationList.indexOf(me), me, bodyView.getY(), moveAimTop, (int) (bodyView.getHeight() / bodyView.getScaleY()), popNotificationList == null ? 1 : popNotificationList.size(), true); + } + final float fromY = bodyView.getY(); + float toY = moveAimTop; + PopValueAnimator valueAnimator = PopValueAnimator.ofFloat(bodyView.getY(), moveAimTop); + bodyView.setTag(valueAnimator); + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + if (getDialogImpl() == null || !isShow) { + animation.cancel(); + return; + } + View bodyView = getDialogImpl().boxBody; + float value = (Float) animation.getAnimatedValue(); + float totalDistance = toY - fromY; + if (moveDisplacementInterceptor != null && moveDisplacementInterceptor.animUpdater(popNotificationList == null ? 0 : popNotificationList.indexOf(me), me, bodyView, fromY, toY, Math.max(0f, Math.min(1f, (totalDistance == 0f ? 1f : (value - fromY) / totalDistance))), animation, popNotificationList == null ? 1 : countDisplayPopNotificationNum(), true)) { + return; + } + if (bodyView != null && bodyView.isAttachedToWindow()) { + bodyView.setY(defaultTop = value); + } + } + }); + valueAnimator.setDuration(enterAnimDuration == -1 ? 300 : enterAnimDuration).setInterpolator(new DecelerateInterpolator(2f)); + valueAnimator.start(); + } + } + + private void moveFront(int newDialogHeight) { + if (getDialogImpl() != null && getDialogImpl().boxBody != null) { + View bodyView = getDialogImpl().boxBody; + if (getDialogImpl() == null || bodyView == null) return; + if (style.popNotificationSettings() != null) + align = style.popNotificationSettings().align(); + if (align == null) align = DialogXStyle.PopNotificationSettings.ALIGN.TOP; + float moveAimTop = 0; + float y = bodyView.getY(); + if (bodyView.getTag() instanceof PopValueAnimator) { + ((PopValueAnimator) bodyView.getTag()).end(); + y = ((PopValueAnimator) bodyView.getTag()).getEndValue(); + } + switch (align) { + case TOP: + moveAimTop = y - newDialogHeight * 1.1f; + break; + case TOP_INSIDE: + moveAimTop = y - newDialogHeight + bodyView.getPaddingTop(); + break; + case CENTER: + case BOTTOM: + case BOTTOM_INSIDE: + moveAimTop = y + newDialogHeight * 1.1f; + break; + } + if (moveDisplacementInterceptor != null) { + moveAimTop = moveDisplacementInterceptor.resetAnimY(popNotificationList == null ? 0 : popNotificationList.indexOf(me), me, bodyView.getY(), moveAimTop, (int) (bodyView.getHeight() / bodyView.getScaleY()), popNotificationList == null ? 1 : popNotificationList.size(), false); + } + final float fromY = bodyView.getY(); + float toY = moveAimTop; + PopValueAnimator valueAnimator = PopValueAnimator.ofFloat(fromY, toY); + bodyView.setTag(valueAnimator); + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + if (getDialogImpl() == null || !isShow) { + animation.cancel(); + return; + } + View bodyView = getDialogImpl().boxBody; + float value = (Float) animation.getAnimatedValue(); + float totalDistance = toY - fromY; + if (moveDisplacementInterceptor != null && moveDisplacementInterceptor.animUpdater(popNotificationList == null ? 0 : popNotificationList.indexOf(me), me, bodyView, fromY, toY, Math.max(0f, Math.min(1f, (totalDistance == 0f ? 1f : (value - fromY) / totalDistance))), animation, popNotificationList == null ? 1 : countDisplayPopNotificationNum(), false)) { + return; + } + if (bodyView != null && bodyView.isAttachedToWindow()) { + bodyView.setY(defaultTop = value); + } + } + }); + valueAnimator.setDuration(exitAnimDuration == -1 ? 300 : exitAnimDuration).setInterpolator(new DecelerateInterpolator(2f)); + valueAnimator.start(); + } + } + + private int countDisplayPopNotificationNum() { + if (popNotificationList == null) return 0; + int count = 0; + for (int i = 0; i < popNotificationList.size(); i++) { + PopNotification tips = popNotificationList.get(i); + if (tips != null && !tips.preRecycle) { + count++; + } + } + return count; + } + + public void refreshUI() { + if (getDialogImpl() == null) return; + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl != null) dialogImpl.refreshView(); + } + }); + } + + public void dismiss() { + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl == null) return; + dialogImpl.doDismiss(null); + } + }); + } + + public DialogLifecycleCallback getDialogLifecycleCallback() { + return dialogLifecycleCallback == null ? new DialogLifecycleCallback() { + } : dialogLifecycleCallback; + } + + public PopNotification setDialogLifecycleCallback(DialogLifecycleCallback dialogLifecycleCallback) { + this.dialogLifecycleCallback = dialogLifecycleCallback; + if (isShow) dialogLifecycleCallback.onShow(me); + return this; + } + + public PopNotification setStyle(DialogXStyle style) { + this.style = style; + return this; + } + + public PopNotification setTheme(DialogX.THEME theme) { + this.theme = theme; + return this; + } + + public PopNotification.DialogImpl getDialogImpl() { + return dialogImpl; + } + + public PopNotification setCustomView(OnBindView onBindView) { + this.onBindView = onBindView; + refreshUI(); + return this; + } + + public View getCustomView() { + if (onBindView == null) return null; + return onBindView.getCustomView(); + } + + public PopNotification removeCustomView() { + this.onBindView.clean(); + refreshUI(); + return this; + } + + public DialogXStyle.PopNotificationSettings.ALIGN getAlign() { + return align; + } + + public PopNotification setAlign(DialogXStyle.PopNotificationSettings.ALIGN align) { + this.align = align; + return this; + } + + public int getIconResId() { + return iconResId; + } + + public PopNotification setIconResId(int iconResId) { + this.iconResId = iconResId; + refreshUI(); + return this; + } + + public PopNotification setIcon(Bitmap bitmap) { + this.iconBitmap = bitmap; + refreshUI(); + return this; + } + + public PopNotification setIcon(Drawable iconDrawable) { + this.iconDrawable = iconDrawable; + return this; + } + + public int getIconSize() { + return iconSize; + } + + public PopNotification setIconSize(int iconSize) { + this.iconSize = iconSize; + refreshUI(); + return this; + } + + public CharSequence getMessage() { + return message; + } + + public PopNotification setMessage(CharSequence message) { + this.message = message; + refreshUI(); + return this; + } + + public PopNotification setMessage(int messageResId) { + this.message = getString(messageResId); + refreshUI(); + return this; + } + + public CharSequence getButtonText() { + return buttonText; + } + + public PopNotification setButton(CharSequence buttonText) { + this.buttonText = buttonText; + refreshUI(); + return this; + } + + public PopNotification setButton(int buttonTextResId) { + this.buttonText = getString(buttonTextResId); + refreshUI(); + return this; + } + + public PopNotification setButton(CharSequence buttonText, OnDialogButtonClickListener onButtonClickListener) { + this.buttonText = buttonText; + this.onButtonClickListener = onButtonClickListener; + refreshUI(); + return this; + } + + public PopNotification setButton(int buttonTextResId, OnDialogButtonClickListener onButtonClickListener) { + this.buttonText = getString(buttonTextResId); + this.onButtonClickListener = onButtonClickListener; + refreshUI(); + return this; + } + + public PopNotification setButton(OnDialogButtonClickListener onButtonClickListener) { + this.onButtonClickListener = onButtonClickListener; + return this; + } + + public TextInfo getMessageTextInfo() { + return messageTextInfo; + } + + public PopNotification setMessageTextInfo(TextInfo messageTextInfo) { + this.messageTextInfo = messageTextInfo; + refreshUI(); + return this; + } + + public TextInfo getButtonTextInfo() { + return buttonTextInfo; + } + + public PopNotification setButtonTextInfo(TextInfo buttonTextInfo) { + this.buttonTextInfo = buttonTextInfo; + refreshUI(); + return this; + } + + public OnDialogButtonClickListener getOnButtonClickListener() { + return onButtonClickListener; + } + + public PopNotification setOnButtonClickListener(OnDialogButtonClickListener onButtonClickListener) { + this.onButtonClickListener = onButtonClickListener; + return this; + } + + public boolean isAutoTintIconInLightOrDarkMode() { + return autoTintIconInLightOrDarkMode; + } + + public PopNotification setAutoTintIconInLightOrDarkMode(boolean autoTintIconInLightOrDarkMode) { + this.autoTintIconInLightOrDarkMode = autoTintIconInLightOrDarkMode; + refreshUI(); + return this; + } + + public OnDialogButtonClickListener getOnPopNotificationClickListener() { + return onPopNotificationClickListener; + } + + public PopNotification setOnPopNotificationClickListener(OnDialogButtonClickListener onPopNotificationClickListener) { + this.onPopNotificationClickListener = onPopNotificationClickListener; + refreshUI(); + return this; + } + + public int getBackgroundColor() { + return backgroundColor; + } + + public PopNotification setBackgroundColor(@ColorInt int backgroundColor) { + this.backgroundColor = backgroundColor; + refreshUI(); + return this; + } + + public PopNotification setBackgroundColorRes(@ColorRes int backgroundColorResId) { + this.backgroundColor = getColor(backgroundColorResId); + refreshUI(); + return this; + } + + public long getEnterAnimDuration() { + return enterAnimDuration; + } + + public PopNotification setEnterAnimDuration(long enterAnimDuration) { + this.enterAnimDuration = enterAnimDuration; + return this; + } + + public long getExitAnimDuration() { + return exitAnimDuration; + } + + public PopNotification setExitAnimDuration(long exitAnimDuration) { + this.exitAnimDuration = exitAnimDuration; + return this; + } + + @Override + public void restartDialog() { + if (getDialogView() != null) { + dismiss(getDialogView()); + isShow = false; + } + if (getDialogImpl().boxCustom != null) { + getDialogImpl().boxCustom.removeAllViews(); + } + + if (DialogX.onlyOnePopNotification) { + PopNotification oldInstance = null; + if (popNotificationList != null && !popNotificationList.isEmpty()) { + oldInstance = popNotificationList.get(popNotificationList.size() - 1); + } + if (oldInstance != null) { + oldInstance.dismiss(); + } + } + if (popNotificationList == null) popNotificationList = new ArrayList<>(); + popNotificationList.add(PopNotification.this); + + int layoutResId = isLightTheme() ? R.layout.layout_dialogx_popnotification_material : R.layout.layout_dialogx_popnotification_material_dark; + if (style.popNotificationSettings() != null) { + if (style.popNotificationSettings().layout(isLightTheme()) != 0) { + layoutResId = style.popNotificationSettings().layout(isLightTheme()); + } + align = style.popNotificationSettings().align(); + if (align == null) align = DialogXStyle.PopNotificationSettings.ALIGN.TOP; + int styleEnterAnimResId = style.popNotificationSettings().enterAnimResId(isLightTheme()); + int styleExitAnimResId = style.popNotificationSettings().exitAnimResId(isLightTheme()); + enterAnimResId = enterAnimResId == 0 ? ( + overrideEnterAnimRes == 0 ? (styleEnterAnimResId != 0 ? styleEnterAnimResId : R.anim.anim_dialogx_notification_enter) : overrideEnterAnimRes + ) : enterAnimResId; + exitAnimResId = exitAnimResId == 0 ? ( + overrideExitAnimRes == 0 ? (styleExitAnimResId != 0 ? styleExitAnimResId : R.anim.anim_dialogx_notification_exit) : overrideExitAnimRes + ) : exitAnimResId; + enterAnimDuration = enterAnimDuration == -1 ? ( + overrideEnterDuration + ) : enterAnimDuration; + exitAnimDuration = exitAnimDuration == -1 ? ( + overrideExitDuration + ) : exitAnimDuration; + } + enterAnimDuration = 0; + View dialogView = createView(layoutResId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + } + + public void hide() { + isHide = true; + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + } + + public PopNotification setAnimResId(int enterResId, int exitResId) { + this.enterAnimResId = enterResId; + this.exitAnimResId = exitResId; + return this; + } + + public PopNotification setEnterAnimResId(int enterResId) { + this.enterAnimResId = enterResId; + return this; + } + + public PopNotification setExitAnimResId(int exitResId) { + this.exitAnimResId = exitResId; + return this; + } + + @Override + protected void shutdown() { + dismiss(); + } + + public PopNotification setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public PopNotification setMargin(int left, int top, int right, int bottom) { + bodyMargin[0] = left; + bodyMargin[1] = top; + bodyMargin[2] = right; + bodyMargin[3] = bottom; + refreshUI(); + return this; + } + + public PopNotification setMarginLeft(int left) { + bodyMargin[0] = left; + refreshUI(); + return this; + } + + public PopNotification setMarginTop(int top) { + bodyMargin[1] = top; + refreshUI(); + return this; + } + + public PopNotification setMarginRight(int right) { + bodyMargin[2] = right; + refreshUI(); + return this; + } + + public PopNotification setMarginBottom(int bottom) { + bodyMargin[3] = bottom; + refreshUI(); + return this; + } + + public int getMarginLeft() { + return bodyMargin[0]; + } + + public int getMarginTop() { + return bodyMargin[1]; + } + + public int getMarginRight() { + return bodyMargin[2]; + } + + public int getMarginBottom() { + return bodyMargin[3]; + } + + public PopNotification iconSuccess() { + setTintIcon(false); + int resId = R.mipmap.ico_dialogx_success; + if (getStyle().popNotificationSettings() != null && getStyle().popNotificationSettings().defaultIconSuccess() != 0) { + resId = getStyle().popNotificationSettings().defaultIconSuccess(); + } + setIconSize(dip2px(26)); + setIconResId(resId); + return this; + } + + public PopNotification iconWarning() { + setTintIcon(false); + int resId = R.mipmap.ico_dialogx_warning; + if (getStyle().popNotificationSettings() != null && getStyle().popNotificationSettings().defaultIconWarning() != 0) { + resId = getStyle().popNotificationSettings().defaultIconWarning(); + } + setIconSize(dip2px(26)); + setIconResId(resId); + return this; + } + + public PopNotification iconError() { + setTintIcon(false); + int resId = R.mipmap.ico_dialogx_error; + if (getStyle().popNotificationSettings() != null && getStyle().popNotificationSettings().defaultIconError() != 0) { + resId = getStyle().popNotificationSettings().defaultIconError(); + } + setIconSize(dip2px(26)); + setIconResId(resId); + return this; + } + + public boolean getTintIcon() { + return tintIcon == BOOLEAN.TRUE; + } + + public PopNotification setTintIcon(boolean tintIcon) { + this.tintIcon = tintIcon ? BOOLEAN.TRUE : BOOLEAN.FALSE; + refreshUI(); + return this; + } + + public Drawable getIconDrawable() { + return iconDrawable; + } + + public Bitmap getIconBitmap() { + return iconBitmap; + } + + public CharSequence getTitle() { + return title; + } + + public PopNotification setTitle(CharSequence title) { + this.title = title; + refreshUI(); + return this; + } + + public TextInfo getTitleTextInfo() { + return titleTextInfo; + } + + public PopNotification setTitleTextInfo(TextInfo titleTextInfo) { + this.titleTextInfo = titleTextInfo; + refreshUI(); + return this; + } + + public PopNotification setRadius(float radiusPx) { + backgroundRadius = radiusPx; + refreshUI(); + return this; + } + + public float getRadius() { + return backgroundRadius; + } + + public DialogXAnimInterface getDialogXAnimImpl() { + return dialogXAnimImpl; + } + + public PopNotification setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public PopNotification setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public PopNotification setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + public PopNotification setHapticFeedbackEnabled(boolean isHapticFeedbackEnabled) { + this.isHapticFeedbackEnabled = isHapticFeedbackEnabled ? 1 : 0; + return this; + } + + public boolean isSlideToClose() { + return slideToClose; + } + + public PopNotification setSlideToClose(boolean slideToClose) { + this.slideToClose = slideToClose; + refreshUI(); + return this; + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new PopNotification() { + * + * @param dialog self + * @Override public void onShow(PopNotification dialog) { + * //... + * } + * } + */ + protected void onShow(PopNotification dialog) { + + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new PopNotification() { + * + * @param dialog self + * @Override public boolean onDismiss(PopNotification dialog) { + * WaitDialog.show("Please Wait..."); + * if (dialog.getButtonSelectResult() == BUTTON_SELECT_RESULT.BUTTON_OK) { + * //点击了OK的情况 + * //... + * } else { + * //其他按钮点击、对话框dismiss的情况 + * //... + * } + * return false; + * } + * } + */ + //用于使用 new 构建实例时,override 的生命周期事件 + protected void onDismiss(PopNotification dialog) { + + } + + public PopNotification setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public PopNotification onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public PopNotification onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public PopNotification appendMessage(CharSequence message) { + this.message = TextUtils.concat(this.message, message); + refreshUI(); + return this; + } + + public PopNotification setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex(" + orderIndex + ") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public PopNotification bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public PopNotification setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public PopNotification cleanAction(int actionId) { + dialogActionRunnableMap.remove(actionId); + return this; + } + + public PopNotification cleanAllAction() { + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + public void callDialogDismiss() { + dismiss(); + } + + public PopNotification bindDismissWithLifecycleOwner(LifecycleOwner owner) { + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/PopTip.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/PopTip.java new file mode 100644 index 0000000..500edc3 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/PopTip.java @@ -0,0 +1,1566 @@ +package com.kongzue.dialogx.dialogs; + +import android.animation.ValueAnimator; +import android.app.Activity; +import android.graphics.Outline; +import android.graphics.Rect; +import android.graphics.drawable.GradientDrawable; +import android.os.Build; +import android.text.TextUtils; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.view.animation.DecelerateInterpolator; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.annotation.Nullable; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.interfaces.BlurViewType; +import com.kongzue.dialogx.interfaces.DialogConvertViewInterface; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.NoTouchInterface; +import com.kongzue.dialogx.interfaces.OnBindView; +import com.kongzue.dialogx.interfaces.OnDialogButtonClickListener; +import com.kongzue.dialogx.interfaces.OnSafeInsetsChangeListener; +import com.kongzue.dialogx.interfaces.PopMoveDisplacementInterceptor; +import com.kongzue.dialogx.util.TextInfo; +import com.kongzue.dialogx.util.views.DialogXBaseRelativeLayout; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/20 11:59 + */ +public class PopTip extends BaseDialog implements NoTouchInterface { + + public static final int TIME_NO_AUTO_DISMISS_DELAY = -1; + protected static List popTipList; + + public static long overrideEnterDuration = -1; + public static long overrideExitDuration = -1; + public static int overrideEnterAnimRes = 0; + public static int overrideExitAnimRes = 0; + public static int maxShowCount = Integer.MAX_VALUE; + public static PopMoveDisplacementInterceptor moveDisplacementInterceptor; + + protected OnBindView onBindView; + protected DialogLifecycleCallback dialogLifecycleCallback; + protected PopTip me = this; + protected DialogImpl dialogImpl; + protected int enterAnimResId = 0; + protected int exitAnimResId = 0; + protected DialogXStyle.PopTipSettings.ALIGN align; + protected OnDialogButtonClickListener onButtonClickListener; + protected OnDialogButtonClickListener onPopTipClickListener; + protected BOOLEAN tintIcon; + protected float backgroundRadius = DialogX.defaultPopTipBackgroundRadius; + protected DialogXAnimInterface dialogXAnimImpl; + + protected int iconResId; + protected CharSequence message; + protected CharSequence buttonText; + + protected TextInfo messageTextInfo; + protected TextInfo buttonTextInfo = new TextInfo().setBold(true); + protected int[] bodyMargin = new int[]{-1, -1, -1, -1}; + + protected PopTip() { + super(); + } + + @Override + public boolean isCancelable() { + return false; + } + + public static PopTip build() { + return new PopTip(); + } + + public static PopTip build(DialogXStyle style) { + return new PopTip().setStyle(style); + } + + public static PopTip build(OnBindView onBindView) { + return new PopTip().setCustomView(onBindView); + } + + public PopTip(OnBindView onBindView) { + this.onBindView = onBindView; + } + + public PopTip(CharSequence message) { + this.message = message; + } + + public PopTip(int messageResId) { + this.message = getString(messageResId); + } + + public static PopTip tip(String message) { + return PopTip.show(message); + } + + public static PopTip tip(int messageResId) { + return PopTip.show(messageResId); + } + + public static PopTip tip(String message, String buttonText) { + return PopTip.show(message, buttonText); + } + + public static PopTip tip(int messageResId, int buttonTextResId) { + return PopTip.show(messageResId, buttonTextResId); + } + + public static PopTip tip(int iconResId, String message) { + return PopTip.show(iconResId, message); + } + + public static PopTip tip(int iconResId, String message, String buttonText) { + return PopTip.show(iconResId, message, buttonText); + } + + public PopTip(int iconResId, CharSequence message) { + this.iconResId = iconResId; + this.message = message; + } + + public PopTip(int iconResId, CharSequence message, CharSequence buttonText) { + this.iconResId = iconResId; + this.message = message; + this.buttonText = buttonText; + } + + public PopTip(int iconResId, int messageResId, int buttonTextResId) { + this.iconResId = iconResId; + this.message = getString(messageResId); + this.buttonText = getString(buttonTextResId); + } + + public PopTip(CharSequence message, CharSequence buttonText) { + this.message = message; + this.buttonText = buttonText; + } + + public PopTip(int messageResId, int buttonTextResId) { + this.message = getString(messageResId); + this.buttonText = getString(buttonTextResId); + } + + public PopTip(CharSequence message, OnBindView onBindView) { + this.message = message; + this.onBindView = onBindView; + } + + public PopTip(int messageResId, OnBindView onBindView) { + this.message = getString(messageResId); + this.onBindView = onBindView; + } + + public PopTip(int iconResId, CharSequence message, OnBindView onBindView) { + this.iconResId = iconResId; + this.message = message; + this.onBindView = onBindView; + } + + public PopTip(int iconResId, CharSequence message, CharSequence buttonText, OnBindView onBindView) { + this.iconResId = iconResId; + this.message = message; + this.buttonText = buttonText; + this.onBindView = onBindView; + } + + public PopTip(int iconResId, int messageResId, int buttonTextResId, OnBindView onBindView) { + this.iconResId = iconResId; + this.message = getString(messageResId); + this.buttonText = getString(buttonTextResId); + this.onBindView = onBindView; + } + + public PopTip(CharSequence message, CharSequence buttonText, OnBindView onBindView) { + this.message = message; + this.buttonText = buttonText; + this.onBindView = onBindView; + } + + public PopTip(int messageResId, int buttonTextResId, OnBindView onBindView) { + this.message = getString(messageResId); + this.buttonText = getString(buttonTextResId); + this.onBindView = onBindView; + } + + public static PopTip show(OnBindView onBindView) { + PopTip popTip = new PopTip(onBindView); + popTip.show(); + return popTip; + } + + public static PopTip show(CharSequence message) { + PopTip popTip = new PopTip(message); + popTip.show(); + return popTip; + } + + public static PopTip show(int messageResId) { + PopTip popTip = new PopTip(messageResId); + popTip.show(); + return popTip; + } + + public static PopTip show(CharSequence message, OnBindView onBindView) { + PopTip popTip = new PopTip(message, onBindView); + popTip.show(); + return popTip; + } + + public static PopTip show(int messageResId, OnBindView onBindView) { + PopTip popTip = new PopTip(messageResId, onBindView); + popTip.show(); + return popTip; + } + + public static PopTip show(CharSequence message, CharSequence buttonText) { + PopTip popTip = new PopTip(message, buttonText); + popTip.show(); + return popTip; + } + + public static PopTip show(int messageResId, int buttonTextResId) { + PopTip popTip = new PopTip(messageResId, buttonTextResId); + popTip.show(); + return popTip; + } + + public static PopTip show(int iconResId, CharSequence message, OnBindView onBindView) { + PopTip popTip = new PopTip(iconResId, message, onBindView); + popTip.show(); + return popTip; + } + + public static PopTip show(int iconResId, CharSequence message) { + PopTip popTip = new PopTip(iconResId, message); + popTip.show(); + return popTip; + } + + public static PopTip show(int iconResId, CharSequence message, CharSequence buttonText) { + PopTip popTip = new PopTip(iconResId, message, buttonText); + popTip.show(); + return popTip; + } + + public static PopTip show(int iconResId, CharSequence message, CharSequence buttonText, OnBindView onBindView) { + PopTip popTip = new PopTip(iconResId, message, buttonText, onBindView); + popTip.show(); + return popTip; + } + + public static PopTip show(int iconResId, int messageResId, int buttonTextResId, OnBindView onBindView) { + PopTip popTip = new PopTip(iconResId, messageResId, buttonTextResId, onBindView); + popTip.show(); + return popTip; + } + + public static PopTip show(CharSequence message, CharSequence buttonText, OnBindView onBindView) { + PopTip popTip = new PopTip(message, buttonText, onBindView); + popTip.show(); + return popTip; + } + + public static PopTip show(int messageResId, int buttonTextResId, OnBindView onBindView) { + PopTip popTip = new PopTip(messageResId, buttonTextResId, onBindView); + popTip.show(); + return popTip; + } + + public PopTip show() { + if (isHide && getDialogView() != null) { + getDialogView().setVisibility(View.VISIBLE); + return this; + } + super.beforeShow(); + if (getDialogView() == null) { + if (DialogX.onlyOnePopTip) { + PopTip oldInstance = null; + if (popTipList != null && !popTipList.isEmpty()) { + oldInstance = popTipList.get(popTipList.size() - 1); + } + if (oldInstance != null) { + oldInstance.dismiss(); + } + } else { + if (popTipList != null) { + CopyOnWriteArrayList copyPopTipList = new CopyOnWriteArrayList<>(popTipList); + for (int i = 0; i < copyPopTipList.size(); i++) { + PopTip popInstance = copyPopTipList.get(i); + if (copyPopTipList.size() < maxShowCount) { + popInstance.moveBack(); + } else { + if (i <= copyPopTipList.size() - maxShowCount) { + popInstance.dismiss(); + popTipList.remove(popInstance); + } else { + popInstance.moveBack(); + } + } + } + } + } + if (popTipList == null) popTipList = new ArrayList<>(); + popTipList.add(PopTip.this); + int layoutResId = isLightTheme() ? R.layout.layout_dialogx_poptip_material : R.layout.layout_dialogx_poptip_material_dark; + if (style.popTipSettings() != null) { + if (style.popTipSettings().layout(isLightTheme()) != 0) { + layoutResId = style.popTipSettings().layout(isLightTheme()); + } + if (align == null) { + if (style.popTipSettings().align() == null) { + align = DialogXStyle.PopTipSettings.ALIGN.BOTTOM; + } else { + align = style.popTipSettings().align(); + } + } + int styleEnterAnimResId = style.popTipSettings().enterAnimResId(isLightTheme()); + int styleExitAnimResId = style.popTipSettings().exitAnimResId(isLightTheme()); + enterAnimResId = enterAnimResId == 0 ? ( + overrideEnterAnimRes == 0 ? (styleEnterAnimResId != 0 ? styleEnterAnimResId : R.anim.anim_dialogx_default_enter) : overrideEnterAnimRes + ) : enterAnimResId; + exitAnimResId = exitAnimResId == 0 ? ( + overrideExitAnimRes == 0 ? (styleExitAnimResId != 0 ? styleExitAnimResId : R.anim.anim_dialogx_default_exit) : overrideExitAnimRes + ) : exitAnimResId; + enterAnimDuration = enterAnimDuration == -1 ? ( + overrideEnterDuration + ) : enterAnimDuration; + exitAnimDuration = exitAnimDuration == -1 ? ( + overrideExitDuration + ) : exitAnimDuration; + } + View dialogView = createView(layoutResId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + } else { + show(getDialogView()); + } + return this; + } + + public PopTip show(Activity activity) { + super.beforeShow(); + if (getDialogView() == null) { + if (DialogX.onlyOnePopTip) { + PopTip oldInstance = null; + if (popTipList != null && !popTipList.isEmpty()) { + oldInstance = popTipList.get(popTipList.size() - 1); + } + if (oldInstance != null) { + oldInstance.dismiss(); + } + } else { + if (popTipList != null) { + CopyOnWriteArrayList copyPopTipList = new CopyOnWriteArrayList<>(popTipList); + for (int i = 0; i < copyPopTipList.size(); i++) { + PopTip popInstance = copyPopTipList.get(i); + if (copyPopTipList.size() < maxShowCount) { + popInstance.moveBack(); + } else { + if (i <= copyPopTipList.size() - maxShowCount) { + popInstance.dismiss(); + popTipList.remove(popInstance); + } else { + popInstance.moveBack(); + } + } + } + } + } + if (popTipList == null) popTipList = new ArrayList<>(); + popTipList.add(PopTip.this); + int layoutResId = isLightTheme() ? R.layout.layout_dialogx_poptip_material : R.layout.layout_dialogx_poptip_material_dark; + if (style.popTipSettings() != null) { + if (style.popTipSettings().layout(isLightTheme()) != 0) { + layoutResId = style.popTipSettings().layout(isLightTheme()); + } + + if (align == null) { + if (style.popTipSettings().align() == null) { + align = DialogXStyle.PopTipSettings.ALIGN.BOTTOM; + } else { + align = style.popTipSettings().align(); + } + } + int styleEnterAnimResId = style.popTipSettings().enterAnimResId(isLightTheme()); + int styleExitAnimResId = style.popTipSettings().exitAnimResId(isLightTheme()); + enterAnimResId = enterAnimResId == 0 ? ( + overrideEnterAnimRes == 0 ? (styleEnterAnimResId != 0 ? styleEnterAnimResId : R.anim.anim_dialogx_default_enter) : overrideEnterAnimRes + ) : enterAnimResId; + exitAnimResId = exitAnimResId == 0 ? ( + overrideExitAnimRes == 0 ? (styleExitAnimResId != 0 ? styleExitAnimResId : R.anim.anim_dialogx_default_exit) : overrideExitAnimRes + ) : exitAnimResId; + enterAnimDuration = enterAnimDuration == -1 ? ( + overrideEnterDuration + ) : enterAnimDuration; + exitAnimDuration = exitAnimDuration == -1 ? ( + overrideExitDuration + ) : exitAnimDuration; + } + View dialogView = createView(layoutResId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(activity, dialogView); + } else { + show(activity, getDialogView()); + } + return this; + } + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + protected Timer autoDismissTimer; + protected long autoDismissDelay; + + public PopTip autoDismiss(long delay) { + autoDismissDelay = delay; + if (autoDismissTimer != null) { + autoDismissTimer.cancel(); + } + if (delay < 0) return this; + autoDismissTimer = new Timer(); + autoDismissTimer.schedule(new TimerTask() { + @Override + public void run() { + dismiss(); + } + }, delay); + return this; + } + + public void resetAutoDismissTimer() { + autoDismiss(autoDismissDelay); + } + + public PopTip showShort() { + autoDismiss(2000); + if (!preShow && !isShow) { + show(); + } + return this; + } + + public PopTip showLong() { + autoDismiss(3500); + if (!preShow && !isShow) { + show(); + } + return this; + } + + public PopTip showAlways() { + return noAutoDismiss(); + } + + public PopTip noAutoDismiss() { + autoDismiss(TIME_NO_AUTO_DISMISS_DELAY); + return this; + } + + public class DialogImpl implements DialogConvertViewInterface { + + public DialogXBaseRelativeLayout boxRoot; + public LinearLayout boxBody; + public ImageView imgDialogxPopIcon; + public TextView txtDialogxPopText; + public RelativeLayout boxCustom; + public TextView txtDialogxButton; + + private List blurViews; + + public DialogImpl(View convertView) { + if (convertView == null) return; + setDialogView(convertView); + boxRoot = convertView.findViewById(R.id.box_root); + boxBody = convertView.findViewById(R.id.box_body); + imgDialogxPopIcon = convertView.findViewById(R.id.img_dialogx_pop_icon); + txtDialogxPopText = convertView.findViewById(R.id.txt_dialogx_pop_text); + boxCustom = convertView.findViewById(R.id.box_custom); + txtDialogxButton = convertView.findViewById(R.id.txt_dialogx_button); + + blurViews = findAllBlurView(convertView); + + init(); + dialogImpl = this; + refreshView(); + } + + @Override + public void init() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(getThisOrderIndex()); + } + + if (messageTextInfo == null) messageTextInfo = DialogX.popTextInfo; + if (buttonTextInfo == null) buttonTextInfo = DialogX.buttonTextInfo; + if (backgroundColor == null) backgroundColor = DialogX.backgroundColor; + + if (autoDismissTimer == null) { + showShort(); + } + + boxRoot.setParentDialog(me); + boxRoot.setAutoUnsafePlacePadding(true); + boxRoot.setOnLifecycleCallBack(new DialogXBaseRelativeLayout.OnLifecycleCallBack() { + @Override + public void onShow() { + isShow = true; + preShow = false; + setLifecycleState(Lifecycle.State.CREATED); + boxRoot.setAlpha(0f); + onDialogShow(); + getDialogLifecycleCallback().onShow(me); + PopTip.this.onShow(me); + } + + @Override + public void onDismiss() { + if (popTipList != null) { + popTipList.remove(PopTip.this); + if (popTipList.isEmpty()) { + popTipList = null; + } + } + isShow = false; + + if (autoDismissTimer != null) { + autoDismissTimer.cancel(); + } + getDialogLifecycleCallback().onDismiss(me); + PopTip.this.onDismiss(me); + setLifecycleState(Lifecycle.State.DESTROYED); + dialogImpl = null; + System.gc(); + } + }); + + applyPopTipAlign(); + + boxRoot.setOnSafeInsetsChangeListener(new OnSafeInsetsChangeListener() { + @Override + public void onChange(Rect unsafeRect) { + if (align == DialogXStyle.PopTipSettings.ALIGN.TOP_INSIDE) { + boxBody.setPadding(0, unsafeRect.top, 0, 0); + } + } + }); + + boxRoot.setOnBackPressedListener(new DialogXBaseRelativeLayout.PrivateBackPressedListener() { + @Override + public boolean onBackPressed() { + return false; + } + }); + + boxRoot.post(new Runnable() { + @Override + public void run() { + getDialogXAnimImpl().doShowAnim(me, boxBody); + setLifecycleState(Lifecycle.State.RESUMED); + } + }); + + txtDialogxButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + haptic(v); + if (onButtonClickListener != null) { + if (!onButtonClickListener.onClick(me, v)) { + doDismiss(v); + } + } else { + doDismiss(v); + } + } + }); + onDialogInit(); + } + + private void applyPopTipAlign() { + RelativeLayout.LayoutParams rlp; + rlp = ((RelativeLayout.LayoutParams) boxBody.getLayoutParams()); + if (align == null) align = DialogXStyle.PopTipSettings.ALIGN.BOTTOM; + switch (align) { + case TOP: + rlp.removeRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + rlp.removeRule(RelativeLayout.CENTER_IN_PARENT); + rlp.addRule(RelativeLayout.ALIGN_PARENT_TOP); + break; + case BOTTOM: + rlp.removeRule(RelativeLayout.ALIGN_PARENT_TOP); + rlp.removeRule(RelativeLayout.CENTER_IN_PARENT); + rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + break; + case CENTER: + rlp.removeRule(RelativeLayout.ALIGN_PARENT_TOP); + rlp.removeRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + rlp.addRule(RelativeLayout.CENTER_IN_PARENT); + break; + } + boxBody.setLayoutParams(rlp); + } + + @Override + public void refreshView() { + if (boxRoot == null || getOwnActivity() == null) { + return; + } + boxRoot.setRootPadding(screenPaddings[0], screenPaddings[1], screenPaddings[2], screenPaddings[3]); + if (backgroundColor != null) { + tintColor(boxBody, backgroundColor); + tintColor(txtDialogxButton, backgroundColor); + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setOverlayColor(backgroundColor); + } + } + } + + if (onBindView != null && onBindView.getCustomView() != null) { + onBindView.bindParent(boxCustom, me); + boxCustom.setVisibility(View.VISIBLE); + } else { + boxCustom.setVisibility(View.GONE); + } + + showText(txtDialogxPopText, message); + showText(txtDialogxButton, buttonText); + + useTextInfo(txtDialogxPopText, messageTextInfo); + useTextInfo(txtDialogxButton, buttonTextInfo); + + if (iconResId != 0) { + imgDialogxPopIcon.setVisibility(View.VISIBLE); + imgDialogxPopIcon.setImageResource(iconResId); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (isTintIcon()) { + imgDialogxPopIcon.setImageTintList(txtDialogxPopText.getTextColors()); + } else { + imgDialogxPopIcon.setImageTintList(null); + } + } + } else { + imgDialogxPopIcon.setVisibility(View.GONE); + } + + if (backgroundRadius > -1) { + if (boxBody.getBackground() instanceof GradientDrawable) { + GradientDrawable gradientDrawable = (GradientDrawable) boxBody.getBackground(); + if (gradientDrawable != null) + gradientDrawable.setCornerRadius(backgroundRadius); + } + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + boxBody.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), backgroundRadius); + } + }); + boxBody.setClipToOutline(true); + } + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setRadiusPx(backgroundRadius); + } + } + } + + if (onPopTipClickListener != null) { + boxBody.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (!onPopTipClickListener.onClick(me, v)) { + haptic(v); + dismiss(); + } + } + }); + } else { + boxBody.setOnClickListener(null); + boxBody.setClickable(false); + } + + RelativeLayout.LayoutParams rlp = ((RelativeLayout.LayoutParams) boxBody.getLayoutParams()); + if (bodyMargin[0] != -1) rlp.leftMargin = bodyMargin[0]; + if (bodyMargin[1] != -1) rlp.topMargin = bodyMargin[1]; + if (bodyMargin[2] != -1) rlp.rightMargin = bodyMargin[2]; + if (bodyMargin[3] != -1) rlp.bottomMargin = bodyMargin[3]; + boxBody.setLayoutParams(rlp); + onDialogRefreshUI(); + } + + @Override + public void doDismiss(final View v) { + if (PopTip.this.preDismiss(PopTip.this)) { + return; + } + if (v != null) v.setEnabled(false); + + if (!dismissAnimFlag && boxRoot != null) { + dismissAnimFlag = true; + boxRoot.post(new Runnable() { + @Override + public void run() { + getDialogXAnimImpl().doExitAnim(me, boxBody); + + preRecycle = true; + runOnMainDelay(new Runnable() { + @Override + public void run() { + waitForDismiss(); + } + }, getExitAnimationDuration(null)); + + if (popTipList != null) { + //使位于自己之前的PopTip moveDown + int index = popTipList.indexOf(me); + for (int i = 0; i < index; i++) { + PopTip popTip = popTipList.get(i); + popTip.moveFront(); + } + } + } + }); + } + } + + protected DialogXAnimInterface getDialogXAnimImpl() { + if (dialogXAnimImpl == null) { + dialogXAnimImpl = new DialogXAnimInterface() { + @Override + public void doShowAnim(PopTip dialog, ViewGroup dialogBodyView) { + Animation enterAnim = AnimationUtils.loadAnimation(getOwnActivity(), enterAnimResId == 0 ? R.anim.anim_dialogx_default_enter : enterAnimResId); + long enterAnimDuration = getEnterAnimationDuration(enterAnim); + enterAnim.setInterpolator(new DecelerateInterpolator(2f)); + enterAnim.setDuration(enterAnimDuration); + enterAnim.setFillAfter(true); + boxBody.startAnimation(enterAnim); + + boxRoot.animate() + .setDuration(enterAnimDuration) + .alpha(1f) + .setInterpolator(new DecelerateInterpolator()) + .setListener(null); + } + + @Override + public void doExitAnim(PopTip dialog, ViewGroup dialogBodyView) { + Animation exitAnim = AnimationUtils.loadAnimation(getOwnActivity() == null ? boxRoot.getContext() : getOwnActivity(), exitAnimResId == 0 ? R.anim.anim_dialogx_default_exit : exitAnimResId); + long exitAnimDuration = getExitAnimationDuration(exitAnim); + exitAnim.setDuration(exitAnimDuration); + exitAnim.setFillAfter(true); + boxBody.startAnimation(exitAnim); + + boxRoot.animate() + .alpha(0f) + .setInterpolator(new AccelerateInterpolator()) + .setDuration(exitAnimDuration); + } + }; + } + return dialogXAnimImpl; + } + + public long getExitAnimationDuration(@Nullable Animation defaultExitAnim) { + if (defaultExitAnim == null && boxBody.getAnimation() != null) { + defaultExitAnim = boxBody.getAnimation(); + } + long exitAnimDurationTemp = (defaultExitAnim == null || defaultExitAnim.getDuration() == 0) ? 300 : defaultExitAnim.getDuration(); + if (overrideExitDuration >= 0) { + exitAnimDurationTemp = overrideExitDuration; + } + if (exitAnimDuration != -1) { + exitAnimDurationTemp = exitAnimDuration; + } + return exitAnimDurationTemp; + } + + public long getEnterAnimationDuration(@Nullable Animation defaultEnterAnim) { + if (defaultEnterAnim == null && boxBody.getAnimation() != null) { + defaultEnterAnim = boxBody.getAnimation(); + } + long enterAnimDurationTemp = (defaultEnterAnim == null || defaultEnterAnim.getDuration() == 0) ? 300 : defaultEnterAnim.getDuration(); + if (overrideEnterDuration >= 0) { + enterAnimDurationTemp = overrideEnterDuration; + } + if (enterAnimDuration >= 0) { + enterAnimDurationTemp = enterAnimDuration; + } + return enterAnimDurationTemp; + } + } + + protected boolean preRecycle = false; + + /** + * 之所以这样处理,在较为频繁的启停 PopTip 时可能存在 PopTip 关闭动画位置错误无法计算的问题 + * 使用 preRecycle 标记记录是否需要回收,而不是立即销毁 + * 等待所有 PopTip 处于待回收状态时一并回收可以避免此问题 + */ + private void waitForDismiss() { + if (popTipList == null || popTipList.isEmpty()) { + return; + } + preRecycle = true; + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + CopyOnWriteArrayList copyPopTipList = new CopyOnWriteArrayList<>(popTipList); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + copyPopTipList.removeIf(Objects::isNull); + } else { + Iterator iterator = copyPopTipList.iterator(); + while (iterator.hasNext()) { + if (iterator.next() == null) { + iterator.remove(); + } + } + } + dismiss(getDialogView()); + } + + private void moveBack() { + if (getDialogImpl() != null && getDialogImpl().boxBody != null) { + if (getDialogImpl() == null || getDialogImpl().boxBody == null) return; + View bodyView = getDialogImpl().boxBody; + bodyView.post(new Runnable() { + @Override + public void run() { + if (getDialogImpl() == null) { + return; + } + if (align == null && style.popTipSettings() != null) { + align = style.popTipSettings().align(); + } + if (align == null) align = DialogXStyle.PopTipSettings.ALIGN.TOP; + float moveAimTop = 0; + switch (align) { + case TOP: + moveAimTop = bodyView.getY() + bodyView.getHeight() * 1.3f; + break; + case TOP_INSIDE: + moveAimTop = bodyView.getY() + bodyView.getHeight() - bodyView.getPaddingTop(); + break; + case CENTER: + case BOTTOM: + case BOTTOM_INSIDE: + moveAimTop = bodyView.getY() - bodyView.getHeight() * 1.3f; + break; + } + if (moveDisplacementInterceptor != null) { + moveAimTop = moveDisplacementInterceptor.resetAnimY(popTipList == null ? 0 : popTipList.indexOf(me), me, bodyView.getY(), moveAimTop, (int) (bodyView.getHeight() / bodyView.getScaleY()), popTipList == null ? 1 : popTipList.size(), true); + } + if (bodyView.getTag() instanceof ValueAnimator) { + ((ValueAnimator) bodyView.getTag()).end(); + } + //log("#Animation from:" + bodyView.getY() + " to:" + moveAimTop); + final float fromY = bodyView.getY(); + float toY = moveAimTop; + ValueAnimator valueAnimator = ValueAnimator.ofFloat(fromY, toY); + bodyView.setTag(valueAnimator); + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + if (getDialogImpl() == null || !isShow) { + animation.cancel(); + return; + } + View bodyView = getDialogImpl().boxBody; + float value = (Float) animation.getAnimatedValue(); + float totalDistance = toY - fromY; + if (moveDisplacementInterceptor != null && moveDisplacementInterceptor.animUpdater(popTipList == null ? 0 : popTipList.indexOf(me), me, bodyView, fromY, toY, Math.max(0f, Math.min(1f, (totalDistance == 0f ? 1f : (value - fromY) / totalDistance))), animation, popTipList == null ? 1 : countDisplayPopTipsNum(), true)) { + return; + } + if (bodyView != null && bodyView.isAttachedToWindow()) { + bodyView.setY(value); + } + } + }); + valueAnimator.setDuration(enterAnimDuration == -1 ? 300 : enterAnimDuration).setInterpolator(new DecelerateInterpolator(2f)); + valueAnimator.start(); + } + }); + } + } + + private void moveFront() { + if (getDialogImpl() != null && getDialogImpl().boxBody != null) { + if (getDialogImpl() == null || getDialogImpl().boxBody == null) return; + View bodyView = getDialogImpl().boxBody; + bodyView.post(new Runnable() { + @Override + public void run() { + if (getDialogImpl() == null) { + return; + } + if (align == null && style.popTipSettings() != null) { + align = style.popTipSettings().align(); + } + if (align == null) align = DialogXStyle.PopTipSettings.ALIGN.TOP; + float moveAimTop = 0; + switch (align) { + case TOP: + moveAimTop = bodyView.getY() - bodyView.getHeight() * 1.3f; + break; + case TOP_INSIDE: + moveAimTop = bodyView.getY() - bodyView.getHeight() + bodyView.getPaddingTop(); + break; + case CENTER: + case BOTTOM: + case BOTTOM_INSIDE: + moveAimTop = bodyView.getY() + bodyView.getHeight() * 1.3f; + break; + } + if (moveDisplacementInterceptor != null) { + moveAimTop = moveDisplacementInterceptor.resetAnimY(popTipList == null ? 0 : popTipList.indexOf(me), me, bodyView.getY(), moveAimTop, (int) (bodyView.getHeight() / bodyView.getScaleY()), popTipList == null ? 1 : popTipList.size(), false); + } + if (bodyView.getTag() instanceof ValueAnimator) { + ((ValueAnimator) bodyView.getTag()).end(); + } + //log("#Animation from:" + bodyView.getY() + " to:" + moveAimTop); + final float fromY = bodyView.getY(); + float toY = moveAimTop; + ValueAnimator valueAnimator = ValueAnimator.ofFloat(fromY, toY); + bodyView.setTag(valueAnimator); + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + if (getDialogImpl() == null || !isShow) { + animation.cancel(); + return; + } + View bodyView = getDialogImpl().boxBody; + float value = (Float) animation.getAnimatedValue(); + float totalDistance = toY - fromY; + if (moveDisplacementInterceptor != null && moveDisplacementInterceptor.animUpdater(popTipList == null ? 0 : popTipList.indexOf(me), me, bodyView, fromY, toY, Math.max(0f, Math.min(1f, (totalDistance == 0f ? 1f : (value - fromY) / totalDistance))), animation, popTipList == null ? 1 : countDisplayPopTipsNum(), false)) { + return; + } + if (bodyView != null && bodyView.isAttachedToWindow()) { + bodyView.setY(value); + } + } + }); + valueAnimator.setDuration(exitAnimDuration == -1 ? 300 : exitAnimDuration).setInterpolator(new AccelerateInterpolator(2f)); + valueAnimator.start(); + } + }); + } + } + + private int countDisplayPopTipsNum() { + if (popTipList == null) return 0; + int count = 0; + for (int i = 0; i < popTipList.size(); i++) { + PopTip tips = popTipList.get(i); + if (tips != null && !tips.preRecycle) { + count++; + } + } + return count; + } + + public void refreshUI() { + if (getDialogImpl() == null) return; + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl != null) dialogImpl.refreshView(); + } + }); + } + + public void dismiss() { + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl == null) return; + dialogImpl.doDismiss(null); + } + }); + } + + public DialogLifecycleCallback getDialogLifecycleCallback() { + return dialogLifecycleCallback == null ? new DialogLifecycleCallback() { + } : dialogLifecycleCallback; + } + + public PopTip setDialogLifecycleCallback(DialogLifecycleCallback dialogLifecycleCallback) { + this.dialogLifecycleCallback = dialogLifecycleCallback; + if (isShow) dialogLifecycleCallback.onShow(me); + return this; + } + + public PopTip setStyle(DialogXStyle style) { + this.style = style; + return this; + } + + public PopTip setTheme(DialogX.THEME theme) { + this.theme = theme; + return this; + } + + public PopTip.DialogImpl getDialogImpl() { + return dialogImpl; + } + + public PopTip setCustomView(OnBindView onBindView) { + this.onBindView = onBindView; + refreshUI(); + return this; + } + + public View getCustomView() { + if (onBindView == null) return null; + return onBindView.getCustomView(); + } + + public PopTip removeCustomView() { + this.onBindView.clean(); + refreshUI(); + return this; + } + + public DialogXStyle.PopTipSettings.ALIGN getAlign() { + return align; + } + + public PopTip setAlign(DialogXStyle.PopTipSettings.ALIGN align) { + this.align = align; + if (getDialogImpl() != null) { + getDialogImpl().applyPopTipAlign(); + } + return this; + } + + public int getIconResId() { + return iconResId; + } + + public PopTip setIconResId(int iconResId) { + this.iconResId = iconResId; + refreshUI(); + return this; + } + + public CharSequence getMessage() { + return message; + } + + public PopTip setMessage(CharSequence message) { + this.message = message; + refreshUI(); + return this; + } + + public PopTip setMessage(int messageResId) { + this.message = getString(messageResId); + refreshUI(); + return this; + } + + public CharSequence getButtonText() { + return buttonText; + } + + public PopTip setButton(CharSequence buttonText) { + this.buttonText = buttonText; + refreshUI(); + return this; + } + + public PopTip setButton(int buttonTextResId) { + this.buttonText = getString(buttonTextResId); + refreshUI(); + return this; + } + + public PopTip setButton(CharSequence buttonText, OnDialogButtonClickListener onButtonClickListener) { + this.buttonText = buttonText; + this.onButtonClickListener = onButtonClickListener; + refreshUI(); + return this; + } + + public PopTip setButton(int buttonTextResId, OnDialogButtonClickListener onButtonClickListener) { + this.buttonText = getString(buttonTextResId); + this.onButtonClickListener = onButtonClickListener; + refreshUI(); + return this; + } + + public PopTip setButton(OnDialogButtonClickListener onButtonClickListener) { + this.onButtonClickListener = onButtonClickListener; + return this; + } + + public TextInfo getMessageTextInfo() { + return messageTextInfo; + } + + public PopTip setMessageTextInfo(TextInfo messageTextInfo) { + this.messageTextInfo = messageTextInfo; + refreshUI(); + return this; + } + + public TextInfo getButtonTextInfo() { + return buttonTextInfo; + } + + public PopTip setButtonTextInfo(TextInfo buttonTextInfo) { + this.buttonTextInfo = buttonTextInfo; + refreshUI(); + return this; + } + + public OnDialogButtonClickListener getOnButtonClickListener() { + return onButtonClickListener; + } + + public PopTip setOnButtonClickListener(OnDialogButtonClickListener onButtonClickListener) { + this.onButtonClickListener = onButtonClickListener; + return this; + } + + @Deprecated + public boolean isAutoTintIconInLightOrDarkMode() { + return isTintIcon(); + } + + @Deprecated + public PopTip setAutoTintIconInLightOrDarkMode(boolean autoTintIconInLightOrDarkMode) { + setTintIcon(autoTintIconInLightOrDarkMode); + return this; + } + + public OnDialogButtonClickListener getOnPopTipClickListener() { + return onPopTipClickListener; + } + + public PopTip setOnPopTipClickListener(OnDialogButtonClickListener onPopTipClickListener) { + this.onPopTipClickListener = onPopTipClickListener; + refreshUI(); + return this; + } + + public int getBackgroundColor() { + return backgroundColor; + } + + public PopTip setBackgroundColor(@ColorInt int backgroundColor) { + this.backgroundColor = backgroundColor; + refreshUI(); + return this; + } + + public PopTip setBackgroundColorRes(@ColorRes int backgroundColorResId) { + this.backgroundColor = getColor(backgroundColorResId); + refreshUI(); + return this; + } + + public long getEnterAnimDuration() { + return enterAnimDuration; + } + + public PopTip setEnterAnimDuration(long enterAnimDuration) { + this.enterAnimDuration = enterAnimDuration; + return this; + } + + public long getExitAnimDuration() { + return exitAnimDuration; + } + + public PopTip setExitAnimDuration(long exitAnimDuration) { + this.exitAnimDuration = exitAnimDuration; + return this; + } + + @Override + public void restartDialog() { + if (getDialogView() != null) { + dismiss(getDialogView()); + isShow = false; + } + if (getDialogImpl().boxCustom != null) { + getDialogImpl().boxCustom.removeAllViews(); + } + + if (DialogX.onlyOnePopTip) { + PopTip oldInstance = null; + if (popTipList != null && !popTipList.isEmpty()) { + oldInstance = popTipList.get(popTipList.size() - 1); + } + if (oldInstance != null) { + oldInstance.dismiss(); + } + } else { + if (popTipList != null) { + CopyOnWriteArrayList copyPopTipList = new CopyOnWriteArrayList<>(popTipList); + for (int i = 0; i < copyPopTipList.size(); i++) { + PopTip popInstance = copyPopTipList.get(i); + if (copyPopTipList.size() < maxShowCount) { + popInstance.moveBack(); + } else { + if (i <= copyPopTipList.size() - maxShowCount) { + popInstance.dismiss(); + popTipList.remove(popInstance); + } else { + popInstance.moveBack(); + } + } + } + } + } + if (popTipList == null) popTipList = new ArrayList<>(); + popTipList.add(PopTip.this); + + int layoutResId = isLightTheme() ? R.layout.layout_dialogx_poptip_material : R.layout.layout_dialogx_poptip_material_dark; + if (style.popTipSettings() != null) { + if (style.popTipSettings().layout(isLightTheme()) != 0) { + layoutResId = style.popTipSettings().layout(isLightTheme()); + } + if (align == null) { + if (style.popTipSettings().align() == null) { + align = DialogXStyle.PopTipSettings.ALIGN.BOTTOM; + } else { + align = style.popTipSettings().align(); + } + } + int styleEnterAnimResId = style.popTipSettings().enterAnimResId(isLightTheme()); + int styleExitAnimResId = style.popTipSettings().exitAnimResId(isLightTheme()); + enterAnimResId = enterAnimResId == 0 ? ( + overrideEnterAnimRes == 0 ? (styleEnterAnimResId != 0 ? styleEnterAnimResId : R.anim.anim_dialogx_default_enter) : overrideEnterAnimRes + ) : enterAnimResId; + exitAnimResId = exitAnimResId == 0 ? ( + overrideExitAnimRes == 0 ? (styleExitAnimResId != 0 ? styleExitAnimResId : R.anim.anim_dialogx_default_exit) : overrideExitAnimRes + ) : exitAnimResId; + enterAnimDuration = enterAnimDuration == -1 ? ( + overrideEnterDuration + ) : enterAnimDuration; + exitAnimDuration = exitAnimDuration == -1 ? ( + overrideExitDuration + ) : exitAnimDuration; + } + enterAnimDuration = 0; + View dialogView = createView(layoutResId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + } + + public void hide() { + isHide = true; + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + } + + public PopTip setAnimResId(int enterResId, int exitResId) { + this.enterAnimResId = enterResId; + this.exitAnimResId = exitResId; + return this; + } + + public PopTip setEnterAnimResId(int enterResId) { + this.enterAnimResId = enterResId; + return this; + } + + public PopTip setExitAnimResId(int exitResId) { + this.exitAnimResId = exitResId; + return this; + } + + public PopTip setHapticFeedbackEnabled(boolean isHapticFeedbackEnabled) { + this.isHapticFeedbackEnabled = isHapticFeedbackEnabled ? 1 : 0; + return this; + } + + @Override + protected void shutdown() { + dismiss(); + } + + public PopTip setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public PopTip setMargin(int left, int top, int right, int bottom) { + bodyMargin[0] = left; + bodyMargin[1] = top; + bodyMargin[2] = right; + bodyMargin[3] = bottom; + refreshUI(); + return this; + } + + public PopTip setMarginLeft(int left) { + bodyMargin[0] = left; + refreshUI(); + return this; + } + + public PopTip setMarginTop(int top) { + bodyMargin[1] = top; + refreshUI(); + return this; + } + + public PopTip setMarginRight(int right) { + bodyMargin[2] = right; + refreshUI(); + return this; + } + + public PopTip setMarginBottom(int bottom) { + bodyMargin[3] = bottom; + refreshUI(); + return this; + } + + public int getMarginLeft() { + return bodyMargin[0]; + } + + public int getMarginTop() { + return bodyMargin[1]; + } + + public int getMarginRight() { + return bodyMargin[2]; + } + + public int getMarginBottom() { + return bodyMargin[3]; + } + + public PopTip iconSuccess() { + setTintIcon(false); + int resId = R.mipmap.ico_dialogx_success; + if (getStyle().popTipSettings() != null && getStyle().popTipSettings().defaultIconSuccess() != 0) { + resId = getStyle().popTipSettings().defaultIconSuccess(); + } + setIconResId(resId); + return this; + } + + public PopTip iconWarning() { + setTintIcon(false); + int resId = R.mipmap.ico_dialogx_warning; + if (getStyle().popTipSettings() != null && getStyle().popTipSettings().defaultIconWarning() != 0) { + resId = getStyle().popTipSettings().defaultIconWarning(); + } + setIconResId(resId); + return this; + } + + public PopTip iconError() { + setTintIcon(false); + int resId = R.mipmap.ico_dialogx_error; + if (getStyle().popTipSettings() != null && getStyle().popTipSettings().defaultIconError() != 0) { + resId = getStyle().popTipSettings().defaultIconError(); + } + setIconResId(resId); + return this; + } + + public boolean isTintIcon() { + if (tintIcon == null && getStyle().popTipSettings() != null) { + return getStyle().popTipSettings().tintIcon(); + } + return tintIcon == BOOLEAN.TRUE; + } + + public PopTip setTintIcon(boolean tintIcon) { + this.tintIcon = tintIcon ? BOOLEAN.TRUE : BOOLEAN.FALSE; + refreshUI(); + return this; + } + + public PopTip setRadius(float radiusPx) { + backgroundRadius = radiusPx; + refreshUI(); + return this; + } + + public float getRadius() { + return backgroundRadius; + } + + public DialogXAnimInterface getDialogXAnimImpl() { + return dialogXAnimImpl; + } + + public PopTip setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public PopTip setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public PopTip setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new PopTip() { + * + * @param dialog self + * @Override public void onShow(PopTip dialog) { + * //... + * } + * } + */ + protected void onShow(PopTip dialog) { + + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new PopTip() { + * + * @param dialog self + * @Override public boolean onDismiss(PopTip dialog) { + * WaitDialog.show("Please Wait..."); + * if (dialog.getButtonSelectResult() == BUTTON_SELECT_RESULT.BUTTON_OK) { + * //点击了OK的情况 + * //... + * } else { + * //其他按钮点击、对话框dismiss的情况 + * //... + * } + * return false; + * } + * } + */ + //用于使用 new 构建实例时,override 的生命周期事件 + protected void onDismiss(PopTip dialog) { + + } + + @Override + protected void cleanActivityContext() { + super.cleanActivityContext(); + dismiss(getDialogView()); + } + + public PopTip setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public PopTip onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public PopTip onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public PopTip appendMessage(CharSequence message) { + this.message = TextUtils.concat(this.message, message); + refreshUI(); + return this; + } + + public PopTip setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex(" + orderIndex + ") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public PopTip bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public PopTip setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public PopTip cleanAction(int actionId) { + dialogActionRunnableMap.remove(actionId); + return this; + } + + public PopTip cleanAllAction() { + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + public void callDialogDismiss() { + dismiss(); + } + + public PopTip bindDismissWithLifecycleOwner(LifecycleOwner owner) { + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/TipDialog.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/TipDialog.java new file mode 100644 index 0000000..e1e5f06 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/TipDialog.java @@ -0,0 +1,313 @@ +package com.kongzue.dialogx.dialogs; + +import android.app.Activity; +import android.os.Build; +import android.text.TextUtils; + +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.OnBackPressedListener; +import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener; + +import java.util.HashMap; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/28 23:53 + */ +public class TipDialog extends WaitDialog { + + /** + * 参数 duration 使用此值,或小于 0 的任意整数时, + * TipDialog 将不自动关闭 + */ + public static final int NO_AUTO_DISMISS = -1; + + protected TipDialog() { + super(); + } + + public static WaitDialog showTipWithDefaultText(TYPE tip) { + return WaitDialog.showTipWithDefaultText(tip); + } + + public static WaitDialog show(int messageResId) { + return show((Activity) null, messageResId); + } + + public static WaitDialog show(Activity activity, int messageResId) { + WaitDialog dialog = getInstance(activity); + if (dialog != null) { + dialog.setTip(messageResId, TYPE.WARNING); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(CharSequence message) { + return show(null, message); + } + + public static WaitDialog show(Activity activity, CharSequence message) { + WaitDialog dialog = getInstance(activity); + if (dialog != null) { + dialog.setTip(message, TYPE.WARNING); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(int messageResId, TYPE tip) { + return show(null, messageResId, tip); + } + + public static WaitDialog show(Activity activity, int messageResId, TYPE tip) { + WaitDialog dialog = getInstance(activity); + if (dialog != null) { + dialog.setTip(messageResId, tip); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(CharSequence message, TYPE tip) { + return show(null, message, tip); + } + + public static WaitDialog show(Activity activity, CharSequence message, TYPE tip) { + WaitDialog dialog = getInstance(activity); + if (dialog != null) { + dialog.setTip(message, tip); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(int messageResId, TYPE tip, long duration) { + return show(null, messageResId, tip, duration); + } + + public static WaitDialog show(Activity activity, int messageResId, TYPE tip, long duration) { + WaitDialog dialog = getInstance(activity); + if (dialog != null) { + dialog.setTip(messageResId, tip); + dialog.setTipShowDuration(duration); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(CharSequence message, TYPE tip, long duration) { + return show(null, message, tip, duration); + } + + public static WaitDialog show(Activity activity, CharSequence message, TYPE tip, long duration) { + WaitDialog dialog = getInstance(activity); + if (dialog != null) { + dialog.setTip(message, tip); + dialog.setTipShowDuration(duration); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + public TipDialog setMaxWidth(int maxWidth) { + this.maxWidth = maxWidth; + refreshUI(); + return this; + } + + public TipDialog setMaxHeight(int maxHeight) { + this.maxHeight = maxHeight; + refreshUI(); + return this; + } + + public TipDialog setMinHeight(int minHeight) { + this.minHeight = minHeight; + refreshUI(); + return this; + } + + public TipDialog setMinWidth(int minWidth) { + this.minWidth = minWidth; + refreshUI(); + return this; + } + + public TipDialog setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public boolean isBkgInterceptTouch() { + return bkgInterceptTouch; + } + + public TipDialog setBkgInterceptTouch(boolean bkgInterceptTouch) { + this.bkgInterceptTouch = bkgInterceptTouch; + return this; + } + + public OnBackgroundMaskClickListener getOnBackgroundMaskClickListener() { + return onBackgroundMaskClickListener; + } + + public TipDialog setOnBackgroundMaskClickListener(OnBackgroundMaskClickListener onBackgroundMaskClickListener) { + this.onBackgroundMaskClickListener = onBackgroundMaskClickListener; + return this; + } + + public TipDialog setRadius(float radiusPx) { + backgroundRadius = radiusPx; + refreshUI(); + return this; + } + + public float getRadius() { + return backgroundRadius; + } + + public TipDialog setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public TipDialog setOnBackPressedListener(OnBackPressedListener onBackPressedListener) { + this.onBackPressedListener = onBackPressedListener; + refreshUI(); + return this; + } + + public TipDialog setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public TipDialog setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + public TipDialog setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public TipDialog onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public TipDialog onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public TipDialog setEnableImmersiveMode(boolean enableImmersiveMode) { + this.enableImmersiveMode = enableImmersiveMode; + refreshUI(); + return this; + } + + public TipDialog appendMessage(CharSequence message) { + this.message = TextUtils.concat(this.message, message); + refreshUI(); + return this; + } + + public TipDialog setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex(" + orderIndex + ") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public TipDialog bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public TipDialog setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public TipDialog cleanAction(int actionId){ + dialogActionRunnableMap.remove(actionId); + return this; + } + + public TipDialog cleanAllAction(){ + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + public void callDialogDismiss(){ + dismiss(); + } + + public TipDialog bindDismissWithLifecycleOwner(LifecycleOwner owner){ + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/WaitDialog.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/WaitDialog.java new file mode 100644 index 0000000..791a6fe --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/WaitDialog.java @@ -0,0 +1,1347 @@ +package com.kongzue.dialogx.dialogs; + +import android.animation.ValueAnimator; +import android.app.Activity; +import android.content.Context; +import android.graphics.Outline; +import android.graphics.drawable.GradientDrawable; +import android.os.Build; +import android.text.TextUtils; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.view.animation.DecelerateInterpolator; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.annotation.Nullable; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.interfaces.BlurViewType; +import com.kongzue.dialogx.interfaces.DialogConvertViewInterface; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.OnBackPressedListener; +import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener; +import com.kongzue.dialogx.interfaces.OnBindView; +import com.kongzue.dialogx.interfaces.ProgressViewInterface; +import com.kongzue.dialogx.util.TextInfo; +import com.kongzue.dialogx.util.views.DialogXBaseRelativeLayout; +import com.kongzue.dialogx.util.views.MaxRelativeLayout; +import com.kongzue.dialogx.util.views.ProgressView; + +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/27 14:50 + */ +public class WaitDialog extends BaseDialog { + + public static int overrideEnterDuration = -1; + public static int overrideExitDuration = -1; + public static int overrideEnterAnimRes = 0; + public static int overrideExitAnimRes = 0; + public static BOOLEAN overrideCancelable; + protected boolean bkgInterceptTouch = true; + protected OnBindView onBindView; + protected int customEnterAnimResId; + protected int customExitAnimResId; + protected float backgroundRadius = DialogX.defaultWaitAndTipDialogBackgroundRadius; + protected DialogXAnimInterface dialogXAnimImpl; + protected OnBackPressedListener onBackPressedListener; + + public enum TYPE { + /** + * @deprecated NONE 和 PROGRESSING 不建议使用,禁止使用此方法。 + * 此类型等同于直接使用 WaitDialog,因此请勿使用 TipDialog 并指定使用 TYPE.NONE, + * 如有需要,请直接使用: WaitDialog.show(...) + *

+ * 要是用进度,请直接使用 WaitDialog.show(float) + */ + @Deprecated NONE, SUCCESS, WARNING, ERROR, @Deprecated PROGRESSING + } + + protected CharSequence message; + protected long tipShowDuration = 1500; + protected float waitProgress = -1; + protected int showType = -1; //-1:WaitDialog 状态标示符,其余为 TipDialog 状态标示 + protected TextInfo messageTextInfo; + protected Integer maskColor = null; + protected BOOLEAN privateCancelable; + + protected DialogLifecycleCallback dialogLifecycleCallback; + protected OnBackgroundMaskClickListener onBackgroundMaskClickListener; + + protected WaitDialog() { + super(); + ownActivity = new WeakReference<>(getTopActivity()); + cancelable = DialogX.cancelableTipDialog; + } + + protected static WaitDialog instanceBuild() { + return new WaitDialog(); + } + + public static WaitDialog build() { + return new WaitDialog(); + } + + public static WaitDialog showWaitWithDefaultText() { + WaitDialog dialog = getInstance(); + if (dialog != null) { + dialog.setTip(null, TYPE.NONE); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + protected static WaitDialog showTipWithDefaultText(TYPE tip) { + WaitDialog dialog = getInstance(); + if (dialog != null) { + dialog.setTip(null, tip); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(CharSequence message) { + WaitDialog dialog = getInstance(); + if (dialog != null) { + dialog.setTip(message, TYPE.NONE); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(Activity activity, CharSequence message) { + WaitDialog dialog = getInstance(activity); + if (dialog != null) { + dialog.setTip(message, TYPE.NONE); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(int messageResId) { + WaitDialog dialog = getInstance(); + if (dialog != null) { + dialog.setTip(messageResId, TYPE.NONE); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(Activity activity, int messageResId) { + WaitDialog dialog = getInstance(activity); + if (dialog != null) { + dialog.setTip(messageResId, TYPE.NONE); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(CharSequence message, float progress) { + WaitDialog dialog = getInstance(); + if (dialog != null) { + dialog.setTip(message, TYPE.PROGRESSING); + dialog.setProgress(progress); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(Activity activity, CharSequence message, float progress) { + WaitDialog dialog = getInstance(activity); + if (dialog != null) { + dialog.setTip(message, TYPE.PROGRESSING); + dialog.setProgress(progress); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(int messageResId, float progress) { + WaitDialog dialog = getInstance(); + if (dialog != null) { + dialog.setTip(messageResId, TYPE.PROGRESSING); + dialog.setProgress(progress); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(Activity activity, int messageResId, float progress) { + WaitDialog dialog = getInstance(activity); + if (dialog != null) { + dialog.setTip(messageResId, TYPE.PROGRESSING); + dialog.setProgress(progress); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(Activity activity, float progress) { + WaitDialog dialog = getInstance(activity); + if (dialog != null) { + dialog.setTip(TYPE.PROGRESSING); + dialog.setProgress(progress); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(float progress) { + WaitDialog dialog = getInstance(); + if (dialog != null) { + dialog.setTip(TYPE.PROGRESSING); + dialog.setProgress(progress); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public float getProgress() { + return waitProgress; + } + + public WaitDialog setProgress(float waitProgress) { + this.waitProgress = waitProgress; + refreshUI(); + return this; + } + + private WeakReference dialogView; + + protected View getWaitDialogView() { + if (dialogView == null) return null; + return dialogView.get(); + } + + protected void setWaitDialogView(View v) { + dialogView = new WeakReference<>(v); + setDialogView(v); + } + + public WaitDialog show() { + super.beforeShow(); + runOnMain(new Runnable() { + @Override + public void run() { + int layoutResId = R.layout.layout_dialogx_wait; + if (style.overrideWaitTipRes() != null && style.overrideWaitTipRes().overrideWaitLayout(isLightTheme()) != 0) { + layoutResId = style.overrideWaitTipRes().overrideWaitLayout(isLightTheme()); + } + dialogImpl = new WeakReference<>(new DialogImpl(layoutResId)); + if (getDialogImpl() != null) { + getDialogImpl().lazyCreate(); + if (getWaitDialogView() != null) { + getWaitDialogView().setTag(WaitDialog.this); + show(getWaitDialogView()); + } + } + } + }); + return this; + } + + public WaitDialog show(final Activity activity) { + super.beforeShow(); + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + int layoutResId = R.layout.layout_dialogx_wait; + if (style.overrideWaitTipRes() != null && style.overrideWaitTipRes().overrideWaitLayout(isLightTheme()) != 0) { + layoutResId = style.overrideWaitTipRes().overrideWaitLayout(isLightTheme()); + } + dialogImpl = new WeakReference<>(new DialogImpl(layoutResId)); + if (getDialogImpl() != null) { + getDialogImpl().lazyCreate(); + if (getWaitDialogView() != null) { + getWaitDialogView().setTag(WaitDialog.this); + show(activity, getWaitDialogView()); + } + } + } + }); + return this; + } + + protected WeakReference dialogImpl; + + public class DialogImpl implements DialogConvertViewInterface { + + private List blurViews; + + public DialogXBaseRelativeLayout boxRoot; + public MaxRelativeLayout bkg; + public RelativeLayout boxProgress; + public ProgressViewInterface progressView; + public RelativeLayout boxCustomView; + public TextView txtInfo; + + private int layoutResId; + + public DialogImpl(int layoutResId) { + this.layoutResId = layoutResId; + } + + public void lazyCreate() { + View dialogView = createView(layoutResId); + if (dialogView == null) return; + setWaitDialogView(dialogView); + boxRoot = dialogView.findViewById(R.id.box_root); + bkg = dialogView.findViewById(R.id.bkg); + boxProgress = dialogView.findViewById(R.id.box_progress); + View progressViewCache = (View) style.overrideWaitTipRes().overrideWaitView(getOwnActivity(), isLightTheme()); + if (progressViewCache == null) { + progressViewCache = new ProgressView(getOwnActivity()); + } + progressView = (ProgressViewInterface) progressViewCache; + boxProgress.addView(progressViewCache, new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + boxCustomView = dialogView.findViewById(R.id.box_customView); + txtInfo = dialogView.findViewById(R.id.txt_info); + + blurViews = findAllBlurView(dialogView); + + init(); + setDialogImpl(this); + refreshView(); + } + + public DialogImpl(View convertView) { + if (convertView == null) return; + setDialogView(convertView); + boxRoot = convertView.findViewById(R.id.box_root); + bkg = convertView.findViewById(R.id.bkg); + boxProgress = convertView.findViewById(R.id.box_progress); + View progressViewCache = (View) style.overrideWaitTipRes().overrideWaitView(getOwnActivity(), isLightTheme()); + if (progressViewCache == null) { + progressViewCache = new ProgressView(getOwnActivity()); + } + progressView = (ProgressViewInterface) progressViewCache; + boxProgress.addView(progressViewCache, new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + boxCustomView = convertView.findViewById(R.id.box_customView); + txtInfo = convertView.findViewById(R.id.txt_info); + init(); + setDialogImpl(this); + refreshView(); + } + + public void init() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && getDialogView() != null) { + getDialogView().setTranslationZ(getThisOrderIndex()); + } + + if (messageTextInfo == null) messageTextInfo = DialogX.tipTextInfo; + if (backgroundColor == null) backgroundColor = DialogX.tipBackgroundColor; + + blurViews = findAllBlurView(dialogView.get()); + + Integer blurFrontColor = getColor(isLightTheme() ? R.color.dialogxWaitBkgDark : R.color.dialogxWaitBkgLight); + Float dialogXRadius = (float) dip2px(15); + if (style.overrideWaitTipRes() != null) { + dialogXRadius = getFloatStyleAttr((float) style.overrideWaitTipRes().overrideRadiusPx(), dialogXRadius); + blurFrontColor = getColorNullable(getIntStyleAttr(style.overrideWaitTipRes().overrideBackgroundColorRes(isLightTheme()), isLightTheme() ? R.color.dialogxWaitBkgDark : R.color.dialogxWaitBkgLight), blurFrontColor); + } + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setOverlayColor(backgroundColor == null ? blurFrontColor : backgroundColor); + ((BlurViewType) blurView).setRadiusPx(dialogXRadius); + } + } else { + GradientDrawable gradientDrawable = (GradientDrawable) getResources().getDrawable(R.drawable.rect_dialogx_material_wait_bkg); + gradientDrawable.setColor(blurFrontColor); + gradientDrawable.setCornerRadius(dialogXRadius); + bkg.setBackground(gradientDrawable); + } + boxRoot.setClickable(true); + + boxRoot.setParentDialog(WaitDialog.this); + boxRoot.setOnLifecycleCallBack(new DialogXBaseRelativeLayout.OnLifecycleCallBack() { + @Override + public void onShow() { + isShow = true; + preShow = false; + setLifecycleState(Lifecycle.State.CREATED); + boxRoot.setAlpha(0f); + bkg.post(new Runnable() { + @Override + public void run() { + if (getOwnActivity() == null) return; + + getDialogXAnimImpl().doShowAnim(WaitDialog.this, bkg); + + onDialogShow(); + getDialogLifecycleCallback().onShow(WaitDialog.this); + setLifecycleState(Lifecycle.State.RESUMED); + } + }); + } + + @Override + public void onDismiss() { + cleanInstance(); + } + }); + + if (readyTipType != null && readyTipType != TYPE.NONE) { + progressView.noLoading(); + ((View) progressView).postDelayed(new Runnable() { + @Override + public void run() { + showTip(readyTipType); + } + }, 100); + } + + boxRoot.setOnBackPressedListener(new DialogXBaseRelativeLayout.PrivateBackPressedListener() { + @Override + public boolean onBackPressed() { + if (onBackPressedListener != null) { + if (onBackPressedListener.onBackPressed(WaitDialog.this)) { + dismiss(); + } + } else { + if (isCancelable()) { + dismiss(); + } + } + return true; + } + }); + onDialogInit(); + } + + private float oldProgress = -1; + + public void refreshView() { + if (boxRoot == null || getOwnActivity() == null) { + return; + } + boxRoot.setAutoUnsafePlacePadding(isEnableImmersiveMode()); + boxRoot.setRootPadding(screenPaddings[0], screenPaddings[1], screenPaddings[2], screenPaddings[3]); + + bkg.setMaxWidth(getMaxWidth()); + bkg.setMaxHeight(getMaxHeight()); + bkg.setMinWidth(getMinWidth()); + bkg.setMinHeight(getMinHeight()); + + if (backgroundColor != null) { + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setOverlayColor(backgroundColor); + } + } else { + GradientDrawable gradientDrawable = (GradientDrawable) getResources().getDrawable(R.drawable.rect_dialogx_material_wait_bkg); + gradientDrawable.setColor(getBackgroundColor()); + gradientDrawable.setCornerRadius(getRadius()); + bkg.setBackground(gradientDrawable); + } + } + if (style.overrideWaitTipRes() != null) { + int overrideTextColorRes = getIntStyleAttr(style.overrideWaitTipRes().overrideTextColorRes(isLightTheme()), isLightTheme() ? R.color.white : R.color.black); + txtInfo.setTextColor(getResources().getColor(overrideTextColorRes)); + progressView.setColor(getResources().getColor(overrideTextColorRes)); + } else { + int overrideTextColorRes = isLightTheme() ? R.color.white : R.color.black; + txtInfo.setTextColor(getResources().getColor(overrideTextColorRes)); + progressView.setColor(getResources().getColor(overrideTextColorRes)); + } + if (DialogX.tipProgressColor != null) progressView.setColor(DialogX.tipProgressColor); + + if (waitProgress >= 0 && waitProgress <= 1 && oldProgress != waitProgress) { + progressView.progress(waitProgress); + oldProgress = waitProgress; + } + if (backgroundRadius > -1) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + bkg.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), backgroundRadius); + } + }); + bkg.setClipToOutline(true); + } + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setRadiusPx(backgroundRadius); + } + } + } + + showText(txtInfo, message == null ? getDefaultTipText(readyTipType) : message); + useTextInfo(txtInfo, messageTextInfo); + + if (maskColor != null) { + boxRoot.setBackgroundColor(maskColor); + } + + if (onBindView != null && onBindView.getCustomView() != null) { + onBindView.bindParent(boxCustomView, WaitDialog.this); + boxCustomView.setVisibility(View.VISIBLE); + boxProgress.setVisibility(View.GONE); + } else { + boxCustomView.setVisibility(View.GONE); + boxProgress.setVisibility(View.VISIBLE); + } + + if (bkgInterceptTouch) { + if (isCancelable()) { + boxRoot.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onBackgroundMaskClickListener == null || !onBackgroundMaskClickListener.onClick(WaitDialog.this, v)) { + doDismiss(v); + } + } + }); + } else { + boxRoot.setOnClickListener(null); + } + } else { + boxRoot.setClickable(false); + } + onDialogRefreshUI(); + } + + private CharSequence getDefaultTipText(TYPE readyTipType) { + switch (readyTipType) { + case WARNING: + return DialogX.defaultTipDialogWarningText; + case SUCCESS: + return DialogX.defaultTipDialogSuccessText; + case ERROR: + return DialogX.defaultTipDialogErrorText; + case NONE: + return DialogX.defaultWaitDialogWaitingText; + default: + return null; + } + } + + public void doDismiss(final View v) { + if (WaitDialog.this.preDismiss(WaitDialog.this)) { + return; + } + if (boxRoot == null) return; + if (getOwnActivity() == null) return; + + if (!dismissAnimFlag && boxRoot != null) { + dismissAnimFlag = true; + boxRoot.post(new Runnable() { + @Override + public void run() { + if (v != null) v.setEnabled(false); + getDialogXAnimImpl().doExitAnim(WaitDialog.this, bkg); + + runOnMainDelay(new Runnable() { + @Override + public void run() { + if (boxRoot != null) { + boxRoot.setVisibility(View.GONE); + } + dismiss(getWaitDialogView()); + } + }, getExitAnimationDuration(null)); + } + }); + } + } + + protected DialogXAnimInterface getDialogXAnimImpl() { + if (dialogXAnimImpl == null) { + dialogXAnimImpl = new DialogXAnimInterface() { + @Override + public void doShowAnim(WaitDialog dialog, ViewGroup dialogBodyView) { + int enterAnimResId = R.anim.anim_dialogx_default_enter; + if (overrideEnterAnimRes != 0) { + enterAnimResId = overrideEnterAnimRes; + } + if (customEnterAnimResId != 0) { + enterAnimResId = customEnterAnimResId; + } + Animation enterAnim = AnimationUtils.loadAnimation(getOwnActivity(), enterAnimResId); + long enterAnimDurationTemp = getEnterAnimationDuration(enterAnim); + enterAnim.setInterpolator(new DecelerateInterpolator()); + enterAnim.setDuration(enterAnimDurationTemp); + bkg.startAnimation(enterAnim); + + ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f); + bkgAlpha.setDuration(enterAnimDurationTemp); + bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + boxRoot.setBkgAlpha((Float) animation.getAnimatedValue()); + } + }); + bkgAlpha.start(); + + boxRoot.animate().setDuration(enterAnimDurationTemp).alpha(1f).setInterpolator(new DecelerateInterpolator()).setListener(null); + } + + @Override + public void doExitAnim(WaitDialog dialog, ViewGroup dialogBodyView) { + Context context = getOwnActivity(); + if (context == null) context = boxRoot.getContext(); + if (context == null) return; + + int exitAnimResId = R.anim.anim_dialogx_default_exit; + if (overrideExitAnimRes != 0) { + exitAnimResId = overrideExitAnimRes; + } + if (customExitAnimResId != 0) { + exitAnimResId = customExitAnimResId; + } + Animation exitAnim = AnimationUtils.loadAnimation(context, exitAnimResId); + long exitAnimDurationTemp = getExitAnimationDuration(exitAnim); + exitAnim.setDuration(exitAnimDurationTemp); + exitAnim.setInterpolator(new AccelerateInterpolator()); + bkg.startAnimation(exitAnim); + + boxRoot.animate().alpha(0f).setInterpolator(new AccelerateInterpolator()).setDuration(exitAnimDurationTemp); + + ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1f, 0f); + bkgAlpha.setDuration(exitAnimDurationTemp); + bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + if (boxRoot != null) { + boxRoot.setBkgAlpha((Float) animation.getAnimatedValue()); + } + } + }); + bkgAlpha.start(); + } + }; + } + return dialogXAnimImpl; + } + + public long getExitAnimationDuration(@Nullable Animation defaultExitAnim) { + if (defaultExitAnim == null && bkg.getAnimation() != null) { + defaultExitAnim = bkg.getAnimation(); + } + long exitAnimDurationTemp = (defaultExitAnim == null || defaultExitAnim.getDuration() == 0) ? 300 : defaultExitAnim.getDuration(); + if (overrideExitDuration >= 0) { + exitAnimDurationTemp = overrideExitDuration; + } + if (exitAnimDuration != -1) { + exitAnimDurationTemp = exitAnimDuration; + } + return exitAnimDurationTemp; + } + + public long getEnterAnimationDuration(@Nullable Animation defaultEnterAnim) { + if (defaultEnterAnim == null && bkg.getAnimation() != null) { + defaultEnterAnim = bkg.getAnimation(); + } + long enterAnimDurationTemp = (defaultEnterAnim == null || defaultEnterAnim.getDuration() == 0) ? 300 : defaultEnterAnim.getDuration(); + if (overrideEnterDuration >= 0) { + enterAnimDurationTemp = overrideEnterDuration; + } + if (enterAnimDuration >= 0) { + enterAnimDurationTemp = enterAnimDuration; + } + return enterAnimDurationTemp; + } + + public void showTip(final TYPE tip) { + runOnMain(new Runnable() { + @Override + public void run() { + showType = tip.ordinal(); + if (progressView == null) return; + switch (tip) { + case NONE: + progressView.loading(); + return; + case SUCCESS: + progressView.success(); + break; + case WARNING: + progressView.warning(); + break; + case ERROR: + progressView.error(); + break; + } + + if (boxProgress != null && boxProgress.getVisibility() == View.VISIBLE) { + //此事件是在完成衔接动画绘制后执行的逻辑 + progressView.whenShowTick(new Runnable() { + @Override + public void run() { + getDialogLifecycleCallback().onShow(WaitDialog.this); + refreshView(); + if (tipShowDuration > 0) { + ((View) progressView).postDelayed(new Runnable() { + @Override + public void run() { + if (showType > -1) { + doDismiss(null); + } + } + }, tipShowDuration); + } + } + }); + } else { + getDialogLifecycleCallback().onShow(WaitDialog.this); + refreshView(); + if (tipShowDuration > 0) { + runOnMainDelay(new Runnable() { + @Override + public void run() { + if (showType > -1) { + doDismiss(null); + } + } + }, tipShowDuration); + } + } + } + }); + } + } + + public void cleanInstance() { + isShow = false; + getDialogLifecycleCallback().onDismiss(WaitDialog.this); + setLifecycleState(Lifecycle.State.DESTROYED); + if (dialogImpl != null) dialogImpl.clear(); + dialogImpl = null; + if (dialogView != null) dialogView.clear(); + dialogView = null; + dialogLifecycleCallback = null; + if (ownActivity != null) ownActivity.clear(); + System.gc(); + } + + private void setDialogImpl(DialogImpl d) { + if (dialogImpl != null && dialogImpl.get() != d) dialogImpl = new WeakReference<>(d); + } + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + @Override + public boolean isLightTheme() { + if (DialogX.tipTheme == null) { + return super.isLightTheme(); + } else { + return DialogX.tipTheme == DialogX.THEME.LIGHT; + } + } + + public void refreshUI() { + if (getDialogImpl() == null) return; + runOnMain(new Runnable() { + @Override + public void run() { + if (getDialogImpl() != null) getDialogImpl().refreshView(); + } + }); + } + + public void doDismiss() { + isShow = false; + runOnMain(new Runnable() { + @Override + public void run() { + if (getDialogImpl() != null) { + getDialogImpl().doDismiss(null); + } + } + }); + } + + public static void dismiss() { + WaitDialog instance = getInstance(); + if (instance != null) { + instance.doDismiss(); + } + } + + public static void dismiss(Activity activity) { + WaitDialog instance = getInstance(activity); + if (instance != null) instance.doDismiss(); + } + + protected Timer delayDismissTimer; + + protected void cancelDelayDismissTimer() { + if (delayDismissTimer != null) { + delayDismissTimer.cancel(); + } + } + + public static void dismiss(long delayTime) { + WaitDialog instance = getInstance(); + if (instance != null) { + instance.doDismiss(delayTime); + } + } + + public void doDismiss(long delayTime) { + cancelDelayDismissTimer(); + delayDismissTimer = new Timer(); + delayDismissTimer.schedule(new TimerTask() { + @Override + public void run() { + doDismiss(); + } + }, delayTime); + } + + protected static WaitDialog me() { + WaitDialog instance = getInstance(); + return instance == null ? instanceBuild() : instance; + } + + protected TYPE readyTipType; + + protected void showTip(CharSequence message, TYPE type) { + showType = type.ordinal(); + this.message = message; + readyTipType = type; + show(); + } + + protected void showTip(Activity activity, CharSequence message, TYPE type) { + showType = type.ordinal(); + this.message = message; + readyTipType = type; + show(activity); + } + + protected void showTip(int messageResId, TYPE type) { + showType = type.ordinal(); + this.message = getString(messageResId); + readyTipType = type; + show(); + } + + protected void showTip(Activity activity, int messageResId, TYPE type) { + showType = type.ordinal(); + this.message = getString(messageResId); + readyTipType = type; + show(activity); + } + + protected void showTip(TYPE type) { + if (readyTipType == type) { + return; + } + showType = type.ordinal(); + readyTipType = type; + if (getDialogImpl() != null) { + getDialogImpl().showTip(type); + } + } + + protected void setTip(TYPE type) { + showTip(type); + } + + protected void setTip(CharSequence message, TYPE type) { + this.message = message; + showTip(type); + refreshUI(); + } + + protected void setTip(int messageResId, TYPE type) { + this.message = getString(messageResId); + showTip(type); + refreshUI(); + } + + protected void setTipShowDuration(long tipShowDuration) { + this.tipShowDuration = tipShowDuration; + showTip(readyTipType); + } + + public static CharSequence getMessage() { + return me().message; + } + + public static WaitDialog setMessage(CharSequence message) { + WaitDialog instance = getInstance(); + if (instance != null) { + instance.preMessage(message); + instance.refreshUI(); + return instance; + } + return instanceBuild(); + } + + public static WaitDialog setMessage(int messageResId) { + WaitDialog instance = getInstance(); + if (instance != null) { + instance.preMessage(messageResId); + instance.refreshUI(); + return instance; + } + return instanceBuild(); + } + + public boolean isCancelable() { + if (privateCancelable != null) { + return privateCancelable == BOOLEAN.TRUE; + } + if (overrideCancelable != null) { + return overrideCancelable == BOOLEAN.TRUE; + } + return DialogX.cancelableTipDialog; + } + + public WaitDialog setCancelable(boolean cancelable) { + privateCancelable = cancelable ? BOOLEAN.TRUE : BOOLEAN.FALSE; + refreshUI(); + return this; + } + + /** + * 用于从 WaitDialog 到 TipDialog 的消息设置 + * 此方法不会立即执行,而是等到动画衔接完成后由事件设置 + * + * @param message 消息 + * @return me + */ + protected WaitDialog preMessage(CharSequence message) { + this.message = message; + return this; + } + + protected WaitDialog preMessage(int messageResId) { + this.message = getString(messageResId); + return this; + } + + public DialogLifecycleCallback getDialogLifecycleCallback() { + return dialogLifecycleCallback == null ? new DialogLifecycleCallback() { + } : dialogLifecycleCallback; + } + + public WaitDialog setDialogLifecycleCallback(DialogLifecycleCallback dialogLifecycleCallback) { + this.dialogLifecycleCallback = dialogLifecycleCallback; + if (isShow) dialogLifecycleCallback.onShow(me()); + return this; + } + + public DialogImpl getDialogImpl() { + if (dialogImpl == null) return null; + return dialogImpl.get(); + } + + public WaitDialog setCustomView(OnBindView onBindView) { + this.onBindView = onBindView; + refreshUI(); + return this; + } + + public View getCustomView() { + if (onBindView == null) return null; + return onBindView.getCustomView(); + } + + public WaitDialog removeCustomView() { + this.onBindView.clean(); + refreshUI(); + return this; + } + + public OnBackPressedListener getOnBackPressedListener() { + return (OnBackPressedListener) onBackPressedListener; + } + + public WaitDialog setOnBackPressedListener(OnBackPressedListener onBackPressedListener) { + this.onBackPressedListener = onBackPressedListener; + refreshUI(); + return this; + } + + public int getBackgroundColor() { + return backgroundColor; + } + + public WaitDialog setBackgroundColor(@ColorInt int backgroundColor) { + this.backgroundColor = backgroundColor; + refreshUI(); + return this; + } + + public WaitDialog setBackgroundColorRes(@ColorRes int backgroundColorResId) { + this.backgroundColor = getColor(backgroundColorResId); + refreshUI(); + return this; + } + + public WaitDialog setMaskColor(@ColorInt int maskColor) { + this.maskColor = maskColor; + refreshUI(); + return this; + } + + public WaitDialog setEnterAnimDuration(long enterAnimDuration) { + this.enterAnimDuration = enterAnimDuration; + return this; + } + + public long getExitAnimDuration() { + return exitAnimDuration; + } + + public WaitDialog setExitAnimDuration(long exitAnimDuration) { + this.exitAnimDuration = exitAnimDuration; + return this; + } + + @Override + public void restartDialog() { + refreshUI(); + } + + /** + * 获取当前 WaitDialog 显示状态 + *

+ * 值的含义: + * -1: WaitDialog 等待状态 + * 0: TipDialog 无状态(与 WaitDialog 等待状态相同) + * 1: TipDialog 完成状态 + * 2: TipDialog 警告状态 + * 3: TipDialog 错误状态 + * + * @return showType + */ + public static int getType() { + return me().showType; + } + + public WaitDialog setAnimResId(int enterResId, int exitResId) { + customEnterAnimResId = enterResId; + customExitAnimResId = exitResId; + return this; + } + + public WaitDialog setEnterAnimResId(int enterResId) { + customEnterAnimResId = enterResId; + return this; + } + + public WaitDialog setExitAnimResId(int exitResId) { + customExitAnimResId = exitResId; + return this; + } + + public static WaitDialog getInstance() { + return getInstance(null); + } + + public static WaitDialog getInstance(Activity activity) { + if (activity == null) { + activity = getTopActivity(); + } + if (isActivityDestroyed(activity)) { + return null; + } + for (BaseDialog baseDialog : getRunningDialogList()) { + if (baseDialog instanceof WaitDialog) { + if (baseDialog.isShow() && baseDialog.getOwnActivity() == activity) { + return (WaitDialog) baseDialog; + } + } + } + return new WaitDialog(); + } + + @Override + protected void shutdown() { + dismiss(); + } + + public WaitDialog setMaxWidth(int maxWidth) { + this.maxWidth = maxWidth; + refreshUI(); + return this; + } + + public WaitDialog setMaxHeight(int maxHeight) { + this.maxHeight = maxHeight; + refreshUI(); + return this; + } + + public WaitDialog setMinHeight(int minHeight) { + this.minHeight = minHeight; + refreshUI(); + return this; + } + + public WaitDialog setMinWidth(int minWidth) { + this.minWidth = minWidth; + refreshUI(); + return this; + } + + public WaitDialog setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public boolean isBkgInterceptTouch() { + return bkgInterceptTouch; + } + + public WaitDialog setBkgInterceptTouch(boolean bkgInterceptTouch) { + this.bkgInterceptTouch = bkgInterceptTouch; + return this; + } + + public OnBackgroundMaskClickListener getOnBackgroundMaskClickListener() { + return onBackgroundMaskClickListener; + } + + public WaitDialog setOnBackgroundMaskClickListener(OnBackgroundMaskClickListener onBackgroundMaskClickListener) { + this.onBackgroundMaskClickListener = onBackgroundMaskClickListener; + return this; + } + + public WaitDialog setStyle(DialogXStyle style) { + this.style = style; + return this; + } + + public WaitDialog setTheme(DialogX.THEME theme) { + this.theme = theme; + return this; + } + + public TextInfo getMessageTextInfo() { + return messageTextInfo; + } + + public WaitDialog setMessageTextInfo(TextInfo messageTextInfo) { + this.messageTextInfo = messageTextInfo; + refreshUI(); + return this; + } + + public WaitDialog setMessageContent(CharSequence message) { + this.message = message; + refreshUI(); + return this; + } + + public WaitDialog setMessageContent(int messageResId) { + this.message = getString(messageResId); + refreshUI(); + return this; + } + + public CharSequence getMessageContent() { + return message; + } + + public WaitDialog setTipType(TYPE type) { + showTip(type); + return this; + } + + public WaitDialog setRadius(float radiusPx) { + backgroundRadius = radiusPx; + refreshUI(); + return this; + } + + public float getRadius() { + return backgroundRadius < 0 ? dip2px(15) : backgroundRadius; + } + + public DialogXAnimInterface getDialogXAnimImpl() { + return dialogXAnimImpl; + } + + public WaitDialog setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public WaitDialog setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public WaitDialog setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + public WaitDialog onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public WaitDialog onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public WaitDialog setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public WaitDialog setEnableImmersiveMode(boolean enableImmersiveMode) { + this.enableImmersiveMode = enableImmersiveMode; + refreshUI(); + return this; + } + + public WaitDialog appendMessage(CharSequence message) { + this.message = TextUtils.concat(this.message, message); + refreshUI(); + return this; + } + + public WaitDialog setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex(" + orderIndex + ") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public WaitDialog bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public WaitDialog setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public WaitDialog cleanAction(int actionId){ + dialogActionRunnableMap.remove(actionId); + return this; + } + + public WaitDialog cleanAllAction(){ + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + public void callDialogDismiss(){ + dismiss(); + } + + public WaitDialog bindDismissWithLifecycleOwner(LifecycleOwner owner){ + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/impl/ActivityLifecycleImpl.java b/DialogX/src/main/java/com/kongzue/dialogx/impl/ActivityLifecycleImpl.java new file mode 100644 index 0000000..17a08fd --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/impl/ActivityLifecycleImpl.java @@ -0,0 +1,229 @@ +package com.kongzue.dialogx.impl; + +import android.app.Activity; +import android.app.Application; +import android.content.Context; +import android.os.Build; +import android.os.Bundle; +import android.util.ArrayMap; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.util.DialogXFloatingWindowActivity; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import static com.kongzue.dialogx.DialogX.error; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/22 11:31 + */ +public class ActivityLifecycleImpl implements Application.ActivityLifecycleCallbacks { + + private onActivityResumeCallBack onActivityResumeCallBack; + private static ActivityLifecycleImpl activityLifecycle; + private static Application application; + + public ActivityLifecycleImpl(ActivityLifecycleImpl.onActivityResumeCallBack onActivityResumeCallBack) { + this.onActivityResumeCallBack = onActivityResumeCallBack; + } + + public static void init(Context context, ActivityLifecycleImpl.onActivityResumeCallBack onActivityResumeCallBack) { + if (context != null) { + Application application = getApplicationContext(context); + if (application == null) { + error("DialogX 未初始化(E1)。\n请检查是否在启动对话框前进行初始化操作,使用以下代码进行初始化:\nDialogX.init(context);\n\n另外建议您前往查看 DialogX 的文档进行使用:https://github.com/kongzue/DialogX"); + return; + } + + ActivityLifecycleImpl.application = application; + if (activityLifecycle != null) { + application.unregisterActivityLifecycleCallbacks(activityLifecycle); + } + application.registerActivityLifecycleCallbacks(activityLifecycle = new ActivityLifecycleImpl(onActivityResumeCallBack)); + } else { + if (ActivityLifecycleImpl.application != null) { + init(ActivityLifecycleImpl.application, onActivityResumeCallBack); + } + } + } + + public static Application getApplicationContext(Context context) { + if (context != null) { + return (Application) context.getApplicationContext(); + } + try { + Application application = (Application) Class.forName("android.app.ActivityThread").getMethod("currentApplication").invoke(null, (Object[]) null); + return application; + } catch (Exception e) { + } + try { + Class activityThreadClass = Class.forName("android.app.ActivityThread"); + Object activityThread = activityThreadClass.getDeclaredMethod("currentActivityThread").invoke(null); + Method getApplicationMethod = activityThreadClass.getDeclaredMethod("getApplication"); + Application application = (Application) getApplicationMethod.invoke(activityThread); + return application; + } catch (Exception e) { + } + try { + Application application = (Application) Class.forName("android.app.AppGlobals").getMethod("getInitialApplication").invoke(null, (Object[]) null); + return application; + } catch (Exception e) { + } + error("DialogX.init: 初始化异常,请确保init方法内传入的Context是有效的。"); + return null; + } + + public static Application getApplicationContext() { + if (application != null) { + return application; + } + try { + Application application = (Application) Class.forName("android.app.ActivityThread").getMethod("currentApplication").invoke(null, (Object[]) null); + return application; + } catch (Exception e) { + } + try { + Class activityThreadClass = Class.forName("android.app.ActivityThread"); + Object activityThread = activityThreadClass.getDeclaredMethod("currentActivityThread").invoke(null); + Method getApplicationMethod = activityThreadClass.getDeclaredMethod("getApplication"); + Application application = (Application) getApplicationMethod.invoke(activityThread); + return application; + } catch (Exception e) { + } + try { + Application application = (Application) Class.forName("android.app.AppGlobals").getMethod("getInitialApplication").invoke(null, (Object[]) null); + return application; + } catch (Exception e) { + } + return null; + } + + public static Activity getTopActivity() { + try { + Class activityThreadClass = Class.forName("android.app.ActivityThread"); + Object activityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null); + Field activitiesField = activityThreadClass.getDeclaredField("mActivities"); + activitiesField.setAccessible(true); + Map activities; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + activities = (HashMap) activitiesField.get(activityThread); + } else { + activities = (ArrayMap) activitiesField.get(activityThread); + } + if (activities.size() < 1) { + return null; + } + for (Object activityRecord : activities.values()) { + Class activityRecordClass = activityRecord.getClass(); + Field pausedField = activityRecordClass.getDeclaredField("paused"); + pausedField.setAccessible(true); + if (!pausedField.getBoolean(activityRecord)) { + Field activityField = activityRecordClass.getDeclaredField("activity"); + activityField.setAccessible(true); + Activity activity = (Activity) activityField.get(activityRecord); + return activity; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + + public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) { + if (onActivityResumeCallBack != null) { + if (activity instanceof DialogXFloatingWindowActivity) { + return; + } + onActivityResumeCallBack.getActivity(activity); + } + } + + + public void onActivityStarted(@NonNull Activity activity) { + if (application == null) { + BaseDialog.init(activity); + } + } + + + public void onActivityPreResumed(@NonNull Activity activity) { +// Application.ActivityLifecycleCallbacks.super.onActivityPreResumed(activity); // # Android 4.4 bug,此代码将导致在 Android 4.4 上闪退,错误原因:java.lang.VerifyError + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + callOnResume(activity); + } + } + + + public void onActivityResumed(@NonNull Activity activity) { + if (activity.isDestroyed() || activity.isFinishing() || activity instanceof DialogXFloatingWindowActivity) { + return; + } + if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + callOnResume(activity); + } + BaseDialog.onActivityResume(activity); + } + + private void callOnResume(Activity activity) { + if (activity.isDestroyed() || activity.isFinishing() || activity instanceof DialogXFloatingWindowActivity) { + return; + } + if (onActivityResumeCallBack != null) { + onActivityResumeCallBack.getActivity(activity); + } + } + + + public void onActivityPaused(@NonNull Activity activity) { + } + + + public void onActivityStopped(@NonNull Activity activity) { + } + + + public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) { + + } + + public void onActivityDestroyed(@NonNull Activity activity) { + if (BaseDialog.getTopActivity() == activity) { + BaseDialog.cleanContext(); + } + if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.Q) { + BaseDialog.recycleDialog(activity); + } + } + + public void onActivityPreDestroyed(@NonNull final Activity activity) { + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + BaseDialog.recycleDialog(activity); + } + } + + public interface onActivityResumeCallBack { + void getActivity(Activity activity); + } + + public static boolean isExemptActivities(Activity activity) { + if (activity == null) return true; + for (String packageName : DialogX.unsupportedActivitiesPackageNames) { + if (activity.getClass().getName().contains(packageName)) { + return true; + } + } + return false; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/impl/AnimatorListenerEndCallBack.java b/DialogX/src/main/java/com/kongzue/dialogx/impl/AnimatorListenerEndCallBack.java new file mode 100644 index 0000000..c0dc335 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/impl/AnimatorListenerEndCallBack.java @@ -0,0 +1,30 @@ +package com.kongzue.dialogx.impl; + +import android.animation.Animator; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/22 14:37 + */ +public abstract class AnimatorListenerEndCallBack implements Animator.AnimatorListener { + @Override + public void onAnimationStart(Animator animation) { + + } + + @Override + public abstract void onAnimationEnd(Animator animation); + + @Override + public void onAnimationCancel(Animator animation) { + + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/impl/DialogFragmentImpl.java b/DialogX/src/main/java/com/kongzue/dialogx/impl/DialogFragmentImpl.java new file mode 100644 index 0000000..3f22d90 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/impl/DialogFragmentImpl.java @@ -0,0 +1,137 @@ +package com.kongzue.dialogx.impl; + +import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; +import static com.kongzue.dialogx.DialogX.error; + +import android.app.Activity; +import android.graphics.Color; +import android.graphics.PixelFormat; +import android.graphics.drawable.ColorDrawable; +import android.os.Build; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; + +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.interfaces.NoTouchInterface; + +import java.lang.ref.WeakReference; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2021/7/28 15:45 + */ +public class DialogFragmentImpl extends DialogFragment { + + public DialogFragmentImpl() { + } + + private View dialogView; + private BaseDialog baseDialog; + + public DialogFragmentImpl(BaseDialog baseDialog, View dialogView) { + this.dialogView = dialogView; + this.baseDialog = baseDialog; + activityWeakReference = new WeakReference<>(baseDialog.getOwnActivity()); + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return dialogView; + } + + WeakReference activityWeakReference = null; + + @Override + public void onStart() { + super.onStart(); + final Activity activity = activityWeakReference.get(); + + if (getDialog() == null) return; + Window dialogWindow = getDialog().getWindow(); + if (dialogWindow == null) return; + dialogWindow.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); + + dialogWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); + + WindowManager.LayoutParams lp = dialogWindow.getAttributes(); + lp.width = WindowManager.LayoutParams.MATCH_PARENT; + lp.height = WindowManager.LayoutParams.MATCH_PARENT; + lp.dimAmount = 0f; + lp.format = PixelFormat.TRANSPARENT; + dialogView.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + for (BaseDialog dialog : BaseDialog.getRunningDialogList()) { + if (dialog.getOwnActivity() == activity && dialog != baseDialog && dialog.getDialogImplMode() == baseDialog.getDialogImplMode()) { + if (!(dialog instanceof NoTouchInterface) && dialog.getDialogView() != null) { + Log.e(">>>", "onTouch: dialog=" + dialog + " baseDialog="+baseDialog ); + dialog.getDialogView().dispatchTouchEvent(event); + return true; + } + } + } + if (baseDialog instanceof NoTouchInterface) { + return activity.dispatchTouchEvent(event); + } else { + return false; + } + } + }); + dialogWindow.setAttributes(lp); + dialogWindow.addFlags(FLAG_TRANSLUCENT_STATUS); + dialogWindow.getDecorView().setPadding(0, 0, 0, 0); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + } + dialogWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + int visibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; + if (activity != null) { + if ((activity.getWindow().getDecorView().getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) == View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) { + visibility = visibility | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; + } + } + dialogWindow.getDecorView().setSystemUiVisibility(visibility); + dialogWindow.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); + dialogWindow.setStatusBarColor(Color.TRANSPARENT); + dialogWindow.setNavigationBarColor(Color.TRANSPARENT); + } else { + dialogWindow.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + } + } + + @Override + public void show(FragmentManager manager, @Nullable String tag) { + if (manager == null) { + error("DialogX.DialogFragment 模式无法支持非 AppCompatActivity 启动。"); + return; + } + //super.show(manager, tag); + + FragmentTransaction ft = manager.beginTransaction(); + ft.add(this, tag); + ft.commitAllowingStateLoss(); + } + + @Override + public void dismiss() { + dismissAllowingStateLoss(); + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BaseDialog.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BaseDialog.java new file mode 100644 index 0000000..f22a8f5 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BaseDialog.java @@ -0,0 +1,1173 @@ +package com.kongzue.dialogx.interfaces; + +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; +import static com.kongzue.dialogx.DialogX.DEBUGMODE; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.res.ColorStateList; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Color; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.text.TextUtils; +import android.util.Log; +import android.view.HapticFeedbackConstants; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowInsets; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import android.widget.FrameLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.FragmentManager; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleEventObserver; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.LifecycleRegistry; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.dialogs.WaitDialog; +import com.kongzue.dialogx.impl.ActivityLifecycleImpl; +import com.kongzue.dialogx.impl.DialogFragmentImpl; +import com.kongzue.dialogx.util.ActivityRunnable; +import com.kongzue.dialogx.util.DialogListBuilder; +import com.kongzue.dialogx.util.DialogXFloatingWindowActivity; +import com.kongzue.dialogx.util.TextInfo; +import com.kongzue.dialogx.util.WindowUtil; +import com.kongzue.dialogx.util.views.DialogXBaseRelativeLayout; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/22 14:10 + */ +public abstract class BaseDialog implements LifecycleOwner { + + private static Thread uiThread; + private static WeakReference activityWeakReference; + protected WeakReference ownActivity; + private WeakReference rootFrameLayout; + private static List runningDialogList; + protected int isHapticFeedbackEnabled = -1; + private WeakReference dialogView; + protected WeakReference ownDialogFragmentImpl; + protected DialogX.IMPL_MODE dialogImplMode = DialogX.implIMPLMode; + protected WeakReference floatingWindowActivity; + @Nullable + private DialogListBuilder dialogListBuilder; + protected LifecycleRegistry lifecycle = new LifecycleRegistry(this); + protected Map data; + protected DialogXRunnable onShowRunnable; + protected DialogXRunnable onDismissRunnable; + protected boolean enableImmersiveMode = true; // 沉浸式适配 + protected int thisOrderIndex = 0; + protected Map dialogActionRunnableMap = new HashMap(); + + public enum BUTTON_SELECT_RESULT { + NONE, // 未做出选择 + BUTTON_OK, // 选择了确定按钮 + BUTTON_CANCEL, // 选择了取消按钮 + BUTTON_OTHER // 选择了其他按钮 + } + + public static void init(Context context) { + if (context == null) { + context = ActivityLifecycleImpl.getTopActivity(); + } + if (context instanceof Activity) { + initActivityContext((Activity) context); + } + ActivityLifecycleImpl.init(context, new ActivityLifecycleImpl.onActivityResumeCallBack() { + @Override + public void getActivity(Activity activity) { + initActivityContext(activity); + } + }); + } + + private static void initActivityContext(Activity activity) { + if (ActivityLifecycleImpl.isExemptActivities(activity)) { + return; + } + try { + uiThread = Looper.getMainLooper().getThread(); + activityWeakReference = new WeakReference<>(activity); + } catch (Exception e) { + e.printStackTrace(); + error("DialogX.init: 初始化异常,找不到Activity的根布局"); + } + } + + protected static void log(Object o) { + if (DEBUGMODE) { + Log.i(">>>", o.toString()); + } + } + + protected static void error(Object o) { + if (DEBUGMODE) { + Log.e(">>>", o.toString()); + } + } + + public static void onActivityResume(Activity activity) { + if (runningDialogList != null) { + CopyOnWriteArrayList copyOnWriteList = new CopyOnWriteArrayList<>(runningDialogList); + for (int i = copyOnWriteList.size() - 1; i >= 0; i--) { + BaseDialog baseDialog = copyOnWriteList.get(i); + if (baseDialog.getOwnActivity() == activity && baseDialog.isShow && baseDialog.getDialogView() != null) { + View boxRoot = baseDialog.getDialogView().findViewById(R.id.box_root); + if (boxRoot instanceof DialogXBaseRelativeLayout) { + if (((DialogXBaseRelativeLayout) boxRoot).isBaseFocusable()) { + ((DialogXBaseRelativeLayout) boxRoot).requestFocusOnResume(); + return; + } + } + } + } + } + } + + private static void requestDialogFocus() { + if (runningDialogList != null) { + CopyOnWriteArrayList copyOnWriteList = new CopyOnWriteArrayList<>(runningDialogList); + for (int i = copyOnWriteList.size() - 1; i >= 0; i--) { + BaseDialog baseDialog = copyOnWriteList.get(i); + if (baseDialog.getOwnActivity() == getTopActivity() && baseDialog.isShow && baseDialog.getDialogView() != null) { + View boxRoot = baseDialog.getDialogView().findViewById(R.id.box_root); + if (boxRoot instanceof DialogXBaseRelativeLayout) { + if (((DialogXBaseRelativeLayout) boxRoot).isBaseFocusable()) { + boxRoot.requestFocus(); + return; + } + } + } + } + } + } + + public abstract void restartDialog(); + + protected static void show(final View view) { + if (view == null) { + return; + } + final BaseDialog dialog = (BaseDialog) view.getTag(); + if (dialog != null) { + if (dialog.isShow) { + if (dialog.getDialogView() != null) { + dialog.getDialogView().setVisibility(View.VISIBLE); + return; + } + error(((BaseDialog) view.getTag()).dialogKey() + "已处于显示状态,请勿重复执行 show() 指令。"); + return; + } + if (dialog.preShow(dialog)) { + return; + } + dialog.dialogView = new WeakReference<>(view); + + log(dialog.dialogKey() + ".show on " + (dialog.isActivityImplMode() ? dialog.getOwnActivity() : "window")); + + addDialogToRunningList(dialog); + switch (dialog.dialogImplMode) { + case WINDOW: + WindowUtil.show(dialog.getOwnActivity(), view, !(dialog instanceof NoTouchInterface)); + break; + case DIALOG_FRAGMENT: + DialogFragmentImpl dialogFragment = new DialogFragmentImpl(dialog, view); + dialogFragment.show(getSupportFragmentManager(dialog.getOwnActivity()), "DialogX"); + dialog.ownDialogFragmentImpl = new WeakReference<>(dialogFragment); + break; + case FLOATING_ACTIVITY: + if (waitRunDialogX == null) { + waitRunDialogX = new HashMap<>(); + } + waitRunDialogX.put(dialog.dialogKey(), new ActivityRunnable() { + @Override + public void run(Activity activity) { + dialog.floatingWindowActivity = new WeakReference<>((DialogXFloatingWindowActivity) activity); + dialog.floatingWindowActivity.get().setFromActivity(dialog.getOwnActivity()); + final FrameLayout activityRootView = getDecorView(activity); + if (activityRootView == null) { + return; + } + runOnMain(new Runnable() { + @Override + public void run() { + if (view.getParent() == dialog.getRootFrameLayout()) { + error(((BaseDialog) view.getTag()).dialogKey() + "已处于显示状态,请勿重复执行 show() 指令。"); + return; + } + if (view.getParent() != null) { + ((ViewGroup) view.getParent()).removeView(view); + } + activityRootView.addView(view); + } + }); + } + }); + DialogXFloatingWindowActivity dialogXFloatingWindowActivity = DialogXFloatingWindowActivity.getDialogXFloatingWindowActivity(); + if (dialogXFloatingWindowActivity != null && dialogXFloatingWindowActivity.isSameFrom(dialog.getOwnActivity().hashCode())) { + dialogXFloatingWindowActivity.showDialogX(dialog.dialogKey()); + return; + } + Intent intent = new Intent(getPrivateContext(), DialogXFloatingWindowActivity.class); + if (dialog.getOwnActivity() == null) { + intent.addFlags(FLAG_ACTIVITY_NEW_TASK); + } + intent.putExtra("dialogXKey", dialog.dialogKey()); + intent.putExtra("fromActivityUiStatus", dialog.getOwnActivity() == null ? 0 : (getDecorView(dialog.getOwnActivity()) == null ? 0 : getDecorView(dialog.getOwnActivity()).getSystemUiVisibility())); + intent.putExtra("from", getPrivateContext().hashCode()); + getPrivateContext().startActivity(intent); + int version = Integer.valueOf(Build.VERSION.SDK_INT); + if (version > 5 && dialog.getOwnActivity() != null) { + dialog.getOwnActivity().overridePendingTransition(0, 0); + } + break; + default: + if (dialog.getRootFrameLayout() == null) { + return; + } + runOnMain(new Runnable() { + @Override + public void run() { + if (view.getParent() == dialog.getRootFrameLayout()) { + error(((BaseDialog) view.getTag()).dialogKey() + "已处于显示状态,请勿重复执行 show() 指令。"); + return; + } + if (view.getParent() != null) { + ((ViewGroup) view.getParent()).removeView(view); + } + dialog.getRootFrameLayout().addView(view); + } + }); + break; + } + } + } + + private static FragmentManager getSupportFragmentManager(Activity activity) { + return (activity instanceof AppCompatActivity) ? ((AppCompatActivity) activity).getSupportFragmentManager() : null; + } + + private static Map waitRunDialogX; + + public static ActivityRunnable getActivityRunnable(String dialogXKey) { + if (dialogXKey == null) { + return null; + } + return waitRunDialogX.get(dialogXKey); + } + + protected static void show(final Activity activity, final View view) { + if (activity == null || view == null) { + return; + } + if (activityWeakReference == null || activityWeakReference.get() == null || ActivityLifecycleImpl.getApplicationContext() == null) { + init(activity.getApplicationContext()); + } + final BaseDialog baseDialog = (BaseDialog) view.getTag(); + if (baseDialog != null) { + baseDialog.setOwnActivity(activity); + if (baseDialog.getDialogView() != null) { + baseDialog.getDialogView().setVisibility(View.VISIBLE); + } + if (baseDialog.isShow) { + error(((BaseDialog) view.getTag()).dialogKey() + "已处于显示状态,请勿重复执行 show() 指令。"); + return; + } + if (activity.isDestroyed()) { + error(((BaseDialog) view.getTag()).dialogKey() + ".show ERROR: activity is Destroyed."); + return; + } + if (baseDialog.preShow(baseDialog)) { + return; + } + baseDialog.dialogView = new WeakReference<>(view); + + log(baseDialog + ".show on " + activity); + addDialogToRunningList(baseDialog); + + switch (baseDialog.dialogImplMode) { + case WINDOW: + WindowUtil.show(activity, view, !(baseDialog instanceof NoTouchInterface)); + break; + case DIALOG_FRAGMENT: + DialogFragmentImpl dialogFragment = new DialogFragmentImpl(baseDialog, view); + dialogFragment.show(getSupportFragmentManager(activity), "DialogX"); + baseDialog.ownDialogFragmentImpl = new WeakReference<>(dialogFragment); + break; + case FLOATING_ACTIVITY: + if (waitRunDialogX == null) { + waitRunDialogX = new HashMap<>(); + } + waitRunDialogX.put(baseDialog.dialogKey(), new ActivityRunnable() { + @Override + public void run(Activity activity) { + baseDialog.floatingWindowActivity = new WeakReference<>((DialogXFloatingWindowActivity) activity); + baseDialog.floatingWindowActivity.get().setFromActivity(baseDialog.getOwnActivity()); + final FrameLayout activityRootView = getDecorView(activity); + if (activityRootView == null) { + return; + } + runOnMain(new Runnable() { + @Override + public void run() { + if (view.getParent() == baseDialog.getRootFrameLayout()) { + error(((BaseDialog) view.getTag()).dialogKey() + "已处于显示状态,请勿重复执行 show() 指令。"); + return; + } + if (view.getParent() != null) { + ((ViewGroup) view.getParent()).removeView(view); + } + activityRootView.addView(view); + } + }); + } + }); + DialogXFloatingWindowActivity dialogXFloatingWindowActivity = DialogXFloatingWindowActivity.getDialogXFloatingWindowActivity(); + if (dialogXFloatingWindowActivity != null && dialogXFloatingWindowActivity.isSameFrom(activity.hashCode())) { + dialogXFloatingWindowActivity.showDialogX(baseDialog.dialogKey()); + return; + } + Intent intent = new Intent(activity, DialogXFloatingWindowActivity.class); + intent.putExtra("dialogXKey", baseDialog.dialogKey()); + intent.putExtra("from", activity.hashCode()); + intent.putExtra("fromActivityUiStatus", getDecorView(activity) == null ? 0 : getDecorView(activity).getSystemUiVisibility()); + activity.startActivity(intent); + int version = Integer.valueOf(Build.VERSION.SDK_INT); + if (version > 5) { + activity.overridePendingTransition(0, 0); + } + break; + default: + final FrameLayout activityRootView = getDecorView(activity); + if (activityRootView == null) { + return; + } + runOnMain(new Runnable() { + @Override + public void run() { + if (view.getParent() == baseDialog.getRootFrameLayout()) { + error(((BaseDialog) view.getTag()).dialogKey() + "已处于显示状态,请勿重复执行 show() 指令。"); + return; + } + if (view.getParent() != null) { + ((ViewGroup) view.getParent()).removeView(view); + } + activityRootView.addView(view); + } + }); + break; + } + } + } + + private void setOwnActivity(Activity activity) { + ownActivity = new WeakReference<>(activity); + } + + protected static void dismiss(final View dialogView) { + if (dialogView == null) { + return; + } + final BaseDialog baseDialog = (BaseDialog) dialogView.getTag(); + log(baseDialog.dialogKey() + ".dismiss"); + removeDialogToRunningList(baseDialog); + if (baseDialog.dialogView != null) { + baseDialog.dialogView.clear(); + } + baseDialog.onDialogDismiss(); + switch (baseDialog.dialogImplMode) { + case WINDOW: + WindowUtil.dismiss(dialogView); + break; + case DIALOG_FRAGMENT: + if (baseDialog.ownDialogFragmentImpl != null && baseDialog.ownDialogFragmentImpl.get() != null) { + baseDialog.ownDialogFragmentImpl.get().dismiss(); + } + break; + case FLOATING_ACTIVITY: + if (baseDialog.floatingWindowActivity != null && baseDialog.floatingWindowActivity.get() != null) { + FrameLayout rootView = getDecorView(baseDialog.floatingWindowActivity.get()); + if (rootView != null) { + rootView.removeView(dialogView); + } + baseDialog.floatingWindowActivity.get().finish(baseDialog.dialogKey()); + requestDialogFocus(); + } + break; + default: + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogView.getParent() == null || !(dialogView.getParent() instanceof ViewGroup)) { + if (baseDialog.getRootFrameLayout() == null) { + return; + } + baseDialog.getRootFrameLayout().removeView(dialogView); + } else { + ((ViewGroup) dialogView.getParent()).removeView(dialogView); + } + requestDialogFocus(); + } + }, true); + break; + } + if (baseDialog.getDialogListBuilder() != null) { + if (baseDialog.getDialogListBuilder().isEmpty()) { + baseDialog.cleanDialogList(); + } else { + baseDialog.getDialogListBuilder().showNext(); + } + } + } + + private static void addDialogToRunningList(BaseDialog baseDialog) { + if (runningDialogList == null) { + runningDialogList = new CopyOnWriteArrayList<>(); + } + runningDialogList.add(baseDialog); + } + + private static void removeDialogToRunningList(BaseDialog baseDialog) { + if (runningDialogList != null) { + runningDialogList.remove(baseDialog); + } + } + + public static Activity getTopActivity() { + if (activityWeakReference == null || activityWeakReference.get() == null) { + // 尝试反射初始化 + init(null); + if (activityWeakReference == null || activityWeakReference.get() == null) { + // 若还为空,无奈,尝试直接反射拿顶层 activity + Activity topActivity = ActivityLifecycleImpl.getTopActivity(); + init(topActivity); + return topActivity; + } + return activityWeakReference.get(); + } + return activityWeakReference.get(); + } + + /** + * @return 获取上下文 + * @Deprecated 已废弃,将在未来版本删除此方法,建议使用 {@link #getOwnActivity()} 替代此方法 + */ + @Deprecated + public static Context getContext() { + return getPrivateContext(); + } + + private static Context getPrivateContext() { + Activity activity = getTopActivity(); + if (activity == null) { + Context applicationContext = getApplicationContext(); + if (applicationContext == null) { + error("DialogX 未初始化(E2)。\n请检查是否在启动对话框前进行初始化操作,使用以下代码进行初始化:\nDialogX.init(context);\n\n另外建议您前往查看 DialogX 的文档进行使用:https://github.com/kongzue/DialogX"); + return null; + } + return applicationContext; + } + return activity; + } + + public static Context getApplicationContext() { + return ActivityLifecycleImpl.getApplicationContext(); + } + + /** + * 自动执行,不建议自行调用此方法 + * + * @hide + */ + public static void cleanContext() { + if (activityWeakReference != null) { + activityWeakReference.clear(); + } + activityWeakReference = null; + System.gc(); + } + + protected abstract void shutdown(); + + protected boolean cancelable = true; + protected boolean isShow; + protected DialogXStyle style; + protected DialogX.THEME theme; + protected boolean autoShowInputKeyboard; + protected Integer backgroundColor = null; + protected long enterAnimDuration = -1; + protected long exitAnimDuration = -1; + protected int maxWidth; + protected int maxHeight; + protected int minWidth; + protected int minHeight; + protected int[] screenPaddings = new int[4]; + + public BaseDialog() { + cancelable = DialogX.cancelable; + style = DialogX.globalStyle; + theme = DialogX.globalTheme; + enterAnimDuration = DialogX.enterAnimDuration; + exitAnimDuration = DialogX.exitAnimDuration; + autoShowInputKeyboard = DialogX.autoShowInputKeyboard; + enableImmersiveMode = DialogX.enableImmersiveMode; + } + + public abstract boolean isCancelable(); + + public View createView(int layoutId) { + if (isActivityImplMode()) { + if (getOwnActivity() == null) { + error("DialogX 未初始化(E3)。\n请检查是否在启动对话框前进行初始化操作,使用以下代码进行初始化:\nDialogX.init(context);\n\n另外建议您前往查看 DialogX 的文档进行使用:https://github.com/kongzue/DialogX"); + return null; + } + return LayoutInflater.from(getOwnActivity()).inflate(layoutId, null); + } else { + return LayoutInflater.from(getApplicationContext()).inflate(layoutId, null); + } + } + + public boolean isShow() { + return isShow; + } + + public DialogXStyle getStyle() { + return style; + } + + public DialogX.THEME getTheme() { + return theme; + } + + public static void useTextInfo(TextView textView, TextInfo textInfo) { + if (textInfo == null) { + return; + } + if (textView == null) { + return; + } + if (textInfo.getFontSize() > 0) { + textView.setTextSize(textInfo.getFontSizeComplexUnit(), textInfo.getFontSize()); + } + if (textInfo.getFontColor() != 1) { + textView.setTextColor(textInfo.getFontColor()); + } + if (textInfo.getGravity() != -1) { + textView.setGravity(textInfo.getGravity()); + } + if (textInfo.isShowEllipsis()) { + textView.setEllipsize(TextUtils.TruncateAt.END); + } else { + textView.setEllipsize(null); + } + if (textInfo.getMaxLines() != -1) { + textView.setMaxLines(textInfo.getMaxLines()); + } else { + textView.setMaxLines(Integer.MAX_VALUE); + } + if (textInfo.getTypeface() != null) { + textView.setTypeface(textInfo.getTypeface()); + } + + textView.getPaint().setFakeBoldText(textInfo.isBold()); + } + + protected void showText(TextView textView, CharSequence text) { + if (textView == null) { + return; + } + if (isNull(text)) { + textView.setVisibility(View.GONE); + textView.setText(""); + } else { + textView.setVisibility(View.VISIBLE); + textView.setText(text); + } + } + + public static boolean isNull(String s) { + if (s == null || s.trim().isEmpty() || "null".equals(s) || "(null)".equals(s)) { + return true; + } + return false; + } + + public static boolean isNull(CharSequence c) { + String s = String.valueOf(c); + if (c == null || s.trim().isEmpty() || "null".equals(s) || "(null)".equals(s)) { + return true; + } + return false; + } + + public Resources getResources() { + if (getOwnActivity() != null) { + return getOwnActivity().getResources(); + } + if (getApplicationContext() == null) { + return Resources.getSystem(); + } + return getApplicationContext().getResources(); + } + + public int dip2px(float dpValue) { + final float scale = getResources().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } + + public boolean isLightTheme() { + if (theme == DialogX.THEME.AUTO) { + if (getApplicationContext() == null) { + return theme == DialogX.THEME.LIGHT; + } + return (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_NO; + } + return theme == DialogX.THEME.LIGHT; + } + + @Nullable + public FrameLayout getRootFrameLayout() { + Activity activity = getOwnActivity(); + FrameLayout decorView; + if (isActivityImplMode()) { + if (activity == null) { + activity = getTopActivity(); + if (activity == null) { + error("DialogX 错误:在 getRootFrameLayout() 时无法获取绑定的 activity,请确认是否正确初始化:\n" + "DialogX.init(context);\n\n" + "或者使用 .show(activity) 启动对话框\n另外建议您前往查看 DialogX 的文档进行使用:https://github.com/kongzue/DialogX"); + return null; + } + setOwnActivity(activity); + } + decorView = getDecorView(activity); + } else { + decorView = (FrameLayout) getDialogView().getParent(); + } + if (decorView == null) { + error("DialogX 错误:在 getRootFrameLayout() 时无法获 activity(" + activity + ") 的 decorView,请检查该 activity 是否正常显示且可以使 DialogX 基于其显示。\n" + "若该 activity 不可用,可通过以下代码配置豁免 DialogX 对话框绑定至该 activity,例如:\n" + "DialogX.unsupportedActivitiesPackageNames = new String[]{\n" + " \"com.bytedance.sdk.openadsdk.stub.activity\",\n" + " \"com.mobile.auth.gatewayauth\",\n" + " \"com.google.android.gms.ads\"\n" + "};\n\n" + "另外建议您前往查看 DialogX 的文档进行使用:https://github.com/kongzue/DialogX"); + return null; + } + rootFrameLayout = new WeakReference<>(decorView); + return rootFrameLayout.get(); + } + + public void tintColor(View view, int color) { + if (view == null) { + return; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + view.setBackgroundTintList(ColorStateList.valueOf(color)); + } + } + + /** + * 此标记用于拦截重复 dismiss 指令导致的关闭动画抖动异常 + */ + protected boolean dismissAnimFlag; + protected boolean preShow; + + protected void beforeShow() { + preShow = true; + dismissAnimFlag = false; + setOwnActivity(getTopActivity()); + if (getOwnActivity() == null && isActivityImplMode()) { + // 尝试重新获取 activity + init(null); + if (getOwnActivity() == null) { + error("DialogX 未初始化(E5)。\n请检查是否在启动对话框前进行初始化操作,使用以下代码进行初始化:\nDialogX.init(context);\n\n另外建议您前往查看 DialogX 的文档进行使用:https://github.com/kongzue/DialogX"); + return; + } + } + if (style.styleVer != DialogXStyle.styleVer) { + error("DialogX 所引用的 Style 不符合当前适用版本:" + DialogXStyle.styleVer + " 引入的 Style(" + style.getClass().getSimpleName() + ") 版本" + style.styleVer); + } + + if (dialogImplMode != DialogX.IMPL_MODE.VIEW && getOwnActivity() instanceof LifecycleOwner) { + Lifecycle lifecycle = ((LifecycleOwner) getOwnActivity()).getLifecycle(); + lifecycle.addObserver(new LifecycleEventObserver() { + @Override + public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { + if (event == Lifecycle.Event.ON_DESTROY) { + recycleDialog(getOwnActivity()); + } + } + }); + } + + // Hide IME + if (!(this instanceof NoTouchInterface)) { + View view = getOwnActivity().getCurrentFocus(); + if (view != null) { + InputMethodManager imm = (InputMethodManager) getOwnActivity().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + } + } + } + + private boolean isActivityImplMode() { + return getDialogImplMode() == DialogX.IMPL_MODE.VIEW || getDialogImplMode() == DialogX.IMPL_MODE.FLOATING_ACTIVITY || getDialogImplMode() == DialogX.IMPL_MODE.DIALOG_FRAGMENT; + } + + protected String getString(int resId) { + if (getApplicationContext() == null) { + error("DialogX 未初始化(E6)。\n请检查是否在启动对话框前进行初始化操作,使用以下代码进行初始化:\nDialogX.init(context);\n\n另外建议您前往查看 DialogX 的文档进行使用:https://github.com/kongzue/DialogX"); + return null; + } + if (resId == 0) { + return ""; + } + return getResources().getString(resId); + } + + protected int getColor(int backgroundRes) { + if (getApplicationContext() == null) { + error("DialogX 未初始化(E7)。\n请检查是否在启动对话框前进行初始化操作,使用以下代码进行初始化:\nDialogX.init(context);\n\n另外建议您前往查看 DialogX 的文档进行使用:https://github.com/kongzue/DialogX"); + return Color.BLACK; + } + return getResources().getColor(backgroundRes); + } + + protected Integer getColorNullable(Integer res) { + return res == null ? null : getColor(res); + } + + protected Integer getColorNullable(Integer res, Integer defaultResId) { + return res == null ? getColor(defaultResId) : getColor(res); + } + + public enum BOOLEAN { + TRUE, FALSE + } + + public abstract String dialogKey(); + + protected static void runOnMain(Runnable runnable) { + if (!DialogX.autoRunOnUIThread || (getUiThread() != null && Thread.currentThread() == getUiThread())) { + runnable.run(); + return; + } + runOnMain(runnable, true); + } + + protected static Thread getUiThread() { + if (uiThread == null) { + uiThread = Looper.getMainLooper().getThread(); + } + return uiThread; + } + + protected static void runOnMain(Runnable runnable, boolean needWaitMainLooper) { + getMainHandler().post(runnable); + } + + protected static void runOnMainDelay(Runnable runnable, long delay) { + if (delay < 0) { + return; + } + if (!DialogX.autoRunOnUIThread) { + runnable.run(); + } + getMainHandler().postDelayed(runnable, delay); + } + + public View getDialogView() { + if (dialogView == null) { + return null; + } + return dialogView.get(); + } + + public Activity getOwnActivity() { + if (ownActivity == null || ownActivity.get() == null) { + setOwnActivity(getTopActivity()); + } + return ownActivity.get(); + } + + protected void cleanActivityContext() { + if (ownActivity != null) { + ownActivity.clear(); + } + dialogView = null; + ownActivity = null; + } + + public static void cleanAll() { + if (runningDialogList != null) { + CopyOnWriteArrayList copyOnWriteList = new CopyOnWriteArrayList<>(runningDialogList); + for (BaseDialog baseDialog : copyOnWriteList) { + if (baseDialog.isShow()) { + baseDialog.shutdown(); + } + baseDialog.cleanActivityContext(); + runningDialogList.remove(baseDialog); + } + } + } + + public static void recycleDialog(Activity activity) { + switch (DialogX.implIMPLMode) { + case WINDOW: + if (runningDialogList != null) { + CopyOnWriteArrayList copyOnWriteList = new CopyOnWriteArrayList<>(runningDialogList); + for (BaseDialog baseDialog : copyOnWriteList) { + if (baseDialog.getOwnActivity() == activity && baseDialog.dialogView != null) { + WindowUtil.dismiss(baseDialog.dialogView.get()); + if (baseDialog instanceof WaitDialog) { + ((WaitDialog) baseDialog).cleanInstance(); + } + runningDialogList.remove(baseDialog); + } + } + } + break; + case DIALOG_FRAGMENT: + if (runningDialogList != null) { + CopyOnWriteArrayList copyOnWriteList = new CopyOnWriteArrayList<>(runningDialogList); + for (BaseDialog baseDialog : copyOnWriteList) { + if (baseDialog.getOwnActivity() == activity && baseDialog.ownDialogFragmentImpl != null && baseDialog.ownDialogFragmentImpl.get() != null) { + baseDialog.ownDialogFragmentImpl.get().dismiss(); + if (baseDialog instanceof WaitDialog) { + ((WaitDialog) baseDialog).cleanInstance(); + } + runningDialogList.remove(baseDialog); + } + } + } + break; + case FLOATING_ACTIVITY: + + break; + default: + if (runningDialogList != null) { + CopyOnWriteArrayList copyOnWriteList = new CopyOnWriteArrayList<>(runningDialogList); + for (BaseDialog baseDialog : copyOnWriteList) { + if (baseDialog.getOwnActivity() == activity) { + baseDialog.cleanActivityContext(); + runningDialogList.remove(baseDialog); + if (baseDialog instanceof WaitDialog) { + ((WaitDialog) baseDialog).cleanInstance(); + } + } + } + } + break; + } + if (activity == getTopActivity()) { + cleanContext(); + } + } + + public static List getRunningDialogList() { + if (runningDialogList == null) { + return new ArrayList<>(); + } + return new CopyOnWriteArrayList<>(runningDialogList); + } + + public static List getRunningDialogList(Activity activity) { + List result = new ArrayList<>(); + for (BaseDialog dialog : runningDialogList) { + if (dialog != null && dialog.isShow && dialog.getOwnActivity() == activity) { + result.add(dialog); + } + } + return result; + } + + protected void imeShow(EditText editText, boolean show) { + if (getOwnActivity() == null) { + return; + } + InputMethodManager imm = (InputMethodManager) getOwnActivity().getSystemService(Context.INPUT_METHOD_SERVICE); + if (show) { + imm.showSoftInput(editText, InputMethodManager.RESULT_UNCHANGED_SHOWN); + } else { + imm.hideSoftInputFromWindow(editText.getWindowToken(), 0); + } + } + + public DialogX.IMPL_MODE getDialogImplMode() { + return dialogImplMode; + } + + protected static WindowInsets windowInsets; + + public static WindowInsets publicWindowInsets() { + return windowInsets; + } + + protected void bindFloatingActivity(DialogXFloatingWindowActivity activity) { + floatingWindowActivity = new WeakReference<>(activity); + } + + static WeakReference mMainHandler; + + private static Handler getMainHandler() { + if (mMainHandler != null && mMainHandler.get() != null) { + return mMainHandler.get(); + } + mMainHandler = new WeakReference<>(new Handler(Looper.getMainLooper())); + return mMainHandler.get(); + } + + @Nullable + public DialogListBuilder getDialogListBuilder() { + return dialogListBuilder; + } + + public void setDialogListBuilder(@NonNull DialogListBuilder dialogListBuilder) { + this.dialogListBuilder = dialogListBuilder; + } + + public void cleanDialogList() { + if (this.dialogListBuilder != null) { + this.dialogListBuilder.clear(); + } + this.dialogListBuilder = null; + } + + public boolean isPreShow() { + return preShow; + } + + public abstract D show(); + + protected void onDialogShow() { + if (onShowRunnable != null) onShowRunnable.run(this); + } + + protected void refreshUI() { + } + + protected void onDialogInit() { + } + + protected void onDialogRefreshUI() { + } + + protected void onDialogDismiss() { + if (onDismissRunnable != null) onDismissRunnable.run(this); + } + + @NonNull + @Override + public Lifecycle getLifecycle() { + return lifecycle; + } + + public int getMaxWidth() { + if (maxWidth == 0) { + return DialogX.dialogMaxWidth; + } + return maxWidth; + } + + public int getMaxHeight() { + if (maxHeight == 0) { + return DialogX.dialogMaxHeight; + } + return maxHeight; + } + + public int getMinWidth() { + if (minWidth == 0) { + return DialogX.dialogMinWidth; + } + return minWidth; + } + + public int getMinHeight() { + if (minHeight == 0) { + return DialogX.dialogMinHeight; + } + return minHeight; + } + + protected void setLifecycleState(Lifecycle.State s) { + if (lifecycle == null || s == null) return; + try { + lifecycle.setCurrentState(s); + } catch (Exception e) { + } + } + + protected static FrameLayout getDecorView(Activity activity) { + if (activity == null || activity.getWindow() == null || !(activity.getWindow().getDecorView() instanceof FrameLayout)) + return null; + return (FrameLayout) activity.getWindow().getDecorView(); + } + + protected List findAllBlurView(View v) { + List result = new ArrayList<>(); + if (v instanceof BlurViewType) { + result.add(v); + } + if (v instanceof ViewGroup) { + ViewGroup group = (ViewGroup) v; + for (int i = 0; i < group.getChildCount(); i++) { + List child = findAllBlurView(group.getChildAt(i)); + if (child != null) result.addAll(child); + } + } + if (!result.isEmpty()) { + return result; + } + return null; + } + + protected Integer getIntStyleAttr(Integer styleValue) { + return styleValue <= 0 ? null : styleValue; + } + + protected Integer getIntStyleAttr(Integer styleValue, Integer defaultValue) { + return styleValue <= 0 ? defaultValue : styleValue; + } + + protected Float getFloatStyleAttr(Float styleValue) { + return styleValue <= 0 ? null : styleValue; + } + + protected Float getFloatStyleAttr(Float styleValue, Float defaultValue) { + if (styleValue <= 0) { + return defaultValue; + } + return styleValue; + } + + protected void setDialogView(View view) { + dialogView = new WeakReference<>(view); + } + + protected void haptic(View v) { + if (v != null) if (DialogX.useHaptic && isHapticFeedbackEnabled == -1) + v.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + else if (isHapticFeedbackEnabled == 1) + v.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + } + + protected boolean isHide; + + public boolean isHide() { + return isHide; + } + + public T getData(String key) { + return data == null ? null : (T) data.get(key); + } + + public BaseDialog setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public boolean isEnableImmersiveMode() { + return enableImmersiveMode; + } + + public BaseDialog setEnableImmersiveMode(boolean enableImmersiveMode) { + this.enableImmersiveMode = enableImmersiveMode; + refreshUI(); + return this; + } + + /** + * 启动前执行 + * + * @return 若返回 true 表示拦截启动流程 + */ + public boolean preShow(BaseDialog dialog) { + return false; + } + + /** + * 关闭前执行 + * + * @return 若返回 true 表示拦截退出流程 + */ + public boolean preDismiss(BaseDialog dialog) { + return false; + } + + public int getThisOrderIndex() { + return thisOrderIndex; + } + + public BaseDialog setThisOrderIndex(int thisOrderIndex) { + this.thisOrderIndex = thisOrderIndex; + return this; + } + + protected int getHighestOrderIndex() { + if (getOwnActivity() != null && getDecorView(getOwnActivity()) != null) { + return getDecorView(getOwnActivity()).getChildCount(); + } + return runningDialogList == null ? 1 : runningDialogList.size(); + } + + protected static boolean isActivityDestroyed(Activity activity) { + return activity == null + || activity.isFinishing() + || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && activity.isDestroyed()) + || (activity instanceof LifecycleOwner && ((LifecycleOwner) activity).getLifecycle().getCurrentState() == Lifecycle.State.DESTROYED); + } + + public boolean dispatchTouchEvent(MotionEvent event) { + if (getDialogView() == null) { + if (ownActivity != null && ownActivity.get() != null) { + return ownActivity.get().dispatchTouchEvent(event); + } else { + return false; + } + } + return getDialogView().dispatchTouchEvent(event); + } + + public boolean runAction(int actionId) { + DialogXRunnable runnable = dialogActionRunnableMap.get(actionId); + if (runnable != null) { + runnable.run(this); + return true; + } + return false; + } + + public abstract void callDialogDismiss(); + + protected void bindDismissWithLifecycleOwnerPrivate(LifecycleOwner owner) { + if (owner == null) return; + owner.getLifecycle().addObserver(new LifecycleEventObserver() { + @Override + public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { + if (event == Lifecycle.Event.ON_STOP || event == Lifecycle.Event.ON_DESTROY) { + callDialogDismiss(); + source.getLifecycle().removeObserver(this); + } + } + }); + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BaseOnDialogClickCallback.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BaseOnDialogClickCallback.java new file mode 100644 index 0000000..23fcb6c --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BaseOnDialogClickCallback.java @@ -0,0 +1,14 @@ +package com.kongzue.dialogx.interfaces; + +import android.view.View; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/22 15:07 + */ +public interface BaseOnDialogClickCallback { + +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BlurViewType.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BlurViewType.java new file mode 100644 index 0000000..a451f9f --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BlurViewType.java @@ -0,0 +1,8 @@ +package com.kongzue.dialogx.interfaces; + +import androidx.annotation.Nullable; + +public interface BlurViewType { + void setOverlayColor(@Nullable Integer color); + void setRadiusPx(@Nullable Float r); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BottomDialogSlideEventLifecycleCallback.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BottomDialogSlideEventLifecycleCallback.java new file mode 100644 index 0000000..0923dc1 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BottomDialogSlideEventLifecycleCallback.java @@ -0,0 +1,22 @@ +package com.kongzue.dialogx.interfaces; + +import android.view.MotionEvent; +import android.view.View; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2022/12/11 17:39 + */ +public abstract class BottomDialogSlideEventLifecycleCallback extends DialogLifecycleCallback { + + public boolean onSlideClose(D dialog) { + return false; + } + + public boolean onSlideTouchEvent(D dialog, View v, MotionEvent event) { + return false; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BottomMenuListViewTouchEvent.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BottomMenuListViewTouchEvent.java new file mode 100644 index 0000000..3471e29 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BottomMenuListViewTouchEvent.java @@ -0,0 +1,17 @@ +package com.kongzue.dialogx.interfaces; + +import android.view.MotionEvent; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/11/18 15:01 + */ +public abstract class BottomMenuListViewTouchEvent { + + public void down(MotionEvent event){}; + public void move(MotionEvent event){}; + public void up(MotionEvent event){}; +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogConvertViewInterface.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogConvertViewInterface.java new file mode 100644 index 0000000..1ea5c70 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogConvertViewInterface.java @@ -0,0 +1,19 @@ +package com.kongzue.dialogx.interfaces; + +import android.view.View; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/6 15:52 + */ +public interface DialogConvertViewInterface { + + void init(); + + void refreshView(); + + void doDismiss(View v); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogLifecycleCallback.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogLifecycleCallback.java new file mode 100644 index 0000000..2ec8efb --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogLifecycleCallback.java @@ -0,0 +1,52 @@ +package com.kongzue.dialogx.interfaces; + +import androidx.annotation.NonNull; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.LifecycleRegistry; + +import com.kongzue.dialogx.DialogX; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/22 14:09 + */ +public abstract class DialogLifecycleCallback implements LifecycleOwner { + + private final LifecycleRegistry registry = new LifecycleRegistry(this); + + public void onShow(T dialog) { + try { + //概率性报 no event down from INITIALIZED,目前尚不清楚为何 + if (registry.getCurrentState() != Lifecycle.State.CREATED) { + registry.setCurrentState(Lifecycle.State.CREATED); + } + } catch (Exception e) { + } + if (DialogX.dialogLifeCycleListener != null && DialogX.dialogLifeCycleListener != this) { + DialogX.dialogLifeCycleListener.onShow(dialog); + } + } + + public void onDismiss(T dialog) { + try { + if (registry.getCurrentState() != Lifecycle.State.DESTROYED) { + //概率性报 no event down from INITIALIZED,目前尚不清楚为何 + registry.setCurrentState(Lifecycle.State.DESTROYED); + } + } catch (Exception e) { + } + if (DialogX.dialogLifeCycleListener != null && DialogX.dialogLifeCycleListener != this) { + DialogX.dialogLifeCycleListener.onDismiss(dialog); + } + } + + @NonNull + @Override + public Lifecycle getLifecycle() { + return registry; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXAnimInterface.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXAnimInterface.java new file mode 100644 index 0000000..31a9b9e --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXAnimInterface.java @@ -0,0 +1,22 @@ +package com.kongzue.dialogx.interfaces; + +import android.animation.ValueAnimator; +import android.view.ViewGroup; + +import com.kongzue.dialogx.util.ObjectRunnable; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2022/9/5 9:21 + */ +public abstract class DialogXAnimInterface { + + public void doShowAnim(D dialog, ViewGroup dialogBodyView) { + } + + public void doExitAnim(D dialog, ViewGroup dialogBodyView) { + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXBaseBottomDialog.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXBaseBottomDialog.java new file mode 100644 index 0000000..c63d48d --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXBaseBottomDialog.java @@ -0,0 +1,5 @@ +package com.kongzue.dialogx.interfaces; + +public interface DialogXBaseBottomDialog { + boolean isBottomNonSafetyAreaBySelf(); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXRunnable.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXRunnable.java new file mode 100644 index 0000000..6bb0f26 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXRunnable.java @@ -0,0 +1,7 @@ +package com.kongzue.dialogx.interfaces; + +public interface DialogXRunnable { + + void run(D dialog); + +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXSafetyModeInterface.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXSafetyModeInterface.java new file mode 100644 index 0000000..e8ccf8c --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXSafetyModeInterface.java @@ -0,0 +1,5 @@ +package com.kongzue.dialogx.interfaces; + +public interface DialogXSafetyModeInterface { + int getDialogXSafetyMode(); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DynamicWindowInsetsAnimationListener.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DynamicWindowInsetsAnimationListener.java new file mode 100644 index 0000000..04cf759 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DynamicWindowInsetsAnimationListener.java @@ -0,0 +1,8 @@ +package com.kongzue.dialogx.interfaces; + +import android.view.WindowInsets; + +public interface DynamicWindowInsetsAnimationListener { + + void onChange(WindowInsets windowInsets); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/MenuIconAdapter.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/MenuIconAdapter.java new file mode 100644 index 0000000..0216b80 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/MenuIconAdapter.java @@ -0,0 +1,20 @@ +package com.kongzue.dialogx.interfaces; + +import android.widget.ImageView; + +public abstract class MenuIconAdapter extends OnIconChangeCallBack { + + public MenuIconAdapter() { + } + + public MenuIconAdapter(boolean autoTintIconInLightOrDarkMode) { + super(autoTintIconInLightOrDarkMode); + } + + public abstract boolean applyIcon(D dialog, int index, String menuText, ImageView iconImageView); + + @Override + public int getIcon(D dialog, int index, String menuText) { + return 0; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/MenuItemLayoutRefreshCallback.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/MenuItemLayoutRefreshCallback.java new file mode 100644 index 0000000..bf993ec --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/MenuItemLayoutRefreshCallback.java @@ -0,0 +1,16 @@ +package com.kongzue.dialogx.interfaces; + +import android.view.View; +import android.view.ViewGroup; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2023/2/22 16:47 + */ +public interface MenuItemLayoutRefreshCallback { + + void getView(D dialog, int position, View convertView, ViewGroup parent); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/MenuItemTextInfoInterceptor.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/MenuItemTextInfoInterceptor.java new file mode 100644 index 0000000..a1a246d --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/MenuItemTextInfoInterceptor.java @@ -0,0 +1,28 @@ +package com.kongzue.dialogx.interfaces; + +import com.kongzue.dialogx.util.TextInfo; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2022/1/22 21:32 + */ +public abstract class MenuItemTextInfoInterceptor { + + private boolean autoTintIconInLightOrDarkMode; + + public MenuItemTextInfoInterceptor() { + } + + public MenuItemTextInfoInterceptor(boolean autoTintIconInLightOrDarkMode) { + this.autoTintIconInLightOrDarkMode = autoTintIconInLightOrDarkMode; + } + + public abstract TextInfo menuItemTextInfo(D dialog, int index, String menuText); + + public boolean isAutoTintIconInLightOrDarkMode() { + return autoTintIconInLightOrDarkMode; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/NoTouchInterface.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/NoTouchInterface.java new file mode 100644 index 0000000..810acda --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/NoTouchInterface.java @@ -0,0 +1,11 @@ +package com.kongzue.dialogx.interfaces; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2022/7/19 17:37 + */ +public interface NoTouchInterface { +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnBackPressedListener.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnBackPressedListener.java new file mode 100644 index 0000000..f607214 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnBackPressedListener.java @@ -0,0 +1,12 @@ +package com.kongzue.dialogx.interfaces; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/25 15:48 + */ +public interface OnBackPressedListener { + boolean onBackPressed(D dialog); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnBackgroundMaskClickListener.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnBackgroundMaskClickListener.java new file mode 100644 index 0000000..511c4c8 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnBackgroundMaskClickListener.java @@ -0,0 +1,14 @@ +package com.kongzue.dialogx.interfaces; + +import android.view.View; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2022/7/8 16:27 + */ +public interface OnBackgroundMaskClickListener { + boolean onClick(D dialog, View v); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnBindView.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnBindView.java new file mode 100644 index 0000000..8ffff5e --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnBindView.java @@ -0,0 +1,225 @@ +package com.kongzue.dialogx.interfaces; + +import android.app.Activity; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.RelativeLayout; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.util.views.ExtendChildLayoutParamsFrameLayout; + +import static com.kongzue.dialogx.DialogX.ERROR_INIT_TIPS; + +import androidx.appcompat.app.AppCompatActivity; + +import java.security.SecureRandom; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/8 17:00 + */ +public abstract class OnBindView { + + int layoutResId; + View customView; + private final int PARENT_FLAG = -109; + + public OnBindView(int layoutResId) { + if (BaseDialog.getTopActivity() == null) { + DialogX.error(ERROR_INIT_TIPS); + return; + } + this.layoutResId = layoutResId; + customView = LayoutInflater.from(BaseDialog.getTopActivity()).inflate(layoutResId, new RelativeLayout(BaseDialog.getTopActivity()), false); + } + + public OnBindView(int layoutResId, boolean async) { + if (BaseDialog.getTopActivity() == null) { + DialogX.error(ERROR_INIT_TIPS); + return; + } + this.layoutResId = layoutResId; + if (async) { + new Thread() { + @Override + public void run() { + super.run(); + synchronized (OnBindView.this) { + customView = LayoutInflater.from(BaseDialog.getTopActivity()).inflate(layoutResId, new RelativeLayout(BaseDialog.getTopActivity()), false); + if (waitBindRunnable != null) { + waitBindRunnable.run(); + waitBindRunnable = null; + } + } + } + }.start(); + } else { + customView = LayoutInflater.from(BaseDialog.getTopActivity()).inflate(layoutResId, new RelativeLayout(BaseDialog.getTopActivity()), false); + } + } + + public OnBindView(View customView) { + this.customView = customView; + } + + private androidx.fragment.app.Fragment fragment; + private android.app.Fragment supportFragment; + private int fragmentParentId = View.NO_ID; + + private int getFragmentParentId() { + if (fragmentParentId == View.NO_ID) { + fragmentParentId = View.generateViewId(); + } + return fragmentParentId; + } + + public OnBindView(androidx.fragment.app.Fragment fragment) { + if (BaseDialog.getTopActivity() == null) return; + this.customView = new ExtendChildLayoutParamsFrameLayout(BaseDialog.getTopActivity()); + this.customView.setId(getFragmentParentId()); + this.fragment = fragment; + this.supportFragment = null; + } + + public OnBindView(android.app.Fragment supportFragment) { + if (BaseDialog.getTopActivity() == null) return; + this.customView = new ExtendChildLayoutParamsFrameLayout(BaseDialog.getTopActivity()); + this.customView.setId(getFragmentParentId()); + this.supportFragment = supportFragment; + this.fragment = null; + } + + public abstract void onBind(D dialog, View v); + + public void setEvent(D dialog, View v){} + + public void onFragmentBind(D dialog, View frameLayout, androidx.fragment.app.Fragment fragment, androidx.fragment.app.FragmentManager fragmentManager) { + } + + public void onFragmentBind(D dialog, View frameLayout, android.app.Fragment fragment, android.app.FragmentManager fragmentManager) { + } + + public int getLayoutResId() { + return layoutResId; + } + + public OnBindView setLayoutResId(int layoutResId) { + this.layoutResId = layoutResId; + return this; + } + + public View getCustomView() { + if (customView == null) { + customView = LayoutInflater.from(BaseDialog.getTopActivity()).inflate(layoutResId, new RelativeLayout(BaseDialog.getTopActivity()), false); + } + return customView; + } + + public OnBindView setCustomView(View customView) { + this.customView = customView; + return this; + } + + public void clean() { + layoutResId = 0; + customView = null; + } + + @Deprecated + public void bindParent(ViewGroup parentView) { + if (getCustomView() == null) { + waitBind(parentView, null); + return; + } + if (getCustomView().getParent() != null) { + if (getCustomView().getParent() == parentView) { + return; + } + ((ViewGroup) getCustomView().getParent()).removeView(getCustomView()); + } + ViewGroup.LayoutParams lp = parentView.getLayoutParams(); + if (lp == null) { + lp = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + } + parentView.addView(getCustomView(), lp); + } + + public void bindParent(ViewGroup parentView, BaseDialog dialog) { + if (getCustomView() == null) { + waitBind(parentView, null); + return; + } + if (getCustomView().getParent() == parentView || parentView.getTag(PARENT_FLAG) == getCustomView().toString()) { + return; + } + if (getCustomView().getParent() != null) { + ((ViewGroup) getCustomView().getParent()).removeView(getCustomView()); + } + ViewGroup.LayoutParams lp = getCustomView().getLayoutParams(); + if (lp == null) { + lp = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + } + parentView.addView(getCustomView(), lp); + onBind((D) dialog, getCustomView()); + callSetEvent((D) dialog, getCustomView()); + if (fragment != null || supportFragment != null) { + if (dialog.getDialogImplMode() != DialogX.IMPL_MODE.VIEW) { + BaseDialog.error(dialog.dialogKey() + "非 VIEW 实现模式不支持 fragment 作为子布局显示。\n" + + "其原因为 Window 中不存在 FragmentManager,无法对子布局中的 fragment 进行管理。"); + return; + } + getCustomView().post(new Runnable() { + @Override + public void run() { + if (fragment != null && getCustomView() instanceof FrameLayout && dialog.getOwnActivity() instanceof AppCompatActivity) { + AppCompatActivity appCompatActivity = (AppCompatActivity) dialog.getOwnActivity(); + androidx.fragment.app.FragmentTransaction transaction = appCompatActivity.getSupportFragmentManager().beginTransaction(); + transaction.add(getFragmentParentId(), fragment); + transaction.commit(); + onFragmentBind((D) dialog, getCustomView(), fragment, appCompatActivity.getSupportFragmentManager()); + } + if (supportFragment != null && getCustomView() instanceof FrameLayout && dialog.getOwnActivity() instanceof Activity) { + Activity activity = dialog.getOwnActivity(); + android.app.FragmentTransaction transaction = activity.getFragmentManager().beginTransaction(); + transaction.add(getFragmentParentId(), supportFragment); + transaction.commit(); + onFragmentBind((D) dialog, getCustomView(), supportFragment, activity.getFragmentManager()); + } + } + }); + } + } + + private int dialogHash, parentViewHash; + + private void callSetEvent(D dialog, View view) { + if (dialog.hashCode() != dialogHash || view.hashCode() != parentViewHash) { + dialogHash = dialog.hashCode(); + parentViewHash = view.hashCode(); + setEvent(dialog, getCustomView()); + } + } + + private Runnable waitBindRunnable; + + private void waitBind(ViewGroup parentView, BaseDialog dialog) { + waitBindRunnable = new Runnable() { + @Override + public void run() { + if (getCustomView() == null) { + if (dialog == null) { + bindParent(parentView); + } else { + bindParent(parentView, dialog); + } + } + } + }; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnBindingView.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnBindingView.java new file mode 100644 index 0000000..7b084ba --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnBindingView.java @@ -0,0 +1,108 @@ +package com.kongzue.dialogx.interfaces; + +import static com.kongzue.dialogx.interfaces.BaseDialog.error; + +import android.view.LayoutInflater; +import android.view.View; + +import androidx.fragment.app.Fragment; +import androidx.viewbinding.ViewBinding; + +import com.kongzue.dialogx.R; + +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +public abstract class OnBindingView extends OnBindView { + + protected VB binding; + + public OnBindingView(VB binding) { + super(binding.getRoot()); + this.binding = binding; + } + + public OnBindingView() { + super((View) null); + setCustomView(getBindingRootView(getViewBinding())); + this.binding = (VB) getCustomView().getTag(R.id.dialogx_view_binding_tag_key); + } + + public OnBindingView(Class viewBindingClass) { + super(getBindingRootView(getViewBinding(viewBindingClass))); + this.binding = (VB) getCustomView().getTag(R.id.dialogx_view_binding_tag_key); + } + + public OnBindingView(String viewBindingClassName) { + super(getBindingRootView(getViewBinding(viewBindingClassName))); + this.binding = (VB) getCustomView().getTag(R.id.dialogx_view_binding_tag_key); + } + + private static View getBindingRootView(ViewBinding viewBinding) { + if (viewBinding == null) { + return new View(BaseDialog.getContext()); + } + View view = viewBinding.getRoot(); + view.setTag(R.id.dialogx_view_binding_tag_key, viewBinding); + return view; + } + + private ViewBinding getViewBinding() { + Type superclass = getClass().getGenericSuperclass(); + if (superclass instanceof ParameterizedType) { + Class clazz = (Class) ((ParameterizedType) superclass).getActualTypeArguments()[1]; + return getViewBinding(clazz); + } else { + error("DialogX: OnBindingView初始化异常,若要使用无参构建,必须指定ViewBinding泛型"); + return null; + } + } + + private static ViewBinding getViewBinding(String bindingClassName) { + try { + return getViewBinding(Class.forName(bindingClassName)); + } catch (ClassNotFoundException e) { + error("DialogX: OnBindingView初始化异常,未能根据bindingClassName:" + bindingClassName + "找到对应的ViewBinding,请尝试指定ViewBinding实例"); + throw new RuntimeException(e); + } + } + + private static ViewBinding getViewBinding(Class bindingClass) { + try { + Method inflateMethod = bindingClass.getMethod("inflate", LayoutInflater.class); + return (ViewBinding) inflateMethod.invoke((Object) null, LayoutInflater.from(BaseDialog.getContext())); + } catch (Exception var5) { + error("DialogX: OnBindingView初始化异常,未能根据bindingClass找到对应的ViewBinding,请尝试指定ViewBinding实例"); + var5.printStackTrace(); + } + return null; + } + + public OnBindingView(int layoutResId) { + super(layoutResId); + } + + public OnBindingView(int layoutResId, boolean async) { + super(layoutResId, async); + } + + public OnBindingView(View customView) { + super(customView); + } + + public OnBindingView(Fragment fragment) { + super(fragment); + } + + public OnBindingView(android.app.Fragment supportFragment) { + super(supportFragment); + } + + @Override + public void onBind(D dialog, View v) { + onBind(dialog, v, binding); + } + + public abstract void onBind(D dialog, View v, VB binding); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnDialogButtonClickListener.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnDialogButtonClickListener.java new file mode 100644 index 0000000..1265c82 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnDialogButtonClickListener.java @@ -0,0 +1,16 @@ +package com.kongzue.dialogx.interfaces; + +import android.view.View; + +/** + * Author: @Kongzue + * Github: https://github.com/kongzue/ + * Homepage: http://kongzue.com/ + * Mail: myzcxhh@live.cn + * CreateTime: 2019/3/29 18:44 + */ +public interface OnDialogButtonClickListener extends BaseOnDialogClickCallback{ + + boolean onClick(D dialog, View v); + +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnIconChangeCallBack.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnIconChangeCallBack.java new file mode 100644 index 0000000..e188342 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnIconChangeCallBack.java @@ -0,0 +1,26 @@ +package com.kongzue.dialogx.interfaces; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/9 14:54 + */ +public abstract class OnIconChangeCallBack { + + public OnIconChangeCallBack() { + } + + private Boolean autoTintIconInLightOrDarkMode; + + public OnIconChangeCallBack(boolean autoTintIconInLightOrDarkMode) { + this.autoTintIconInLightOrDarkMode = autoTintIconInLightOrDarkMode; + } + + public abstract int getIcon(D dialog, int index, String menuText); + + public Boolean isAutoTintIconInLightOrDarkMode() { + return autoTintIconInLightOrDarkMode; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnInputDialogButtonClickListener.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnInputDialogButtonClickListener.java new file mode 100644 index 0000000..3f4f7d8 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnInputDialogButtonClickListener.java @@ -0,0 +1,17 @@ +package com.kongzue.dialogx.interfaces; + +import android.view.View; + +import com.kongzue.dialogx.dialogs.MessageDialog; + +/** + * Author: @Kongzue + * Github: https://github.com/kongzue/ + * Homepage: http://kongzue.com/ + * Mail: myzcxhh@live.cn + * CreateTime: 2019/4/8 21:09 + */ +public interface OnInputDialogButtonClickListener extends BaseOnDialogClickCallback{ + + boolean onClick(D dialog, View v, String inputStr); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnMenuButtonClickListener.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnMenuButtonClickListener.java new file mode 100644 index 0000000..9f563c8 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnMenuButtonClickListener.java @@ -0,0 +1,8 @@ +package com.kongzue.dialogx.interfaces; + +import android.view.View; + +public interface OnMenuButtonClickListener extends BaseOnDialogClickCallback{ + + boolean onClick(D dialog, View v); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnMenuItemClickListener.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnMenuItemClickListener.java new file mode 100644 index 0000000..68e313e --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnMenuItemClickListener.java @@ -0,0 +1,12 @@ +package com.kongzue.dialogx.interfaces; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/10 6:26 + */ +public interface OnMenuItemClickListener { + boolean onClick(D dialog, CharSequence text, int index); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnMenuItemSelectListener.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnMenuItemSelectListener.java new file mode 100644 index 0000000..c0e5346 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnMenuItemSelectListener.java @@ -0,0 +1,37 @@ +package com.kongzue.dialogx.interfaces; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2021/4/11 19:18 + */ +public abstract class OnMenuItemSelectListener implements OnMenuItemClickListener { + + /** + * 警告:不建议重写此方法! + * 如果选择使用 OnMenuItemSelectListener 作为 BottomMenu 的回调,那么点击 Item 后,菜单默认不应该关闭, + * 若选择自行处理菜单点击 onClick,那么请务必 return true 作为返回值, + * 否则不会处理 onOneItemSelect 或 onMultiItemSelect 事件。 + * + * @param dialog BottomMenu实例 + * @param text 菜单文本 + * @param index 菜单索引值 + * @return return true:拦截自动关闭对话框;return false:点击后关闭对话框 + */ + @Deprecated + @Override + public boolean onClick(D dialog, CharSequence text, int index) { + return true; + } + + public void onOneItemSelect(D dialog, CharSequence text, int index, boolean select) { + + } + + public void onMultiItemSelect(D dialog, CharSequence[] text, int[] indexArray) { + + } + +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnSafeInsetsChangeListener.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnSafeInsetsChangeListener.java new file mode 100644 index 0000000..e0fda15 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnSafeInsetsChangeListener.java @@ -0,0 +1,15 @@ +package com.kongzue.dialogx.interfaces; + +import android.graphics.Rect; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/19 16:06 + */ +public interface OnSafeInsetsChangeListener { + + void onChange(Rect unsafeRect); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/PopMoveDisplacementInterceptor.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/PopMoveDisplacementInterceptor.java new file mode 100644 index 0000000..79a46aa --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/PopMoveDisplacementInterceptor.java @@ -0,0 +1,41 @@ +package com.kongzue.dialogx.interfaces; + +import android.animation.ValueAnimator; +import android.view.View; + +public abstract class PopMoveDisplacementInterceptor { + + /** + * 重置提示对话框新增时,旧的对话框让位位移的动画具体参数 + * + * @param index 对话框索引 + * @param dialog 对话框 + * @param fromY 从哪来 + * @param toY 往哪去 + * @param dialogHeight 对话框本身的高度 + * @param allTipSize 提示框总数 + * @param moveBack 是否向后位移 + * @return 你要改为往哪去 + */ + public float resetAnimY(int index, D dialog, float fromY, float toY, int dialogHeight, int allTipSize, boolean moveBack) { + return toY; + } + + /** + * 动画更新器 + * + * @param index 对话框索引 + * @param dialog 对话框 + * @param dialogBody 对话框内容布局 + * @param fromY 从哪来 + * @param toY 往哪去 + * @param progress 动画进度(0f~1f) + * @param animation 动画执行器 + * @param allTipSize 提示框总数 + * @param moveBack 是否向后位移 + * @return 返回true表示拦截处理,否则依然会执行原本的动画 + */ + public boolean animUpdater(int index, D dialog, View dialogBody, float fromY, float toY, float progress, ValueAnimator animation, int allTipSize, boolean moveBack) { + return false; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/SELECT_MODE.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/SELECT_MODE.java new file mode 100644 index 0000000..5f5dbc6 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/SELECT_MODE.java @@ -0,0 +1,5 @@ +package com.kongzue.dialogx.interfaces; + +public enum SELECT_MODE { + NONE, SINGLE, MULTIPLE +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/ScrollController.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/ScrollController.java new file mode 100644 index 0000000..8788b40 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/ScrollController.java @@ -0,0 +1,46 @@ +package com.kongzue.dialogx.interfaces; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2021/8/5 8:39 + */ +public interface ScrollController { + + /** + * 返回已滚动的距离 + * 若该距离为 0,BottomDialog、FullScreenDialog 将直接接管和衔接触控事件 + * 若距离不为 0,由本控件处理触控事件 + * + * @return 已滚动的距离 + */ + int getScrollDistance(); + + /** + * 设置是否可以滑动 + * 若可以滑动,BottomDialog、FullScreenDialog 将尝试接管和衔接触控事件, + * 若不可以滑动,BottomDialog、FullScreenDialog 将直接拦截触控事件 + * + * @return 是否可以滑动 + */ + boolean isCanScroll(); + + /** + * BottomDialog、FullScreenDialog 在接管触控事件时会通过此方法传入 lockScroll, + * 若 lockScroll 为真,请勿处理任何触摸事件。 + * + * 举例:建议增加以下代码: + * @Override + * public boolean onTouchEvent(MotionEvent event) { + * if (lockScroll) return false; + * return super.onTouchEvent(event); + * } + * + * @param lockScroll 是否锁定滑动 + */ + void lockScroll(boolean lockScroll); + + boolean isLockScroll(); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/style/MaterialStyle.java b/DialogX/src/main/java/com/kongzue/dialogx/style/MaterialStyle.java new file mode 100644 index 0000000..1ca0038 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/style/MaterialStyle.java @@ -0,0 +1,336 @@ +package com.kongzue.dialogx.style; + +import android.content.Context; + +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.ProgressViewInterface; +import com.kongzue.dialogx.util.views.ProgressView; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/26 13:09 + */ +public class MaterialStyle extends DialogXStyle { + + public static MaterialStyle style() { + return new MaterialStyle(); + } + + @Override + public int layout(boolean light) { + return light ? R.layout.layout_dialogx_material : R.layout.layout_dialogx_material_dark; + } + + @Override + public int enterAnimResId() { + return R.anim.anim_dialogx_default_enter; + } + + @Override + public int exitAnimResId() { + return R.anim.anim_dialogx_default_exit; + } + + @Override + public int[] verticalButtonOrder() { + return new int[]{BUTTON_OK, BUTTON_OTHER, BUTTON_CANCEL}; + } + + @Override + public int[] horizontalButtonOrder() { + return new int[]{BUTTON_OTHER, SPACE, BUTTON_CANCEL, BUTTON_OK}; + } + + @Override + public int splitWidthPx() { + return 1; + } + + @Override + public int splitColorRes(boolean light) { + return 0; + } + + @Override + public BlurBackgroundSetting messageDialogBlurSettings() { + return null; + } + + @Override + public HorizontalButtonRes overrideHorizontalButtonRes() { + return new DefaultHorizontalButtonRes(); + } + + public class DefaultHorizontalButtonRes extends HorizontalButtonRes { + @Override + public int overrideHorizontalOkButtonBackgroundRes(int visibleButtonCount, boolean light) { + return light ? R.drawable.button_dialogx_material_light : R.drawable.button_dialogx_material_night; + } + + @Override + public int overrideHorizontalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) { + return light ? R.drawable.button_dialogx_material_light : R.drawable.button_dialogx_material_night; + } + + @Override + public int overrideHorizontalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) { + return light ? R.drawable.button_dialogx_material_light : R.drawable.button_dialogx_material_night; + } + } + + @Override + public VerticalButtonRes overrideVerticalButtonRes() { + return new DefaultVerticalButtonRes(); + } + + public class DefaultVerticalButtonRes extends VerticalButtonRes { + @Override + public int overrideVerticalOkButtonBackgroundRes(int visibleButtonCount, boolean light) { + return light ? R.drawable.button_dialogx_material_light : R.drawable.button_dialogx_material_night; + } + + @Override + public int overrideVerticalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) { + return light ? R.drawable.button_dialogx_material_light : R.drawable.button_dialogx_material_night; + } + + @Override + public int overrideVerticalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) { + return light ? R.drawable.button_dialogx_material_light : R.drawable.button_dialogx_material_night; + } + } + + @Override + public WaitTipRes overrideWaitTipRes() { + return new DefaultWaitTipRes(); + } + + public class DefaultWaitTipRes extends WaitTipRes { + @Override + public int overrideWaitLayout(boolean light) { + return R.layout.layout_dialogx_wait; + } + + @Override + public int overrideRadiusPx() { + return -1; + } + + @Override + public boolean blurBackground() { + return false; + } + + @Override + public int overrideBackgroundColorRes(boolean light) { + return 0; + } + + @Override + public int overrideTextColorRes(boolean light) { + return light ? R.color.white : R.color.black; + } + + @Override + public ProgressViewInterface overrideWaitView(Context context, boolean light) { + return new ProgressView(context); + } + } + + @Override + public BottomDialogRes overrideBottomDialogRes() { + return new DefaultBottomDialogRes(); + } + + public class DefaultBottomDialogRes extends BottomDialogRes { + @Override + public boolean touchSlide() { + return true; + } + + @Override + public int overrideDialogLayout(boolean light) { + return light ? R.layout.layout_dialogx_bottom_material : R.layout.layout_dialogx_bottom_material_dark; + } + + @Override + public int overrideMenuDividerDrawableRes(boolean light) { + return light ? R.drawable.rect_dialogx_material_menu_split_divider : R.drawable.rect_dialogx_material_menu_split_divider_night; + } + + @Override + public int overrideMenuDividerHeight(boolean light) { + return 1; + } + + @Override + public int overrideMenuTextColor(boolean light) { + return light ? R.color.black90 : R.color.white90; + } + + @Override + public float overrideBottomDialogMaxHeight() { + return 0.6f; + } + + @Override + public int overrideMenuItemLayout(boolean light, int index, int count, boolean isContentVisibility) { + return 0; + } + + @Override + public int overrideSelectionMenuBackgroundColor(boolean light) { + return 0; + } + + @Override + public boolean selectionImageTint(boolean light) { + return false; + } + + @Override + public int overrideSelectionImage(boolean light, boolean isSelected) { + return isSelected ? R.mipmap.img_dialogx_bottom_menu_material_item_selection : R.mipmap.img_dialogx_bottom_menu_material_item_non_select; + } + + @Override + public int overrideMultiSelectionImage(boolean light, boolean isSelected) { + return isSelected ? R.mipmap.img_dialogx_bottom_menu_material_item_multi_selection : R.mipmap.img_dialogx_bottom_menu_material_item_non_multi_select; + } + } + + @Override + public PopTipSettings popTipSettings() { + return new DefaultPopTipSettings(); + } + + public class DefaultPopTipSettings extends PopTipSettings{ + @Override + public int layout(boolean light) { + return light ? R.layout.layout_dialogx_poptip_material : R.layout.layout_dialogx_poptip_material_dark; + } + + @Override + public ALIGN align() { + return ALIGN.BOTTOM; + } + + @Override + public int enterAnimResId(boolean light) { + return R.anim.anim_dialogx_default_enter; + } + + @Override + public int exitAnimResId(boolean light) { + return R.anim.anim_dialogx_default_exit; + } + + @Override + public boolean tintIcon() { + return true; + } + } + + @Override + public PopNotificationSettings popNotificationSettings() { + return new DefaultPopNotificationSettings(); + } + + public class DefaultPopNotificationSettings extends PopNotificationSettings{ + @Override + public int layout(boolean light) { + return light ? R.layout.layout_dialogx_popnotification_material : R.layout.layout_dialogx_popnotification_material_dark; + } + + @Override + public PopNotificationSettings.ALIGN align() { + return ALIGN.TOP; + } + + @Override + public int enterAnimResId(boolean light) { + return R.anim.anim_dialogx_notification_enter; + } + + @Override + public int exitAnimResId(boolean light) { + return R.anim.anim_dialogx_notification_exit; + } + + @Override + public boolean tintIcon() { + return false; + } + } + + @Override + public PopMenuSettings popMenuSettings() { + return new DefaultPopMenuSettings(); + } + + public class DefaultPopMenuSettings extends PopMenuSettings{ + @Override + public int layout(boolean light) { + return light ? R.layout.layout_dialogx_popmenu_material : R.layout.layout_dialogx_popmenu_material_dark; + } + + @Override + public BlurBackgroundSetting blurBackgroundSettings() { + return null; + } + + @Override + public int backgroundMaskColorRes() { + return 0; + } + + @Override + public int overrideMenuDividerDrawableRes(boolean b) { + return 0; + } + + @Override + public int overrideMenuDividerHeight(boolean b) { + return 0; + } + + @Override + public int overrideMenuTextColor(boolean light) { + return light ? R.color.black90 : R.color.white90; + } + + @Override + public int overrideMenuItemLayoutRes(boolean b) { + return 0; + } + + @Override + public int overrideMenuItemBackgroundRes(boolean b, int i, int i1, boolean b1) { + return 0; + } + + @Override + public int overrideSelectionMenuBackgroundColor(boolean b) { + return 0; + } + + @Override + public boolean selectionImageTint(boolean b) { + return false; + } + + @Override + public int paddingVertical() { + return 0; + } + } + + @Override + public boolean tintButtonBackground() { + return true; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/ActivityRunnable.java b/DialogX/src/main/java/com/kongzue/dialogx/util/ActivityRunnable.java new file mode 100644 index 0000000..4c789ec --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/ActivityRunnable.java @@ -0,0 +1,14 @@ +package com.kongzue.dialogx.util; + +import android.app.Activity; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2021/12/20 15:21 + */ +public interface ActivityRunnable { + void run(Activity activity); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/BottomDialogTouchEventInterceptor.java b/DialogX/src/main/java/com/kongzue/dialogx/util/BottomDialogTouchEventInterceptor.java new file mode 100644 index 0000000..9d1201c --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/BottomDialogTouchEventInterceptor.java @@ -0,0 +1,186 @@ +package com.kongzue.dialogx.util; + +import android.animation.ObjectAnimator; +import android.content.res.Resources; +import android.graphics.RectF; +import android.view.MotionEvent; +import android.view.View; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.dialogs.BottomDialog; +import com.kongzue.dialogx.interfaces.BottomDialogSlideEventLifecycleCallback; +import com.kongzue.dialogx.interfaces.ScrollController; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/7 4:01 + */ +public class BottomDialogTouchEventInterceptor { + + /** + * 下边三个值用于判断触控过程, + * isBkgTouched:标记是否已按下 + * bkgTouchDownY:记录起始触控位置 + * scrolledY:记录 ScrollView 已滚动过的距离,下次触控事件将接着上次的位置继续滑动 + * bkgOldY:记录按下时 bkg 的位置,用于区分松开手指时,bkg 移动的方向。 + */ + private boolean isBkgTouched = false; + private float bkgTouchDownY; + private float scrolledY; + private float bkgOldY; + private boolean onlyRestrictingSlideTouchEventsToScrollLayoutAreas = false; + /** + * 0:bkg接收触控事件,-1:scrollView进行滚动 + * 此标记的意义在于,当从 [scrollView滚动] 与 [bkg接收触控事件] 状态切换时, + * 需要对bkgTouchDownY、scrolledY的值进行刷新,否则触控连续过程会出现闪跳。 + */ + private int oldMode; + + public BottomDialogTouchEventInterceptor(BottomDialog me, BottomDialog.DialogImpl impl) { + refresh(me, impl); + } + + public void refresh(final BottomDialog me, final BottomDialog.DialogImpl impl) { + if (me == null || impl == null || impl.bkg == null || impl.scrollView == null) { + return; + } + /** + * BottomDialog 触控事件说明: + * bkg 将拦截并接管所有触控操作。 + * BottomDialog 的启动方式依据是内容布局高度是否大于可显示安全区域的高度。 + * bkg 会在合适的时机,直接接管控制 ScrollView 的滚动。 + * 因此,请确保内容布局的高度计算方式一定是按照内容高度计算, + * 即,请重写 onMeasure 方法: + * @Override + * protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + * int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); + * super.onMeasure(widthMeasureSpec, expandSpec); + * } + */ + View interceptTouchView = impl.bkg; + if (me.isAllowInterceptTouch()) { + if (isOnlyRestrictingSlideTouchEventsToScrollLayoutAreas()){ + impl.bkg.setOnTouchListener(null); + interceptTouchView = (View) impl.scrollView; + } + View finalInterceptTouchView = interceptTouchView; + interceptTouchView.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if (me.getDialogLifecycleCallback() instanceof BottomDialogSlideEventLifecycleCallback) { + if (((BottomDialogSlideEventLifecycleCallback) me.getDialogLifecycleCallback()).onSlideTouchEvent(me, v, event)) { + return true; + } + } + //这里 return 什么实际上无关紧要,重点在于 MaxRelativeLayout.java(dispatchTouchEvent:184) 的事件分发会独立触发此处的额外滑动事件 + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + bkgTouchDownY = event.getY(); + isBkgTouched = true; + bkgOldY = impl.boxBkg.getY(); + break; + case MotionEvent.ACTION_MOVE: + if (isBkgTouched && me.isAllowInterceptTouch()) { + float aimY = impl.boxBkg.getY() + event.getY() - bkgTouchDownY; + if (impl.scrollView.isCanScroll() && touchInScrollView(finalInterceptTouchView, impl.scrollView, event)) { + if (aimY > impl.boxRoot.getUnsafePlace().top) { + if (impl.scrollView.getScrollDistance() == 0) { + impl.scrollView.lockScroll(true); + impl.boxBkg.setY(aimY); + } else { + bkgTouchDownY = event.getY(); + } + } else { + impl.scrollView.lockScroll(false); + impl.boxBkg.setY(impl.boxRoot.getUnsafePlace().top); + } + } else { + if (aimY > impl.boxRoot.getUnsafePlace().top) { + impl.boxBkg.setY(aimY); + return true; + } else { + impl.boxBkg.setY(impl.boxRoot.getUnsafePlace().top); + } + } + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + scrolledY = impl.scrollView.getScrollDistance(); + isBkgTouched = false; + if (bkgOldY == impl.boxRoot.getUnsafePlace().top) { + if (impl.boxBkg.getY() > impl.boxRoot.getUnsafePlace().top + impl.bkgEnterAimY + DialogX.touchSlideTriggerThreshold) { + impl.preDismiss(); + } else if (impl.boxBkg.getY() != bkgOldY) { + ObjectAnimator enterAnim = ObjectAnimator.ofFloat(impl.boxBkg, "y", impl.boxBkg.getY(), + impl.bkgEnterAimY); + enterAnim.setDuration(300); + enterAnim.start(); + } + } else { + if (impl.boxBkg.getY() > bkgOldY + DialogX.touchSlideTriggerThreshold) { + impl.preDismiss(); + } else if (impl.boxBkg.getY() != bkgOldY) { + ObjectAnimator enterAnim = ObjectAnimator.ofFloat(impl.boxBkg, "y", impl.boxBkg.getY(), impl.boxRoot.getUnsafePlace().top); + enterAnim.setDuration(300); + enterAnim.start(); + } + } + if (impl.scrollView instanceof ScrollController) { + impl.scrollView.lockScroll(false); + } + break; + } + return false; + } + }); + } else { + if (impl.scrollView instanceof ScrollController) { + ((ScrollController) impl.scrollView).lockScroll(false); + } + interceptTouchView.setOnTouchListener(null); + } + } + + /** + * 检查 MotionEvent 触摸点是否在 scrollView 相对于对话框移动布局 slideView 范围内 + * + * @param slideView 对话框移动布局 + * @param scrollView 内部滚动布局 + * @param event 触摸事件 + * @return 是否在范围内 + */ + private boolean touchInScrollView(View slideView, ScrollController scrollView, MotionEvent event) { + View scrollViewImpl = (View) scrollView; + RectF scrollViewLocation = new RectF(); + int[] scrollViewScreenLoc = new int[2]; + int[] rootViewScreenLoc = new int[2]; + scrollViewImpl.getLocationInWindow(scrollViewScreenLoc); + slideView.getLocationInWindow(rootViewScreenLoc); + + scrollViewLocation.left = scrollViewScreenLoc[0] - rootViewScreenLoc[0]; + scrollViewLocation.top = scrollViewScreenLoc[1] - rootViewScreenLoc[1]; + scrollViewLocation.right = scrollViewLocation.left + scrollViewImpl.getWidth(); + scrollViewLocation.bottom = scrollViewLocation.top + scrollViewImpl.getHeight(); + + return event.getX() >= scrollViewLocation.left && event.getX() <= scrollViewLocation.right && + event.getY() >= scrollViewLocation.top && event.getY() <= scrollViewLocation.bottom; + } + + private int dip2px(float dpValue) { + final float scale = Resources.getSystem().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } + + public boolean isOnlyRestrictingSlideTouchEventsToScrollLayoutAreas() { + return onlyRestrictingSlideTouchEventsToScrollLayoutAreas; + } + + public BottomDialogTouchEventInterceptor setOnlyRestrictingSlideTouchEventsToScrollLayoutAreas(boolean onlyRestrictingSlideTouchEventsToScrollLayoutAreas) { + this.onlyRestrictingSlideTouchEventsToScrollLayoutAreas = onlyRestrictingSlideTouchEventsToScrollLayoutAreas; + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/BottomMenuArrayAdapter.java b/DialogX/src/main/java/com/kongzue/dialogx/util/BottomMenuArrayAdapter.java new file mode 100644 index 0000000..8d50b87 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/BottomMenuArrayAdapter.java @@ -0,0 +1,278 @@ +package com.kongzue.dialogx.util; + +import static com.kongzue.dialogx.interfaces.BaseDialog.isNull; +import static com.kongzue.dialogx.interfaces.BaseDialog.useTextInfo; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.os.Build; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.Space; +import android.widget.TextView; + +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.dialogs.BottomMenu; +import com.kongzue.dialogx.interfaces.MenuIconAdapter; +import com.kongzue.dialogx.interfaces.SELECT_MODE; + +import java.util.List; + +/** + * @author: Kongzue + * @github: 空祖Github + * @homepage: 主页 + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/7 0:00 + */ +public class BottomMenuArrayAdapter extends BaseAdapter { + + private BottomMenu bottomMenu; + public List objects; + public Context context; + + public BottomMenuArrayAdapter(BottomMenu bottomMenu, Context context, List objects) { + this.objects = objects; + this.context = context; + this.bottomMenu = bottomMenu; + } + + class ViewHolder { + ImageView imgDialogxMenuIcon; + ImageView imgDialogxMenuSelection; + TextView txtDialogxMenuText; + Space spaceDialogxRightPadding; + } + + @Override + public int getCount() { + return objects.size(); + } + + @Override + public CharSequence getItem(int position) { + return objects.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + TextInfo defaultMenuTextInfo; + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + ViewHolder viewHolder = null; + if (convertView == null) { + viewHolder = new ViewHolder(); + LayoutInflater mInflater = LayoutInflater.from(context); + + int resourceId = R.layout.item_dialogx_material_bottom_menu_normal_text; + if (bottomMenu.getStyle().overrideBottomDialogRes() != null) { + resourceId = bottomMenu.getStyle().overrideBottomDialogRes().overrideMenuItemLayout(bottomMenu.isLightTheme(), position, getCount(), false); + if (resourceId == 0) { + resourceId = R.layout.item_dialogx_material_bottom_menu_normal_text; + } else { + if (!isNull(bottomMenu.getTitle()) || !isNull(bottomMenu.getMessage()) || bottomMenu.getCustomView() != null) { + if (position == 0) { + resourceId = bottomMenu.getStyle().overrideBottomDialogRes().overrideMenuItemLayout(bottomMenu.isLightTheme(), position, getCount(), true); + } + } + } + } + convertView = mInflater.inflate(resourceId, null); + + viewHolder.imgDialogxMenuIcon = convertView.findViewById(R.id.img_dialogx_menu_icon); + viewHolder.imgDialogxMenuSelection = convertView.findViewById(R.id.img_dialogx_menu_selection); + viewHolder.txtDialogxMenuText = convertView.findViewById(R.id.txt_dialogx_menu_text); + viewHolder.spaceDialogxRightPadding = convertView.findViewById(R.id.space_dialogx_right_padding); + + convertView.setTag(viewHolder); + } else { + viewHolder = (ViewHolder) convertView.getTag(); + } + if (!bottomMenu.isMenuItemEnable(position)) { + convertView.setAlpha(0.4f); + } else { + convertView.setAlpha(1f); + } + if (bottomMenu.getSelectMode() == SELECT_MODE.SINGLE) { + if (viewHolder.imgDialogxMenuSelection != null) { + if (bottomMenu.getSelection() == position) { + viewHolder.imgDialogxMenuSelection.setVisibility(View.VISIBLE); + int overrideSelectionImageResId = bottomMenu.getStyle().overrideBottomDialogRes().overrideSelectionImage(bottomMenu.isLightTheme(), true); + if (overrideSelectionImageResId != 0) { + viewHolder.imgDialogxMenuSelection.setImageResource(overrideSelectionImageResId); + } + } else { + int overrideSelectionImageResId = bottomMenu.getStyle().overrideBottomDialogRes().overrideSelectionImage(bottomMenu.isLightTheme(), false); + if (overrideSelectionImageResId != 0) { + viewHolder.imgDialogxMenuSelection.setVisibility(View.VISIBLE); + viewHolder.imgDialogxMenuSelection.setImageResource(overrideSelectionImageResId); + } else { + viewHolder.imgDialogxMenuSelection.setVisibility(View.INVISIBLE); + } + } + } + } else if (bottomMenu.getSelectMode() == SELECT_MODE.MULTIPLE) { + if (viewHolder.imgDialogxMenuSelection != null) { + if (bottomMenu.getSelectionList().contains(position)) { + viewHolder.imgDialogxMenuSelection.setVisibility(View.VISIBLE); + int overrideSelectionImageResId = bottomMenu.getStyle().overrideBottomDialogRes().overrideMultiSelectionImage(bottomMenu.isLightTheme(), true); + if (overrideSelectionImageResId != 0) { + viewHolder.imgDialogxMenuSelection.setImageResource(overrideSelectionImageResId); + } + } else { + int overrideSelectionImageResId = bottomMenu.getStyle().overrideBottomDialogRes().overrideMultiSelectionImage(bottomMenu.isLightTheme(), false); + if (overrideSelectionImageResId != 0) { + viewHolder.imgDialogxMenuSelection.setVisibility(View.VISIBLE); + viewHolder.imgDialogxMenuSelection.setImageResource(overrideSelectionImageResId); + } else { + viewHolder.imgDialogxMenuSelection.setVisibility(View.INVISIBLE); + } + } + } + } else { + viewHolder.imgDialogxMenuSelection.setVisibility(View.GONE); + } + int overrideSelectionBackgroundColorRes = 0; + if (bottomMenu.getStyle().overrideBottomDialogRes() != null) { + overrideSelectionBackgroundColorRes = bottomMenu.getStyle().overrideBottomDialogRes().overrideSelectionMenuBackgroundColor(bottomMenu.isLightTheme()); + } + if (bottomMenu.getSelection() == position) { + //选中的背景变色 + if (overrideSelectionBackgroundColorRes != 0) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + convertView.setBackgroundTintList(ColorStateList.valueOf(context.getResources().getColor(overrideSelectionBackgroundColorRes))); + } + } + } + CharSequence text = objects.get(position); + + int textColor = bottomMenu.isLightTheme() ? R.color.black90 : R.color.white90; + if (bottomMenu.getStyle().overrideBottomDialogRes() != null) { + if (bottomMenu.getStyle().overrideBottomDialogRes().overrideMenuTextColor(bottomMenu.isLightTheme()) != 0) { + textColor = bottomMenu.getStyle().overrideBottomDialogRes().overrideMenuTextColor(bottomMenu.isLightTheme()); + } + } + + if (null != text) { + if (defaultMenuTextInfo == null) { + defaultMenuTextInfo = new TextInfo().setShowEllipsis(viewHolder.txtDialogxMenuText.getEllipsize() == TextUtils.TruncateAt.END).setFontColor(viewHolder.txtDialogxMenuText.getTextColors().getDefaultColor()).setBold(viewHolder.txtDialogxMenuText.getPaint().isFakeBoldText()).setFontSize(px2dip(viewHolder.txtDialogxMenuText.getTextSize())).setGravity(viewHolder.txtDialogxMenuText.getGravity()).setMaxLines(viewHolder.txtDialogxMenuText.getMaxLines()); + } + viewHolder.txtDialogxMenuText.setText(text); + viewHolder.txtDialogxMenuText.setTextColor(context.getResources().getColor(textColor)); + if (bottomMenu.getMenuItemTextInfoInterceptor() != null) { + TextInfo textInfo = bottomMenu.getMenuItemTextInfoInterceptor().menuItemTextInfo(bottomMenu, position, text.toString()); + if (textInfo != null) { + useTextInfo(viewHolder.txtDialogxMenuText, textInfo); + } else { + if (bottomMenu.getMenuTextInfo() != null) { + useTextInfo(viewHolder.txtDialogxMenuText, bottomMenu.getMenuTextInfo()); + } else { + useTextInfo(viewHolder.txtDialogxMenuText, defaultMenuTextInfo); + } + } + } else { + if (bottomMenu.getMenuTextInfo() != null) { + useTextInfo(viewHolder.txtDialogxMenuText, bottomMenu.getMenuTextInfo()); + } + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (viewHolder.imgDialogxMenuSelection != null) { + if (bottomMenu.getStyle().overrideBottomDialogRes() != null && bottomMenu.getStyle().overrideBottomDialogRes().selectionImageTint(bottomMenu.isLightTheme())) { + viewHolder.imgDialogxMenuSelection.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(textColor))); + } else { + viewHolder.imgDialogxMenuSelection.setImageTintList(null); + } + } + } + + if (bottomMenu.getOnIconChangeCallBack() != null) { + if (bottomMenu.getOnIconChangeCallBack() instanceof MenuIconAdapter) { + boolean result = ((MenuIconAdapter) bottomMenu.getOnIconChangeCallBack()).applyIcon(bottomMenu, position, text.toString(), viewHolder.imgDialogxMenuIcon); + boolean autoTintIconInLightOrDarkMode = bottomMenu.getOnIconChangeCallBack().isAutoTintIconInLightOrDarkMode() == null ? bottomMenu.isAutoTintIconInLightOrDarkMode() : bottomMenu.getOnIconChangeCallBack().isAutoTintIconInLightOrDarkMode(); + + viewHolder.imgDialogxMenuIcon.setVisibility(result ? View.VISIBLE : View.GONE); + if (result) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (autoTintIconInLightOrDarkMode) { + viewHolder.imgDialogxMenuIcon.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(textColor))); + } + } + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.VISIBLE); + } + } else { + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.GONE); + } + } + } else { + int resId = bottomMenu.getOnIconChangeCallBack().getIcon(bottomMenu, position, text.toString()); + boolean autoTintIconInLightOrDarkMode = bottomMenu.getOnIconChangeCallBack().isAutoTintIconInLightOrDarkMode() == null ? bottomMenu.isAutoTintIconInLightOrDarkMode() : bottomMenu.getOnIconChangeCallBack().isAutoTintIconInLightOrDarkMode(); + + if (resId != 0) { + viewHolder.imgDialogxMenuIcon.setVisibility(View.VISIBLE); + viewHolder.imgDialogxMenuIcon.setImageResource(resId); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.VISIBLE); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (autoTintIconInLightOrDarkMode) { + viewHolder.imgDialogxMenuIcon.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(textColor))); + } + } + } else { + viewHolder.imgDialogxMenuIcon.setVisibility(View.GONE); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.GONE); + } + } + } + } else { + if (bottomMenu.getIconResIds() != null) { + int resId = bottomMenu.getIconResIds(position); + boolean autoTintIconInLightOrDarkMode = bottomMenu.isAutoTintIconInLightOrDarkMode(); + + if (resId != 0) { + viewHolder.imgDialogxMenuIcon.setVisibility(View.VISIBLE); + viewHolder.imgDialogxMenuIcon.setImageResource(resId); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.VISIBLE); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (autoTintIconInLightOrDarkMode) { + viewHolder.imgDialogxMenuIcon.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(textColor))); + } + } + } else { + viewHolder.imgDialogxMenuIcon.setVisibility(View.GONE); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.GONE); + } + } + } else { + viewHolder.imgDialogxMenuIcon.setVisibility(View.GONE); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.GONE); + } + } + } + } + if (bottomMenu.getMenuMenuItemLayoutRefreshCallback() != null) { + bottomMenu.getMenuMenuItemLayoutRefreshCallback().getView(bottomMenu, position, convertView, parent); + } + return convertView; + } + + private int px2dip(float pxValue) { + final float scale = context.getResources().getDisplayMetrics().density; + return (int) (pxValue / scale + 0.5f); + } +} \ No newline at end of file diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/DialogListBuilder.java b/DialogX/src/main/java/com/kongzue/dialogx/util/DialogListBuilder.java new file mode 100644 index 0000000..e372de4 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/DialogListBuilder.java @@ -0,0 +1,74 @@ +package com.kongzue.dialogx.util; + +import com.kongzue.dialogx.interfaces.BaseDialog; + +import java.util.ArrayList; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2022/8/8 15:08 + */ +public class DialogListBuilder { + + ArrayList dialogs; + + public static DialogListBuilder create(BaseDialog... dialogs) { + DialogListBuilder builder = new DialogListBuilder(); + for (BaseDialog d : dialogs) { + if (d != null) builder.add(d); + } + return builder; + } + + public DialogListBuilder add(BaseDialog dialog) { + if (dialogs == null) { + dialogs = new ArrayList<>(); + } + if (dialog == null || dialog.isShow() || dialog.isPreShow()) { + return this; + } + dialog.setDialogListBuilder(this); + dialogs.add(dialog); + return this; + } + + public DialogListBuilder show() { + if (dialogs == null || dialogs.isEmpty()) { + return this; + } + while (!dialogs.isEmpty() && dialogs.get(0) == null) { + dialogs.remove(0); + } + dialogs.get(0).show(); + return this; + } + + public void showNext() { + if (dialogs == null || dialogs.isEmpty()) { + return; + } + while (!dialogs.isEmpty() && dialogs.get(0) == null) { + dialogs.remove(0); + } + if (!dialogs.isEmpty() && dialogs.get(0) != null) { + dialogs.get(0).show(); + } + } + + public boolean isEmpty() { + if (dialogs == null) { + return true; + } + while (!dialogs.isEmpty() && dialogs.get(0) == null) { + dialogs.remove(0); + } + return dialogs.isEmpty(); + } + + public void clear() { + dialogs.clear(); + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/DialogXFloatingWindowActivity.java b/DialogX/src/main/java/com/kongzue/dialogx/util/DialogXFloatingWindowActivity.java new file mode 100644 index 0000000..4db0f62 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/DialogXFloatingWindowActivity.java @@ -0,0 +1,145 @@ +package com.kongzue.dialogx.util; + +import android.app.Activity; +import android.os.Build; +import android.os.Bundle; +import android.view.MotionEvent; +import android.view.View; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; + +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BaseDialog; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2021/12/20 14:58 + */ +public class DialogXFloatingWindowActivity extends AppCompatActivity { + + static WeakReference dialogXFloatingWindowActivity; + int fromActivityHashCode; + List shownDialogXList = new ArrayList<>(); + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + dialogXFloatingWindowActivity = new WeakReference<>(this); + super.onCreate(savedInstanceState); + setContentView(R.layout.layout_dialogx_empty); + + int fromActivityUiStatus = getIntent().getIntExtra("fromActivityUiStatus", 0); + if (fromActivityUiStatus == 0) { + getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); + } else { + getWindow().getDecorView().setSystemUiVisibility(fromActivityUiStatus | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); + } + + setFromActivityHashCode(getIntent().getIntExtra("from", 0)); + String dialogXKey; + ActivityRunnable activityRunnable = BaseDialog.getActivityRunnable(dialogXKey = getIntent().getStringExtra("dialogXKey")); + if (activityRunnable == null) { + finish(); + } else { + shownDialogXList.add(dialogXKey); + activityRunnable.run(this); + } + + getWindow().getDecorView().setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() != MotionEvent.ACTION_CANCEL && getFromActivity() != null) { + if (getFromActivity() != null && !(getFromActivity() instanceof DialogXFloatingWindowActivity)) { + return getFromActivity().dispatchTouchEvent(event); + } + } + return false; + } + }); + } + + public boolean isSameFrom(int fromActivityHashCode) { + return fromActivityHashCode == this.fromActivityHashCode; + } + + public void showDialogX(String dialogXKey) { + ActivityRunnable activityRunnable = BaseDialog.getActivityRunnable(dialogXKey); + if (activityRunnable != null) { + shownDialogXList.add(dialogXKey); + activityRunnable.run(this); + } + } + + public int getFromActivityHashCode() { + return fromActivityHashCode; + } + + public DialogXFloatingWindowActivity setFromActivityHashCode(int fromActivityHashCode) { + this.fromActivityHashCode = fromActivityHashCode; + return this; + } + + public static DialogXFloatingWindowActivity getDialogXFloatingWindowActivity() { + if (dialogXFloatingWindowActivity == null) return null; + return dialogXFloatingWindowActivity.get(); + } + + public void finish(String dialogXKey) { + shownDialogXList.remove(dialogXKey); + if (shownDialogXList.isEmpty()) { + if (dialogXFloatingWindowActivity != null) { + dialogXFloatingWindowActivity.clear(); + } + dialogXFloatingWindowActivity = null; + super.finish(); + int version = Integer.valueOf(Build.VERSION.SDK_INT); + if (version > 5) { + overridePendingTransition(0, 0); + } + } + } + + public void finish() { + if (dialogXFloatingWindowActivity != null) { + dialogXFloatingWindowActivity.clear(); + } + dialogXFloatingWindowActivity = null; + super.finish(); + int version = Integer.valueOf(Build.VERSION.SDK_INT); + if (version > 5) { + overridePendingTransition(0, 0); + } + } + + boolean isScreenshot; + + public boolean isScreenshot() { + return isScreenshot; + } + + public DialogXFloatingWindowActivity setScreenshot(boolean screenshot) { + isScreenshot = screenshot; + return this; + } + + /** + * 原始用户操作界面 + */ + WeakReference fromActivity; + + public Activity getFromActivity() { + return fromActivity == null ? null : fromActivity.get(); + } + + public DialogXFloatingWindowActivity setFromActivity(Activity fromActivity) { + this.fromActivity = new WeakReference<>(fromActivity); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/DialogXImplModeAgent.java b/DialogX/src/main/java/com/kongzue/dialogx/util/DialogXImplModeAgent.java new file mode 100644 index 0000000..e6de8e7 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/DialogXImplModeAgent.java @@ -0,0 +1,51 @@ +package com.kongzue.dialogx.util; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.interfaces.BaseDialog; + +import java.lang.ref.WeakReference; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2022/5/17 15:16 + */ +public class DialogXImplModeAgent { + + private DialogX.IMPL_MODE implMode; + private WeakReference dialogWeakReference; + + public DialogXImplModeAgent(DialogX.IMPL_MODE implMode, BaseDialog dialog) { + this.implMode = implMode; + this.dialogWeakReference = new WeakReference<>(dialog); + } + + public DialogX.IMPL_MODE getImplMode() { + return implMode; + } + + public DialogXImplModeAgent setImplMode(DialogX.IMPL_MODE implMode) { + this.implMode = implMode; + return this; + } + + public BaseDialog getDialog() { + if (dialogWeakReference == null) { + return null; + } + return dialogWeakReference.get(); + } + + public DialogXImplModeAgent setDialogWeakReference(BaseDialog dialog) { + this.dialogWeakReference = new WeakReference<>(dialog); + return this; + } + + public void recycle() { + if (dialogWeakReference != null) dialogWeakReference.clear(); + dialogWeakReference = null; + implMode = null; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/DialogXValueAnimator.java b/DialogX/src/main/java/com/kongzue/dialogx/util/DialogXValueAnimator.java new file mode 100644 index 0000000..e0ab595 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/DialogXValueAnimator.java @@ -0,0 +1,200 @@ +package com.kongzue.dialogx.util; + +import android.os.Handler; +import android.os.Looper; +import android.view.animation.Interpolator; + +public class DialogXValueAnimator { + + public static final int RESTART = 1; + public static final int REVERSE = 2; + public static final int INFINITE = -1; + + Handler handler = new Handler(Looper.getMainLooper()); + + private long duration; + private long startTime; + private boolean isRunning = false; + private ValueUpdateListener listener; + private Interpolator interpolator; + private float startValue; + private float endValue; + private int repeatCount = 0; + private int currentRepeatCount = 0; + private int refreshInterval = 16; // 控制更新频率,默认 60 FPS + + public static DialogXValueAnimator ofFloat(float start, float end){ + return new DialogXValueAnimator(start, end); + } + + public DialogXValueAnimator(float startValue, float endValue) { + this.startValue = startValue; + this.endValue = endValue; + } + + public void setFloatValues(float start, float end) { + startValue = start; + endValue = end; + } + + public void addUpdateListener(ValueUpdateListener listener) { + this.listener = listener; + } + + public void start() { + if (isRunning) { + return; + } + + isRunning = true; + startTime = System.currentTimeMillis(); + + // 开启一个线程用于更新动画 + new Thread(new Runnable() { + @Override + public void run() { + while (isRunning) { + long currentTime = System.currentTimeMillis(); + long elapsed = currentTime - startTime; + + if (elapsed < duration) { + float fraction = (float) elapsed / duration; + + // 使用插值器处理动画进度 + if (interpolator != null) { + fraction = interpolator.getInterpolation(fraction); + } + + float animatedValue = startValue + fraction * (endValue - startValue); + + // 通知监听器 + if (listener != null) { + getHandler().post(new Runnable() { + @Override + public void run() { + listener.onValueUpdate(animatedValue); + } + }); + } + } else { + // 动画结束 + isRunning = false; + + onAnimationEnd(); + + // 处理重复播放 + if (repeatCount == INFINITE || currentRepeatCount < repeatCount) { + currentRepeatCount++; + startTime = System.currentTimeMillis(); + isRunning = true; + } + } + + try { + Thread.sleep(refreshInterval); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + }).start(); + } + + private Handler getHandler() { + if (handler==null) { + handler = new Handler(Looper.getMainLooper()); + } + return handler; + } + + private void onAnimationEnd() { + + } + + public long getDuration() { + return duration; + } + + public DialogXValueAnimator setDuration(long duration) { + this.duration = duration; + return this; + } + + public long getStartTime() { + return startTime; + } + + public DialogXValueAnimator setStartTime(long startTime) { + this.startTime = startTime; + return this; + } + + public ValueUpdateListener getListener() { + return listener; + } + + public DialogXValueAnimator setListener(ValueUpdateListener listener) { + this.listener = listener; + return this; + } + + public float getStartValue() { + return startValue; + } + + public DialogXValueAnimator setStartValue(float startValue) { + this.startValue = startValue; + return this; + } + + public float getEndValue() { + return endValue; + } + + public DialogXValueAnimator setEndValue(float endValue) { + this.endValue = endValue; + return this; + } + + public void cancel() { + isRunning = false; + } + + public void setInterpolator(Interpolator interpolator) { + this.interpolator = interpolator; + } + + public void setRepeatCount(int repeatCount) { + this.repeatCount = repeatCount; + } + + // 监听器接口 + public interface ValueUpdateListener { + void onValueUpdate(float animatedValue); + } + + public Interpolator getInterpolator() { + return interpolator; + } + + public boolean isRunning() { + return isRunning; + } + + public int getRepeatCount() { + return repeatCount; + } + + public int getCurrentRepeatCount() { + return currentRepeatCount; + } + + public int getRefreshInterval() { + return refreshInterval; + } + + public DialogXValueAnimator setRefreshInterval(int refreshInterval) { + this.refreshInterval = refreshInterval; + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/DialogXViewLoc.java b/DialogX/src/main/java/com/kongzue/dialogx/util/DialogXViewLoc.java new file mode 100644 index 0000000..b175546 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/DialogXViewLoc.java @@ -0,0 +1,88 @@ +package com.kongzue.dialogx.util; + +public class DialogXViewLoc { + + public static boolean skipErrorLoc = true; + + private float x; + private float y; + private float w; + private float h; + + public float getX() { + return x; + } + + public DialogXViewLoc setX(float x) { + this.x = x; + return this; + } + + public float getY() { + return y; + } + + public DialogXViewLoc setY(float y) { + this.y = y; + return this; + } + + public float getW() { + return w; + } + + public DialogXViewLoc setW(float w) { + this.w = w; + return this; + } + + public float getH() { + return h; + } + + public DialogXViewLoc setH(float h) { + this.h = h; + return this; + } + + public boolean isSameLoc(int[] loc) { + if (loc.length == 2) { + return x == loc[0] && y == loc[1]; + } + if (loc.length == 4) { + return x == loc[0] && y == loc[1] && w == loc[2] && h == loc[3]; + } + return false; + } + + public void set(int[] loc) { + if (loc.length == 2) { + if (skipErrorLoc) { + if (loc[0] != 0) { + x = loc[0]; + } + if (loc[1] != 0) { + y = loc[1]; + } + } else { + x = loc[0]; + y = loc[1]; + } + } + if (loc.length == 4) { + if (skipErrorLoc) { + if (loc[0] != 0) { + x = loc[0]; + } + if (loc[1] != 0) { + y = loc[1]; + } + } else { + x = loc[0]; + y = loc[1]; + } + w = loc[2]; + h = loc[3]; + } + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/FullScreenDialogTouchEventInterceptor.java b/DialogX/src/main/java/com/kongzue/dialogx/util/FullScreenDialogTouchEventInterceptor.java new file mode 100644 index 0000000..588dfea --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/FullScreenDialogTouchEventInterceptor.java @@ -0,0 +1,160 @@ +package com.kongzue.dialogx.util; + +import android.animation.ObjectAnimator; +import android.content.res.Resources; +import android.graphics.RectF; +import android.view.MotionEvent; +import android.view.View; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.dialogs.FullScreenDialog; +import com.kongzue.dialogx.interfaces.ScrollController; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/19 13:54 + */ +public class FullScreenDialogTouchEventInterceptor { + + /** + * 下边三个值用于判断触控过程, + * isBkgTouched:标记是否已按下 + * bkgTouchDownY:记录起始触控位置 + * scrolledY:记录 ScrollView 已滚动过的距离,下次触控事件将接着上次的位置继续滑动 + * bkgOldY:记录按下时 bkg 的位置,用于区分松开手指时,bkg 移动的方向。 + */ + private boolean isBkgTouched = false; + private float bkgTouchDownY; + private float bkgOldY; + + public FullScreenDialogTouchEventInterceptor(FullScreenDialog me, FullScreenDialog.DialogImpl impl) { + refresh(me, impl); + } + + public void refresh(final FullScreenDialog me, final FullScreenDialog.DialogImpl impl) { + if (me == null || impl == null || impl.bkg == null) { + return; + } + if (me.isAllowInterceptTouch()) { + View touchView = impl.boxCustom; + if (impl.scrollView != null) { + touchView = impl.bkg; + } + touchView.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + bkgTouchDownY = event.getY(); + isBkgTouched = true; + bkgOldY = impl.bkg.getY(); + break; + case MotionEvent.ACTION_MOVE: + if (isBkgTouched) { + float aimY = impl.bkg.getY() + event.getY() - bkgTouchDownY; + if (impl.scrollView != null && impl.scrollView.isCanScroll() && touchInScrollView(v, impl.scrollView, event)) { + if (aimY > me.getDialogImpl().getEnterY()) { + if (impl.scrollView.getScrollDistance() == 0) { + if (impl.scrollView instanceof ScrollController) { + ((ScrollController) impl.scrollView).lockScroll(true); + } + impl.bkg.setY(aimY); + } else { + bkgTouchDownY = event.getY(); + } + } else { + if (impl.scrollView instanceof ScrollController) { + ((ScrollController) impl.scrollView).lockScroll(false); + } + aimY = (me.getDialogImpl().getEnterY()); + impl.bkg.setY(aimY); + } + } else { + if (aimY < me.getDialogImpl().getEnterY()) { + aimY = me.getDialogImpl().getEnterY(); + } + impl.bkg.setY(aimY); + } + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + isBkgTouched = false; + if (bkgOldY == me.getDialogImpl().getEnterY()) { + if (impl.bkg.getY() < DialogX.touchSlideTriggerThreshold) { + ObjectAnimator enterAnim = ObjectAnimator.ofFloat(impl.bkg, "y", impl.bkg.getY(), me.getDialogImpl().getEnterY()); + enterAnim.setDuration(300); + enterAnim.start(); + } else if (impl.bkg.getY() > impl.getEnterY() + DialogX.touchSlideTriggerThreshold) { + impl.preDismiss(); + } else { + ObjectAnimator enterAnim = ObjectAnimator.ofFloat(impl.bkg, "y", impl.bkg.getY(), impl.getEnterY()); + enterAnim.setDuration(300); + enterAnim.start(); + } + } else { + if (impl.bkg.getY() < bkgOldY - DialogX.touchSlideTriggerThreshold) { + ObjectAnimator enterAnim = ObjectAnimator.ofFloat(impl.bkg, "y", impl.bkg.getY(), me.getDialogImpl().getEnterY()); + enterAnim.setDuration(300); + enterAnim.start(); + } else if (impl.bkg.getY() > bkgOldY + DialogX.touchSlideTriggerThreshold) { + impl.preDismiss(); + } else { + ObjectAnimator enterAnim = ObjectAnimator.ofFloat(impl.bkg, "y", impl.bkg.getY(), impl.getEnterY()); + enterAnim.setDuration(300); + enterAnim.start(); + } + } + if (impl.scrollView instanceof ScrollController) { + impl.scrollView.lockScroll(false); + } + break; + } + return false; + } + }); + } else { + View touchView = impl.boxCustom; + if (impl.scrollView != null) { + touchView = impl.bkg; + } + if (impl.scrollView instanceof ScrollController) { + ((ScrollController) impl.scrollView).lockScroll(false); + } + touchView.setOnTouchListener(null); + } + } + + /** + * 检查 MotionEvent 触摸点是否在 scrollView 相对于对话框移动布局 slideView 范围内 + * + * @param slideView 对话框移动布局 + * @param scrollView 内部滚动布局 + * @param event 触摸事件 + * @return 是否在范围内 + */ + private boolean touchInScrollView(View slideView, ScrollController scrollView, MotionEvent event) { + View scrollViewImpl = (View) scrollView; + RectF scrollViewLocation = new RectF(); + int[] scrollViewScreenLoc = new int[2]; + int[] rootViewScreenLoc = new int[2]; + scrollViewImpl.getLocationInWindow(scrollViewScreenLoc); + slideView.getLocationInWindow(rootViewScreenLoc); + + scrollViewLocation.left = scrollViewScreenLoc[0] - rootViewScreenLoc[0]; + scrollViewLocation.top = scrollViewScreenLoc[1] - rootViewScreenLoc[1]; + scrollViewLocation.right = scrollViewLocation.left + scrollViewImpl.getWidth(); + scrollViewLocation.bottom = scrollViewLocation.top + scrollViewImpl.getHeight(); + + return event.getX() >= scrollViewLocation.left && event.getX() <= scrollViewLocation.right && + event.getY() >= scrollViewLocation.top && event.getY() <= scrollViewLocation.bottom; + } + + private int dip2px(float dpValue) { + final float scale = Resources.getSystem().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/IOSMenuArrayAdapter.java b/DialogX/src/main/java/com/kongzue/dialogx/util/IOSMenuArrayAdapter.java new file mode 100644 index 0000000..1c38896 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/IOSMenuArrayAdapter.java @@ -0,0 +1,19 @@ +package com.kongzue.dialogx.util; + +import android.content.Context; +import android.widget.ArrayAdapter; + +import androidx.annotation.NonNull; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/10 19:56 + */ +public class IOSMenuArrayAdapter extends ArrayAdapter { + public IOSMenuArrayAdapter(@NonNull Context context, int resource) { + super(context, resource); + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/InputInfo.java b/DialogX/src/main/java/com/kongzue/dialogx/util/InputInfo.java new file mode 100644 index 0000000..68512ca --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/InputInfo.java @@ -0,0 +1,128 @@ +package com.kongzue.dialogx.util; + +import android.text.InputFilter; + +import androidx.annotation.ColorInt; + +import java.util.Arrays; + +/** + * Author: @Kongzue + * Github: https://github.com/kongzue/ + * Homepage: http://kongzue.com/ + * Mail: myzcxhh@live.cn + * CreateTime: 2018/11/8 21:41 + */ +public class InputInfo { + + private int MAX_LENGTH = -1; //最大长度,-1不生效 + private int inputType; //类型详见 android.text.InputType + private TextInfo textInfo; //默认字体样式 + private boolean multipleLines; //支持多行 + private boolean selectAllText; //默认选中所有文字(便于修改) + + private Integer cursorColor; //输入框光标颜色 + private Integer bottomLineColor;//输入框横线颜色 + + private InputFilter[] inputFilters; //输入过滤器 + + public int getMAX_LENGTH() { + return MAX_LENGTH; + } + + public InputInfo setMAX_LENGTH(int MAX_LENGTH) { + this.MAX_LENGTH = MAX_LENGTH; + return this; + } + + public int getInputType() { + return inputType; + } + + public InputInfo setInputType(int inputType) { + this.inputType = inputType; + return this; + } + + public TextInfo getTextInfo() { + return textInfo; + } + + public InputInfo setTextInfo(TextInfo textInfo) { + this.textInfo = textInfo; + return this; + } + + public boolean isMultipleLines() { + return multipleLines; + } + + public InputInfo setMultipleLines(boolean multipleLines) { + this.multipleLines = multipleLines; + return this; + } + + public boolean isSelectAllText() { + return selectAllText; + } + + public InputInfo setSelectAllText(boolean selectAllText) { + this.selectAllText = selectAllText; + return this; + } + + public Integer getCursorColor() { + return cursorColor; + } + + public InputInfo setCursorColor(@ColorInt int cursorColor) { + this.cursorColor = cursorColor; + return this; + } + + public InputInfo setThemeColor(@ColorInt int themeColor) { + this.cursorColor = themeColor; + this.bottomLineColor = themeColor; + return this; + } + + public Integer getBottomLineColor() { + return bottomLineColor; + } + + public InputInfo setBottomLineColor(int bottomLineColor) { + this.bottomLineColor = bottomLineColor; + return this; + } + + public InputFilter[] getInputFilters() { + return inputFilters; + } + + public InputInfo setInputFilters(InputFilter[] inputFilters) { + this.inputFilters = inputFilters; + return this; + } + + public InputInfo addInputFilter(InputFilter inputFilter) { + if (this.inputFilters == null) { + this.inputFilters = new InputFilter[]{inputFilter}; + } else { + this.inputFilters = Arrays.copyOf(this.inputFilters, this.inputFilters.length + 1); + this.inputFilters[this.inputFilters.length - 1] = inputFilter; + } + return this; + } + + public InputInfo removeInputFilter(InputFilter inputFilter) { + if (this.inputFilters!= null) { + for (int i = 0; i < this.inputFilters.length; i++) { + if (this.inputFilters[i] == inputFilter) { + this.inputFilters = Arrays.copyOf(this.inputFilters, this.inputFilters.length - 1); + return this; + } + } + } + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/ItemDivider.java b/DialogX/src/main/java/com/kongzue/dialogx/util/ItemDivider.java new file mode 100644 index 0000000..1b8c185 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/ItemDivider.java @@ -0,0 +1,96 @@ +package com.kongzue.dialogx.util; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.graphics.drawable.InsetDrawable; + +public class ItemDivider { + + private int left; //左边距(dp) + private int right; //右边距(dp) + private int width = 1; //分割线宽度(px) + private int[] color = {0xFFDFE1E5, 0xFF3A3A3A}; //颜色 + + public ItemDivider() { + } + + public ItemDivider(int left, int right, int width) { + this.left = left; + this.right = right; + this.width = width; + } + + public int getLeft() { + return left; + } + + public ItemDivider setLeft(int left) { + this.left = left; + return this; + } + + public int getRight() { + return right; + } + + public ItemDivider setRight(int right) { + this.right = right; + return this; + } + + public int getWidth() { + return width; + } + + public ItemDivider setWidth(int width) { + this.width = width; + return this; + } + + public int getColor(boolean light) { + return light ? color[0] : color[1]; + } + + public ItemDivider setColor(boolean light, int color) { + if (light) { + this.color[0] = color; + } else { + this.color[1] = color; + } + return this; + } + + public ItemDivider setColor(int color) { + this.color = new int[]{color, color}; + return this; + } + + @Override + public String toString() { + return "ItemDivider{" + + "left(dp)=" + left + + ", right(dp)=" + right + + ", width(px)=" + width + + ", color(light)=" + String.format("#%06X", (0xFFFFFF & color[0])) + + ", color(night)=" + String.format("#%06X", (0xFFFFFF & color[1])) + + '}'; + } + + public Drawable createDividerDrawable(Context c, boolean light) { + GradientDrawable dividerShape = new GradientDrawable(); + dividerShape.setShape(GradientDrawable.RECTANGLE); + dividerShape.setColor(getColor(light)); + + InsetDrawable insetDivider = new InsetDrawable( + dividerShape, + dip2px(c, left), 0, dip2px(c, right), 0 + ); + return insetDivider; + } + + private int dip2px(Context c, float dpValue) { + final float scale = c.getResources().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/MessageMenuArrayAdapter.java b/DialogX/src/main/java/com/kongzue/dialogx/util/MessageMenuArrayAdapter.java new file mode 100644 index 0000000..2b9754f --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/MessageMenuArrayAdapter.java @@ -0,0 +1,270 @@ +package com.kongzue.dialogx.util; + +import static com.kongzue.dialogx.interfaces.BaseDialog.isNull; +import static com.kongzue.dialogx.interfaces.BaseDialog.useTextInfo; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.os.Build; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.Space; +import android.widget.TextView; + +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.dialogs.MessageMenu; +import com.kongzue.dialogx.interfaces.MenuIconAdapter; +import com.kongzue.dialogx.interfaces.SELECT_MODE; + +import java.util.List; + +public class MessageMenuArrayAdapter extends BaseAdapter { + private MessageMenu messageMenu; + public List objects; + public Context context; + + public MessageMenuArrayAdapter(MessageMenu messageMenu, Context context, List objects) { + this.objects = objects; + this.context = context; + this.messageMenu = messageMenu; + } + + class ViewHolder { + ImageView imgDialogxMenuIcon; + ImageView imgDialogxMenuSelection; + TextView txtDialogxMenuText; + Space spaceDialogxRightPadding; + } + + @Override + public int getCount() { + return objects.size(); + } + + @Override + public CharSequence getItem(int position) { + return objects.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + TextInfo defaultMenuTextInfo; + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + MessageMenuArrayAdapter.ViewHolder viewHolder = null; + if (convertView == null) { + viewHolder = new MessageMenuArrayAdapter.ViewHolder(); + LayoutInflater mInflater = LayoutInflater.from(context); + + int resourceId = R.layout.item_dialogx_material_bottom_menu_normal_text; + if (messageMenu.getStyle().overrideBottomDialogRes() != null) { + resourceId = messageMenu.getStyle().overrideBottomDialogRes().overrideMenuItemLayout(messageMenu.isLightTheme(), position, getCount(), false); + if (resourceId == 0) { + resourceId = R.layout.item_dialogx_material_bottom_menu_normal_text; + } else { + if (!isNull(messageMenu.getTitle()) || !isNull(messageMenu.getMessage()) || messageMenu.getCustomView() != null) { + if (position == 0) { + resourceId = messageMenu.getStyle().overrideBottomDialogRes().overrideMenuItemLayout(messageMenu.isLightTheme(), position, getCount(), true); + } + } + } + } + convertView = mInflater.inflate(resourceId, null); + + viewHolder.imgDialogxMenuIcon = convertView.findViewById(R.id.img_dialogx_menu_icon); + viewHolder.imgDialogxMenuSelection = convertView.findViewById(R.id.img_dialogx_menu_selection); + viewHolder.txtDialogxMenuText = convertView.findViewById(R.id.txt_dialogx_menu_text); + viewHolder.spaceDialogxRightPadding = convertView.findViewById(R.id.space_dialogx_right_padding); + + convertView.setTag(viewHolder); + } else { + viewHolder = (MessageMenuArrayAdapter.ViewHolder) convertView.getTag(); + } + if (!messageMenu.isMenuItemEnable(position)) { + convertView.setAlpha(0.4f); + } else { + convertView.setAlpha(1f); + } + if (messageMenu.getSelectMode() == SELECT_MODE.SINGLE) { + if (viewHolder.imgDialogxMenuSelection != null) { + if (messageMenu.getSelection() == position) { + viewHolder.imgDialogxMenuSelection.setVisibility(View.VISIBLE); + int overrideSelectionImageResId = messageMenu.getStyle().overrideBottomDialogRes().overrideSelectionImage(messageMenu.isLightTheme(), true); + if (overrideSelectionImageResId != 0) { + viewHolder.imgDialogxMenuSelection.setImageResource(overrideSelectionImageResId); + } + } else { + int overrideSelectionImageResId = messageMenu.getStyle().overrideBottomDialogRes().overrideSelectionImage(messageMenu.isLightTheme(), false); + if (overrideSelectionImageResId != 0) { + viewHolder.imgDialogxMenuSelection.setVisibility(View.VISIBLE); + viewHolder.imgDialogxMenuSelection.setImageResource(overrideSelectionImageResId); + } else { + viewHolder.imgDialogxMenuSelection.setVisibility(View.INVISIBLE); + } + } + } + } else if (messageMenu.getSelectMode() == SELECT_MODE.MULTIPLE) { + if (viewHolder.imgDialogxMenuSelection != null) { + if (messageMenu.getSelectionList().contains(position)) { + viewHolder.imgDialogxMenuSelection.setVisibility(View.VISIBLE); + int overrideSelectionImageResId = messageMenu.getStyle().overrideBottomDialogRes().overrideMultiSelectionImage(messageMenu.isLightTheme(), true); + if (overrideSelectionImageResId != 0) { + viewHolder.imgDialogxMenuSelection.setImageResource(overrideSelectionImageResId); + } + } else { + int overrideSelectionImageResId = messageMenu.getStyle().overrideBottomDialogRes().overrideMultiSelectionImage(messageMenu.isLightTheme(), false); + if (overrideSelectionImageResId != 0) { + viewHolder.imgDialogxMenuSelection.setVisibility(View.VISIBLE); + viewHolder.imgDialogxMenuSelection.setImageResource(overrideSelectionImageResId); + } else { + viewHolder.imgDialogxMenuSelection.setVisibility(View.INVISIBLE); + } + } + } + } else { + viewHolder.imgDialogxMenuSelection.setVisibility(View.GONE); + } + int overrideSelectionBackgroundColorRes = 0; + if (messageMenu.getStyle().overrideBottomDialogRes() != null) { + overrideSelectionBackgroundColorRes = messageMenu.getStyle().overrideBottomDialogRes().overrideSelectionMenuBackgroundColor(messageMenu.isLightTheme()); + } + if (messageMenu.getSelection() == position) { + //选中的背景变色 + if (overrideSelectionBackgroundColorRes != 0) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + convertView.setBackgroundTintList(ColorStateList.valueOf(context.getResources().getColor(overrideSelectionBackgroundColorRes))); + } + } + } + CharSequence text = objects.get(position); + + int textColor = messageMenu.isLightTheme() ? R.color.black90 : R.color.white90; + if (messageMenu.getStyle().overrideBottomDialogRes() != null) { + if (messageMenu.getStyle().overrideBottomDialogRes().overrideMenuTextColor(messageMenu.isLightTheme()) != 0) { + textColor = messageMenu.getStyle().overrideBottomDialogRes().overrideMenuTextColor(messageMenu.isLightTheme()); + } + } + + if (null != text) { + if (defaultMenuTextInfo == null) { + defaultMenuTextInfo = new TextInfo().setShowEllipsis(viewHolder.txtDialogxMenuText.getEllipsize() == TextUtils.TruncateAt.END).setFontColor(viewHolder.txtDialogxMenuText.getTextColors().getDefaultColor()).setBold(viewHolder.txtDialogxMenuText.getPaint().isFakeBoldText()).setFontSize(px2dip(viewHolder.txtDialogxMenuText.getTextSize())).setGravity(viewHolder.txtDialogxMenuText.getGravity()).setMaxLines(viewHolder.txtDialogxMenuText.getMaxLines()); + } + viewHolder.txtDialogxMenuText.setText(text); + viewHolder.txtDialogxMenuText.setTextColor(context.getResources().getColor(textColor)); + if (messageMenu.getMenuItemTextInfoInterceptor() != null) { + TextInfo textInfo = messageMenu.getMenuItemTextInfoInterceptor().menuItemTextInfo(messageMenu, position, text.toString()); + if (textInfo != null) { + useTextInfo(viewHolder.txtDialogxMenuText, textInfo); + } else { + if (messageMenu.getMenuTextInfo() != null) { + useTextInfo(viewHolder.txtDialogxMenuText, messageMenu.getMenuTextInfo()); + } else { + useTextInfo(viewHolder.txtDialogxMenuText, defaultMenuTextInfo); + } + } + } else { + if (messageMenu.getMenuTextInfo() != null) { + useTextInfo(viewHolder.txtDialogxMenuText, messageMenu.getMenuTextInfo()); + } + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (viewHolder.imgDialogxMenuSelection != null) { + if (messageMenu.getStyle().overrideBottomDialogRes() != null && messageMenu.getStyle().overrideBottomDialogRes().selectionImageTint(messageMenu.isLightTheme())) { + viewHolder.imgDialogxMenuSelection.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(textColor))); + } else { + viewHolder.imgDialogxMenuSelection.setImageTintList(null); + } + } + } + + if (messageMenu.getOnIconChangeCallBack() != null) { + if (messageMenu.getOnIconChangeCallBack() instanceof MenuIconAdapter) { + boolean result = ((MenuIconAdapter) messageMenu.getOnIconChangeCallBack()).applyIcon(messageMenu, position, text.toString(), viewHolder.imgDialogxMenuIcon); + boolean autoTintIconInLightOrDarkMode = messageMenu.getOnIconChangeCallBack().isAutoTintIconInLightOrDarkMode() == null ? messageMenu.isAutoTintIconInLightOrDarkMode() : messageMenu.getOnIconChangeCallBack().isAutoTintIconInLightOrDarkMode(); + + viewHolder.imgDialogxMenuIcon.setVisibility(result ? View.VISIBLE : View.GONE); + if (result) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (autoTintIconInLightOrDarkMode) { + viewHolder.imgDialogxMenuIcon.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(textColor))); + } + } + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.VISIBLE); + } + } else { + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.GONE); + } + } + } else { + int resId = messageMenu.getOnIconChangeCallBack().getIcon(messageMenu, position, text.toString()); + boolean autoTintIconInLightOrDarkMode = messageMenu.getOnIconChangeCallBack().isAutoTintIconInLightOrDarkMode() == null ? messageMenu.isAutoTintIconInLightOrDarkMode() : messageMenu.getOnIconChangeCallBack().isAutoTintIconInLightOrDarkMode(); + + if (resId != 0) { + viewHolder.imgDialogxMenuIcon.setVisibility(View.VISIBLE); + viewHolder.imgDialogxMenuIcon.setImageResource(resId); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.VISIBLE); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (autoTintIconInLightOrDarkMode) { + viewHolder.imgDialogxMenuIcon.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(textColor))); + } + } + } else { + viewHolder.imgDialogxMenuIcon.setVisibility(View.GONE); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.GONE); + } + } + } + } else { + if (messageMenu.getIconResIds() != null) { + int resId = messageMenu.getIconResIds(position); + boolean autoTintIconInLightOrDarkMode = messageMenu.isAutoTintIconInLightOrDarkMode(); + + if (resId != 0) { + viewHolder.imgDialogxMenuIcon.setVisibility(View.VISIBLE); + viewHolder.imgDialogxMenuIcon.setImageResource(resId); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.VISIBLE); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (autoTintIconInLightOrDarkMode) { + viewHolder.imgDialogxMenuIcon.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(textColor))); + } + } + } else { + viewHolder.imgDialogxMenuIcon.setVisibility(View.GONE); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.GONE); + } + } + } else { + viewHolder.imgDialogxMenuIcon.setVisibility(View.GONE); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.GONE); + } + } + } + } + if (messageMenu.getMenuMenuItemLayoutRefreshCallback() != null) { + messageMenu.getMenuMenuItemLayoutRefreshCallback().getView(messageMenu, position, convertView, parent); + } + return convertView; + } + + private int px2dip(float pxValue) { + final float scale = context.getResources().getDisplayMetrics().density; + return (int) (pxValue / scale + 0.5f); + } +} \ No newline at end of file diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/ObjectRunnable.java b/DialogX/src/main/java/com/kongzue/dialogx/util/ObjectRunnable.java new file mode 100644 index 0000000..6e569f4 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/ObjectRunnable.java @@ -0,0 +1,12 @@ +package com.kongzue.dialogx.util; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2022/8/26 17:10 + */ +public abstract class ObjectRunnable { + public abstract void run(D d); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/PopMenuArrayAdapter.java b/DialogX/src/main/java/com/kongzue/dialogx/util/PopMenuArrayAdapter.java new file mode 100644 index 0000000..ebb2b3d --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/PopMenuArrayAdapter.java @@ -0,0 +1,218 @@ +package com.kongzue.dialogx.util; + +import static com.kongzue.dialogx.interfaces.BaseDialog.useTextInfo; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.os.Build; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.Space; +import android.widget.TextView; + +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.dialogs.PopMenu; +import com.kongzue.dialogx.interfaces.MenuIconAdapter; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2021/8/18 15:25 + */ +public class PopMenuArrayAdapter extends BaseAdapter { + + public List menuList; + public Context context; + private PopMenu popMenu; + LayoutInflater mInflater; + + public PopMenuArrayAdapter(PopMenu popMenu, Context context, List menuList) { + this.popMenu = popMenu; + this.menuList = menuList; + this.context = context; + mInflater = LayoutInflater.from(context); + } + + public List getMenuList() { + return menuList; + } + + @Override + public int getCount() { + if (menuList == null) { + menuList = new ArrayList<>(); + } + return menuList.size(); + } + + @Override + public Object getItem(int position) { + return menuList.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + ViewHolder viewHolder; + if (convertView == null) { + viewHolder = new ViewHolder(); + int resourceId = R.layout.item_dialogx_material_context_menu_normal_text; + if (popMenu.getStyle().popMenuSettings() != null) { + int customItemLayout = popMenu.getStyle().popMenuSettings().overrideMenuItemLayoutRes(popMenu.isLightTheme()); + if (customItemLayout != 0) { + resourceId = customItemLayout; + } + } + + convertView = mInflater.inflate(resourceId, null); + + viewHolder.boxItem = convertView.findViewById(R.id.box_item); + viewHolder.imgDialogxMenuIcon = convertView.findViewById(R.id.img_dialogx_menu_icon); + viewHolder.txtDialogxMenuText = convertView.findViewById(R.id.txt_dialogx_menu_text); + viewHolder.spaceDialogxRightPadding = convertView.findViewById(R.id.space_dialogx_right_padding); + + convertView.setTag(viewHolder); + } else { + viewHolder = (ViewHolder) convertView.getTag(); + } + if (!popMenu.isMenuItemEnable(position)) { + convertView.setAlpha(0.4f); + } else { + convertView.setAlpha(1f); + } + int customBackgroundRes = popMenu.getStyle().popMenuSettings() == null ? 0 : popMenu.getStyle().popMenuSettings().overrideMenuItemBackgroundRes(popMenu.isLightTheme(), position, getCount(), false); + if (customBackgroundRes != 0) { + convertView.setBackgroundResource(customBackgroundRes); + } + if (viewHolder.boxItem != null) { + if (popMenu.getPressedIndex() == position) { + viewHolder.boxItem.setBackgroundResource(popMenu.isLightTheme() ? R.color.black5 : R.color.white5); + } else { + viewHolder.boxItem.setBackgroundResource(R.color.empty); + } + } + viewHolder.imgDialogxMenuIcon.setVisibility(View.GONE); + viewHolder.txtDialogxMenuText.setText(menuList.get(position)); + if (popMenu.getStyle().popMenuSettings() != null && popMenu.getStyle().popMenuSettings().paddingVertical() != 0) { + if (position == 0) { + convertView.setPadding(0, popMenu.getStyle().popMenuSettings().paddingVertical(), 0, 0); + } else if (position == getCount() - 1) { + convertView.setPadding(0, 0, 0, popMenu.getStyle().popMenuSettings().paddingVertical()); + } else { + convertView.setPadding(0, 0, 0, 0); + } + } + + if (popMenu.getMenuTextInfo() != null) { + useTextInfo(viewHolder.txtDialogxMenuText, popMenu.getMenuTextInfo()); + } + + int textColor = popMenu.isLightTheme() ? R.color.black90 : R.color.white90; + viewHolder.txtDialogxMenuText.setTextColor(context.getResources().getColor(textColor)); + + if (popMenu.getOnIconChangeCallBack() != null) { + if (popMenu.getOnIconChangeCallBack() instanceof MenuIconAdapter) { + boolean result = ((MenuIconAdapter) popMenu.getOnIconChangeCallBack()).applyIcon(popMenu, position, menuList.get(position).toString(), viewHolder.imgDialogxMenuIcon); + boolean autoTintIconInLightOrDarkMode = popMenu.getOnIconChangeCallBack().isAutoTintIconInLightOrDarkMode() == null ? popMenu.isAutoTintIconInLightOrDarkMode() : popMenu.getOnIconChangeCallBack().isAutoTintIconInLightOrDarkMode(); + + viewHolder.imgDialogxMenuIcon.setVisibility(result ? View.VISIBLE : View.GONE); + if (result) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (autoTintIconInLightOrDarkMode) { + viewHolder.imgDialogxMenuIcon.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(textColor))); + } + } + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.VISIBLE); + } + } else { + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.GONE); + } + } + }else{ + int resId = popMenu.getOnIconChangeCallBack().getIcon(popMenu, position, menuList.get(position).toString()); + boolean autoTintIconInLightOrDarkMode = popMenu.getOnIconChangeCallBack().isAutoTintIconInLightOrDarkMode() == null ? popMenu.isAutoTintIconInLightOrDarkMode() : popMenu.getOnIconChangeCallBack().isAutoTintIconInLightOrDarkMode(); + + if (resId != 0) { + viewHolder.imgDialogxMenuIcon.setVisibility(View.VISIBLE); + viewHolder.imgDialogxMenuIcon.setImageResource(resId); + if (isHaveProperties(viewHolder.txtDialogxMenuText.getGravity(), Gravity.CENTER) || isHaveProperties(viewHolder.txtDialogxMenuText.getGravity(), Gravity.CENTER_HORIZONTAL)) { + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.VISIBLE); + } + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (autoTintIconInLightOrDarkMode) { + viewHolder.imgDialogxMenuIcon.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(textColor))); + } + } + } else { + viewHolder.imgDialogxMenuIcon.setVisibility(View.GONE); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.GONE); + } + } + } + } else { + if (popMenu.getIconResIds() != null) { + int resId = popMenu.getIconResIds(position); + boolean autoTintIconInLightOrDarkMode = popMenu.isAutoTintIconInLightOrDarkMode(); + + if (resId != 0) { + viewHolder.imgDialogxMenuIcon.setVisibility(View.VISIBLE); + viewHolder.imgDialogxMenuIcon.setImageResource(resId); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.VISIBLE); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (autoTintIconInLightOrDarkMode) { + viewHolder.imgDialogxMenuIcon.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(textColor))); + } + } + } else { + viewHolder.imgDialogxMenuIcon.setVisibility(View.GONE); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.GONE); + } + } + } else { + viewHolder.imgDialogxMenuIcon.setVisibility(View.GONE); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.GONE); + } + } + } + + if (popMenu.getMenuMenuItemLayoutRefreshCallback() != null) { + popMenu.getMenuMenuItemLayoutRefreshCallback().getView(popMenu, position, convertView, parent); + } + + return convertView; + } + + private boolean isHaveProperties(int gravity, int property) { + return (gravity & property) == property; + } + + class ViewHolder { + LinearLayout boxItem; + ImageView imgDialogxMenuIcon; + TextView txtDialogxMenuText; + Space spaceDialogxRightPadding; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/PopValueAnimator.java b/DialogX/src/main/java/com/kongzue/dialogx/util/PopValueAnimator.java new file mode 100644 index 0000000..e84ac32 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/PopValueAnimator.java @@ -0,0 +1,39 @@ +package com.kongzue.dialogx.util; + +import android.animation.ValueAnimator; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2022/7/19 14:31 + */ +public class PopValueAnimator extends ValueAnimator { + + float startValue; + float endValue; + + public static PopValueAnimator ofFloat(float... values) { + PopValueAnimator anim = new PopValueAnimator(); + anim.setFloatValues(values); + return anim; + } + + @Override + public void setFloatValues(float... values) { + if (values.length > 1) { + startValue = values[0]; + endValue = values[values.length - 1]; + } + super.setFloatValues(values); + } + + public float getStartValue() { + return startValue; + } + + public float getEndValue() { + return endValue; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/TextInfo.java b/DialogX/src/main/java/com/kongzue/dialogx/util/TextInfo.java new file mode 100644 index 0000000..0e7d5be --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/TextInfo.java @@ -0,0 +1,130 @@ +package com.kongzue.dialogx.util; + +import android.graphics.Typeface; +import android.util.TypedValue; + +import androidx.annotation.ColorInt; + +/** + * Author: @Kongzue + * Github: https://github.com/kongzue/ + * Homepage: http://kongzue.com/ + * Mail: myzcxhh@live.cn + * CreateTime: 2018/11/10 22:01 + */ +public class TextInfo { + + private int fontSize = -1; //字号大小,值为-1时使用默认样式,单位:dp + private FONT_SIZE_UNIT fontSizeUnit = FONT_SIZE_UNIT.DP; //字号单位 + private int gravity = -1; //对齐方式,值为-1时使用默认样式,取值可使用Gravity.CENTER等对齐方式 + private int fontColor = 1; //文字颜色,值为1时使用默认样式,取值可以用Color.rgb(r,g,b)等方式获取 + private boolean bold = false; //是否粗体 + private int maxLines = -1; //最大行数 + private boolean showEllipsis = false; //显示省略号 + private Typeface typeface; //指定字体样式 + + public enum FONT_SIZE_UNIT { + DP, + PX, + SP + } + + public int getFontSize() { + return fontSize; + } + + public int getFontSizeComplexUnit() { + if (fontSizeUnit == null) { + return TypedValue.COMPLEX_UNIT_DIP; + } + switch (fontSizeUnit) { + case PX: + return TypedValue.COMPLEX_UNIT_PX; + case SP: + return TypedValue.COMPLEX_UNIT_SP; + default: + return TypedValue.COMPLEX_UNIT_DIP; + } + } + + public TextInfo setFontSize(int fontSize) { + this.fontSize = fontSize; + return this; + } + + public int getGravity() { + return gravity; + } + + public TextInfo setGravity(int gravity) { + this.gravity = gravity; + return this; + } + + public int getFontColor() { + return fontColor; + } + + public TextInfo setFontColor(@ColorInt int fontColor) { + this.fontColor = fontColor; + return this; + } + + public boolean isBold() { + return bold; + } + + public TextInfo setBold(boolean bold) { + this.bold = bold; + return this; + } + + public int getMaxLines() { + return maxLines; + } + + public TextInfo setMaxLines(int maxLines) { + this.maxLines = maxLines; + return this; + } + + public boolean isShowEllipsis() { + return showEllipsis; + } + + public TextInfo setShowEllipsis(boolean showEllipsis) { + this.showEllipsis = showEllipsis; + return this; + } + + public FONT_SIZE_UNIT getFontSizeUnit() { + return fontSizeUnit; + } + + public TextInfo setFontSizeUnit(FONT_SIZE_UNIT fontSizeUnit) { + this.fontSizeUnit = fontSizeUnit; + return this; + } + + public Typeface getTypeface() { + return typeface; + } + + public TextInfo setTypeface(Typeface typeface) { + this.typeface = typeface; + return this; + } + + @Override + public String toString() { + return "TextInfo{" + + "fontSize=" + fontSize + + ", gravity=" + gravity + + ", fontColor=" + fontColor + + ", bold=" + bold + + ", maxLines=" + maxLines + + ", showEllipsis=" + showEllipsis + + ", typeface=" + typeface + + '}'; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/WindowUtil.java b/DialogX/src/main/java/com/kongzue/dialogx/util/WindowUtil.java new file mode 100644 index 0000000..ed4f53d --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/WindowUtil.java @@ -0,0 +1,136 @@ +package com.kongzue.dialogx.util; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.graphics.PixelFormat; +import android.os.Build; +import android.provider.Settings; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.FrameLayout; +import android.widget.Toast; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.dialogs.PopTip; +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.interfaces.NoTouchInterface; + +import static android.view.WindowManager.LayoutParams.*; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2021/4/29 16:02 + */ +public class WindowUtil { + + public abstract static class WindowSettings { + + //自定义window参数 + public abstract WindowManager.LayoutParams overrideWindowLayoutParamsInterface(Context context, View dialogView, WindowManager.LayoutParams originWindowLayoutParams); + + //自定义根布局 + public ViewGroup overrideRootView(Context context){return null;}; + } + + //自定义window设置 + public static WindowSettings windowSettings; + + public static void show(Activity activity, View dialogView, boolean touchEnable) { + try { + if (activity.getWindow().getDecorView().isAttachedToWindow()) { + showNow(activity, dialogView, touchEnable); + } else { + activity.getWindow().getDecorView().post(new Runnable() { + @Override + public void run() { + showNow(activity, dialogView, touchEnable); + } + }); + } + } catch (Exception e) { + if (activity != null && !activity.isDestroyed()) { + showNow(activity, dialogView, touchEnable); + } + } + } + + private static void showNow(Activity activity, View dialogView, boolean touchEnable) { + if (DialogX.globalHoverWindow && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(activity)) { + Toast.makeText(activity, "使用 DialogX.globalHoverWindow 必须开启悬浮窗权限", Toast.LENGTH_LONG).show(); + Intent intent = new Intent(); + intent.setAction(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); + activity.startActivity(intent); + return; + } + ViewGroup customRootView = null; + if (windowSettings != null) { + customRootView = windowSettings.overrideRootView(activity); + } + ViewGroup rootLayout = customRootView == null ? new FrameLayout(activity) : customRootView; + if (dialogView.getParent() != null) { + ((ViewGroup) dialogView.getParent()).removeView(dialogView); + } + rootLayout.addView(dialogView, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)); + WindowManager manager = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE); + WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); + layoutParams.gravity = Gravity.CENTER_VERTICAL; + layoutParams.format = PixelFormat.TRANSPARENT; + if (DialogX.globalHoverWindow) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + layoutParams.type = TYPE_APPLICATION_OVERLAY; + } else { + layoutParams.type = TYPE_PHONE; + } + } else { + layoutParams.type = TYPE_APPLICATION_ATTACHED_DIALOG; + } + layoutParams.flags = FLAG_FULLSCREEN | + FLAG_TRANSLUCENT_STATUS | + FLAG_TRANSLUCENT_NAVIGATION | + FLAG_LAYOUT_IN_SCREEN + ; + layoutParams.softInputMode = SOFT_INPUT_ADJUST_RESIZE; + if (!touchEnable) { + dialogView.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + for (int i = BaseDialog.getRunningDialogList().size() - 1; i >= 0; i--) { + BaseDialog baseDialog = BaseDialog.getRunningDialogList().get(i); + if (!(baseDialog instanceof NoTouchInterface) && baseDialog.getOwnActivity() == activity) { + if (baseDialog.getDialogView() == null) { + return false; + } + return baseDialog.dispatchTouchEvent(event); + } + } + return activity.dispatchTouchEvent(event); + } + }); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + } + if (windowSettings != null) { + WindowManager.LayoutParams layoutParamsTemp = windowSettings.overrideWindowLayoutParamsInterface(activity, dialogView, layoutParams); + if (layoutParamsTemp != null) { + layoutParams = layoutParamsTemp; + } + } + manager.addView(rootLayout, layoutParams); + } + + public static void dismiss(View dialogView) { + BaseDialog baseDialog = (BaseDialog) dialogView.getTag(); + if (baseDialog != null && baseDialog.getOwnActivity() != null) { + WindowManager manager = (WindowManager) baseDialog.getOwnActivity().getSystemService(Context.WINDOW_SERVICE); + manager.removeViewImmediate((View) dialogView.getParent()); + } + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/ActivityScreenShotImageView.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/ActivityScreenShotImageView.java new file mode 100644 index 0000000..c1368cb --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/ActivityScreenShotImageView.java @@ -0,0 +1,218 @@ +package com.kongzue.dialogx.util.views; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Path; +import android.graphics.Rect; +import android.os.Build; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.impl.ActivityLifecycleImpl; +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.util.DialogXFloatingWindowActivity; + +import java.lang.ref.WeakReference; +import java.util.Objects; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2019/11/17 23:53 + */ +@SuppressLint("AppCompatCustomView") +public class ActivityScreenShotImageView extends ImageView { + + public boolean hideActivityContentView; + float width, height, mRadius; + public static boolean useHardwareRenderingMode = true; + + public ActivityScreenShotImageView(Context context) { + super(context); + init(null); + } + + public ActivityScreenShotImageView(Context context, AttributeSet attrs) { + super(context, attrs); + init(attrs); + } + + public ActivityScreenShotImageView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(attrs); + } + + private void init(AttributeSet attrs) { + requestLayoutType(); + } + + private void requestLayoutType() { + setLayerType(useHardwareRenderingMode ? LAYER_TYPE_HARDWARE : LAYER_TYPE_SOFTWARE, null); + } + + public void setRadius(float mRadius) { + this.mRadius = mRadius; + invalidate(); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + if (width != getWidth() || height != getHeight()) { + refreshImage(); + } + width = getWidth(); + height = getHeight(); + } + + boolean readyDraw = false; + + @Override + protected void onDraw(Canvas canvas) { + if (!readyDraw) { + super.onDraw(canvas); + } + if (width >= mRadius && height > mRadius) { + if (isScreenshotSuccess) { + canvas.drawColor(Color.BLACK); + } + Path path = new Path(); + path.moveTo(mRadius, 0); + path.lineTo(width - mRadius, 0); + path.quadTo(width, 0, width, mRadius); + path.lineTo(width, height - mRadius); + path.quadTo(width, height, width - mRadius, height); + path.lineTo(mRadius, height); + path.quadTo(0, height, 0, height - mRadius); + path.lineTo(0, mRadius); + path.quadTo(0, 0, mRadius, 0); + + canvas.clipPath(path); + } + canvas.drawColor(Color.WHITE); + super.onDraw(canvas); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + if (!isScreenshotSuccess) refreshImage(); + } + + private int screenWidth, screenHeight; + + private void refreshImage() { + if (!isAttachedToWindow()) { + return; + } + if (screenWidth != getMeasuredWidth() || screenHeight != getMeasuredHeight()) { + screenWidth = getMeasuredWidth(); + screenHeight = getMeasuredHeight(); + doScreenshotActivityAndZoom(); + } + } + + private void doScreenshotActivityAndZoom() { + ViewGroup decorView = getDecorView(); + if (decorView == null) return; + drawViewImage(decorView); + setVisibility(VISIBLE); + inited = true; + } + + private ViewGroup getDecorView() { + if (dialog != null) { + Activity ownActivity = dialog.getOwnActivity(); + return (ViewGroup) ownActivity.getWindow().getDecorView(); + } + Activity topActivity = ActivityLifecycleImpl.getTopActivity(); + if (topActivity != null) { + if (topActivity instanceof DialogXFloatingWindowActivity) { + return (ViewGroup) ((DialogXFloatingWindowActivity) topActivity).getFromActivity().getWindow().getDecorView(); + } + return (ViewGroup) topActivity.getWindow().getDecorView(); + } + return null; + } + + private boolean inited = false; + private boolean isScreenshotSuccess; + private WeakReference contentView; + public static boolean hideContentView = false; + + private void drawViewImage(View view) { + if (view.getWidth() == 0 || view.getHeight() == 0) return; + dialog.getDialogView().setVisibility(GONE); + setContentViewVisibility(true); + if (view.getWidth() + view.getHeight() == 0) { + view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); + view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); + } + int viewWidth = view.getWidth(); + int viewHeight = view.getHeight(); + Bitmap bitmap = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + try { + view.draw(canvas); + } catch (Exception e) { + if (DialogX.DEBUGMODE) e.printStackTrace(); + if (useHardwareRenderingMode) { + useHardwareRenderingMode = false; + requestLayoutType(); + drawViewImage(view); + } + } + setImageBitmap(Bitmap.createBitmap(bitmap, 0, 0, view.getWidth(), view.getHeight())); + isScreenshotSuccess = true; + setContentViewVisibility(false); + dialog.getDialogView().setVisibility(VISIBLE); + dialog.getDialogView().requestFocus(); + } + + protected void setContentViewVisibility(boolean show) { + if (hideContentView || hideActivityContentView) { + if (show) { + if (contentView != null && contentView.get() != null) { + contentView.get().setVisibility(VISIBLE); + } + } else { + View userContentView = Objects.requireNonNull(getDecorView()).getChildAt(0); + if (userContentView != null) { + userContentView.setVisibility(GONE); + contentView = new WeakReference<>(userContentView); + } + } + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + setContentViewVisibility(true); + if (contentView != null) { + contentView.clear(); + } + } + + public void setScale(float scale) { + setScaleX(scale); + setScaleY(scale); + readyDraw = true; + } + + BaseDialog dialog; + + public void bindDialog(BaseDialog dialog) { + this.dialog = dialog; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/BlurView.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/BlurView.java new file mode 100644 index 0000000..31c86f4 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/BlurView.java @@ -0,0 +1,539 @@ +package com.kongzue.dialogx.util.views; + +import android.app.Activity; +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Outline; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Shader; +import android.os.Build; +import android.util.AttributeSet; +import android.util.Log; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.view.ViewTreeObserver; + +import androidx.annotation.ColorInt; +import androidx.annotation.Nullable; +import androidx.renderscript.Allocation; +import androidx.renderscript.Element; +import androidx.renderscript.RenderScript; +import androidx.renderscript.ScriptIntrinsicBlur; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BaseDialog; + +@Deprecated +public class BlurView extends View { + + private float mDownsampleFactor = 4; + private int mOverlayColor = Color.WHITE; + private float mBlurRadius = 35; + private boolean overrideOverlayColor = false; + private float mRadius = 0; + private boolean mDirty; + private Bitmap mBitmapToBlur, mBlurredBitmap; + private Canvas mBlurringCanvas; + private RenderScript mRenderScript; + private ScriptIntrinsicBlur mBlurScript; + private Allocation mBlurInput, mBlurOutput; + private boolean mIsRendering; + private final Rect mRectSrc = new Rect(), mRectDst = new Rect(); + private View mDecorView; + private boolean mDifferentRoot; + private static int RENDERING_COUNT; + + private Paint mPaint; + private RectF mRectF; + + public BlurView(Context context, AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public BlurView(Context context) { + super(context); + + init(context, null); + } + + public BlurView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + init(context, attrs); + } + + private boolean isInit = false; + + Paint cutPaint; + Paint overlayPaint; + + private void init(Context context, AttributeSet attrs) { + if (!isInit && context != null) { + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RealtimeBlurView); + mBlurRadius = a.getDimension( + R.styleable.RealtimeBlurView_realtimeBlurRadius, + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 35, context.getResources().getDisplayMetrics()) + ); + mDownsampleFactor = a.getFloat(R.styleable.RealtimeBlurView_realtimeDownsampleFactor, 4); + mOverlayColor = a.getColor(R.styleable.RealtimeBlurView_realtimeOverlayColor, 0x00ffffff); + + //ready rounded corner + mPaint = new Paint(); + mPaint.setAntiAlias(true); + mRectF = new RectF(); + + cutPaint = new Paint(); + cutPaint.setAntiAlias(true); + cutPaint.setColor(mOverlayColor); + + overlayPaint = new Paint(); + overlayPaint.setAntiAlias(true); + + mRadius = a.getDimension(R.styleable.RealtimeBlurView_realtimeRadius, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 15, context.getResources().getDisplayMetrics())); + a.recycle(); + + isInit = true; + + if (!isCompatMode()) { + setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mRadius); + } + }); + setClipToOutline(true); + } + } + } + + private boolean isCompatMode() { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP; + } + + public void setBlurRadius(float radius) { + if (mBlurRadius != radius) { + mBlurRadius = radius; + mDirty = true; + invalidate(); + } + } + + public void setRadiusPx(float r) { + if (mRadius != r) { + mRadius = r; + mDirty = true; + invalidate(); + } + } + + public void setDownsampleFactor(float factor) { + if (factor <= 0) { + throw new IllegalArgumentException("Downsample factor must be greater than 0."); + } + + if (mDownsampleFactor != factor) { + mDownsampleFactor = factor; + mDirty = true; // may also change blur radius + releaseBitmap(); + invalidate(); + } + } + + public void setOverlayColor(int color) { + if (mOverlayColor != color) { + mOverlayColor = color; + invalidate(); + } + } + + private void releaseBitmap() { + if (mBlurInput != null) { + mBlurInput.destroy(); + mBlurInput = null; + } + if (mBlurOutput != null) { + mBlurOutput.destroy(); + mBlurOutput = null; + } + if (mBitmapToBlur != null) { + mBitmapToBlur.recycle(); + mBitmapToBlur = null; + } + if (mBlurredBitmap != null) { + mBlurredBitmap.recycle(); + mBlurredBitmap = null; + } + } + + private void releaseScript() { + if (mRenderScript != null) { + mRenderScript.destroy(); + mRenderScript = null; + } + if (mBlurScript != null) { + mBlurScript.destroy(); + mBlurScript = null; + } + } + + protected void release() { + releaseBitmap(); + releaseScript(); + } + + protected boolean prepare() { + if (mBlurRadius == 0) { + release(); + return false; + } + + float downsampleFactor = mDownsampleFactor; + + if (mDirty || mRenderScript == null) { + if (supportRenderScript && useBlur) { + if (mRenderScript == null) { + try { + mRenderScript = RenderScript.create(getContext()); + mBlurScript = ScriptIntrinsicBlur.create(mRenderScript, Element.U8_4(mRenderScript)); + + } catch (Exception e) { + supportRenderScript = false; + if (isDebug()) { + e.printStackTrace(); + } + } + } + + mDirty = false; + float radius = mBlurRadius / downsampleFactor; + if (radius > 25) { + downsampleFactor = downsampleFactor * radius / 25; + radius = 25; + } + if (mBlurScript != null) mBlurScript.setRadius(radius); + } + } + + final int width = getWidth(); + final int height = getHeight(); + + int scaledWidth = Math.max(1, (int) (width / downsampleFactor)); + int scaledHeight = Math.max(1, (int) (height / downsampleFactor)); + + if (mBlurringCanvas == null || mBlurredBitmap == null || mBlurredBitmap.getWidth() != scaledWidth || mBlurredBitmap.getHeight() != scaledHeight) { + releaseBitmap(); + + boolean r = false; + try { + mBitmapToBlur = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888); + if (mBitmapToBlur == null) { + return false; + } + mBlurringCanvas = new Canvas(mBitmapToBlur); + + if (!supportRenderScript || !useBlur) { + return true; + } + + mBlurInput = Allocation.createFromBitmap(mRenderScript, mBitmapToBlur, + Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT + ); + mBlurOutput = Allocation.createTyped(mRenderScript, mBlurInput.getType()); + + mBlurredBitmap = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888); + if (mBlurredBitmap == null) { + return false; + } + + r = true; + } catch (Exception e) { + if (isDebug()) e.printStackTrace(); + } finally { + if (!r) { + releaseBitmap(); + return false; + } + } + } + return true; + } + + protected void blur(Bitmap bitmapToBlur, Bitmap blurredBitmap) { + mBlurInput.copyFrom(bitmapToBlur); + mBlurScript.setInput(mBlurInput); + mBlurScript.forEach(mBlurOutput); + mBlurOutput.copyTo(blurredBitmap); + } + + private final ViewTreeObserver.OnPreDrawListener preDrawListener = new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + final int[] locations = new int[2]; + Bitmap oldBmp = mBlurredBitmap; + View decor = mDecorView; + if (decor != null && isShown() && prepare()) { + boolean redrawBitmap = mBlurredBitmap != oldBmp; + decor.getLocationInWindow(locations); + int x = -locations[0]; + int y = -locations[1]; + + getLocationInWindow(locations); + x += locations[0]; + y += locations[1]; + + mBitmapToBlur.eraseColor(mOverlayColor & 0xffffff); + + int rc = mBlurringCanvas.save(); + mIsRendering = true; + RENDERING_COUNT++; + try { + mBlurringCanvas.scale(1.f * mBitmapToBlur.getWidth() / getWidth(), 1.f * mBitmapToBlur.getHeight() / getHeight()); + mBlurringCanvas.translate(-x, -y); + if (decor.getBackground() != null) { + decor.getBackground().draw(mBlurringCanvas); + } + decor.draw(mBlurringCanvas); + } catch (Exception e) { + if (isDebug()) e.printStackTrace(); + } finally { + mIsRendering = false; + RENDERING_COUNT--; + mBlurringCanvas.restoreToCount(rc); + } + + blur(mBitmapToBlur, mBlurredBitmap); + + if (redrawBitmap || mDifferentRoot) { + invalidate(); + } + } + + return true; + } + }; + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + Activity activity; + log(getContext()); + if (getContext() instanceof Activity) { + activity = (Activity) getContext(); + } else { + activity = BaseDialog.getTopActivity(); + } + ViewGroup decorView = ((ViewGroup) activity.getWindow().getDecorView()); + if (decorView.getChildCount() >= 1) { + mDecorView = decorView.getChildAt(0); + } + if (mDecorView != null) { + log("mDecorView is ok."); + mDecorView.getViewTreeObserver().addOnPreDrawListener(preDrawListener); + mDifferentRoot = mDecorView.getRootView() != getRootView(); + if (mDifferentRoot) { + mDecorView.postInvalidate(); + } + } else { + log("mDecorView is NULL."); + mDifferentRoot = false; + } + } + + @Override + protected void onDetachedFromWindow() { + if (mDecorView != null) { + mDecorView.getViewTreeObserver().removeOnPreDrawListener(preDrawListener); + } + release(); + super.onDetachedFromWindow(); + } + + @Override + public void draw(Canvas canvas) { + if (!useBlur || !supportRenderScript) { + mRectF.right = getWidth(); + mRectF.bottom = getHeight(); + overlayPaint.setColor(needRemoveAlphaColor() ? removeAlphaColor(mOverlayColor) : mOverlayColor); + canvas.drawRoundRect(mRectF, mRadius, mRadius, overlayPaint); + } else { + if (!mIsRendering && RENDERING_COUNT <= 0) { + super.draw(canvas); + } + } + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (isCompatMode()) { + drawBlurredBitmapCompat(canvas); + } else { + drawBlurredBitmap(canvas, mBlurredBitmap); + } + } + + private void drawBlurredBitmapCompat(Canvas canvas) { + if (mBlurredBitmap != null) { + mRectDst.right = getWidth(); + mRectDst.bottom = getHeight(); + if (getWidth() > 0 && getHeight() > 0) { + Bitmap readyDrawBitmap = getRoundedCornerBitmap(resizeImage(mBlurredBitmap, getWidth(), getHeight()), mRectDst); + if (readyDrawBitmap != null) { + canvas.drawBitmap(readyDrawBitmap, 0, 0, null); + } else { + Bitmap overlyBitmap = drawOverlyColor(Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888)); + if (overlyBitmap != null) canvas.drawBitmap(overlyBitmap, 0, 0, null); + } + } + } else { + Bitmap overlyBitmap = drawOverlyColor(Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888)); + if (overlyBitmap != null) canvas.drawBitmap(overlyBitmap, 0, 0, null); + } + } + + protected void drawBlurredBitmap(Canvas canvas, Bitmap blurredBitmap) { + if (blurredBitmap != null) { + mRectSrc.right = blurredBitmap.getWidth(); + mRectSrc.bottom = blurredBitmap.getHeight(); + mRectDst.right = getWidth(); + mRectDst.bottom = getHeight(); + canvas.drawBitmap(blurredBitmap, mRectSrc, mRectDst, null); + canvas.drawColor(needRemoveAlphaColor() ? removeAlphaColor(mOverlayColor) : mOverlayColor); + } else { + Bitmap overlyBitmap = drawOverlyColor(Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888)); + if (overlyBitmap != null) canvas.drawBitmap(overlyBitmap, 0, 0, null); + } + } + + private Bitmap getRoundedCornerBitmap(Bitmap bitmap, Rect mRectDst) { + bitmap = drawOverlyColor(resizeImage(bitmap, mRectDst.width(), mRectDst.height())); + if (bitmap == null) return null; + Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(output); + BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setShader(bitmapShader); + canvas.drawRoundRect(new RectF(mRectDst), mRadius, mRadius, paint); + return output; + } + + private Bitmap drawOverlyColor(Bitmap bitmap) { + if (bitmap != null) { + Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(output); + Rect originRect = new Rect(); + originRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight()); + canvas.drawBitmap(bitmap, originRect, originRect, overlayPaint); + canvas.drawColor(needRemoveAlphaColor() ? removeAlphaColor(mOverlayColor) : mOverlayColor); + return output; + } else { + return null; + } + } + + private Bitmap resizeImage(Bitmap bitmap, int newWidth, int newHeight) { + if (bitmap != null) { + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + float scaleWidth = ((float) newWidth) / width; + float scaleHeight = ((float) newHeight) / height; + Matrix matrix = new Matrix(); + matrix.postScale(scaleWidth, scaleHeight); + return Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true); + } else { + return null; + } + } + + private static boolean supportRenderScript = false; + private boolean useBlur = true; + + public boolean isUseBlur() { + return useBlur; + } + + public BlurView setUseBlur(boolean useBlur) { + this.useBlur = useBlur; + invalidate(); + return this; + } + + private boolean needRemoveAlphaColor() { + if (overrideOverlayColor) { + return false; + } else { + return !(supportRenderScript && useBlur); + } + } + + private static int removeAlphaColor(@ColorInt int color) { + int alpha = 255; + int red = Color.red(color); + int green = Color.green(color); + int blue = Color.blue(color); + return Color.argb(alpha, red, green, blue); + } + + private static int replaceAlphaColor(@ColorInt int color, int alpha) { + int red = Color.red(color); + int green = Color.green(color); + int blue = Color.blue(color); + return Color.argb(alpha, red, green, blue); + } + + static { + /** + * 之所以需要启动一个新线程检测RenderScript是否可用的原因是不清楚Android什么时候对loadClass做了变更, + * 会直接抛出NoClassDefFoundError无法拦截,在主线程检测会导致程序直接闪退,因此改为异步检测。 + * 检测后会给定(boolean)supportRenderScript用于判断时光支持 + */ + new Thread() { + @Override + public void run() { + try { + BlurView.class.getClassLoader().loadClass(RenderScript.class.getCanonicalName()); + supportRenderScript = true; + } catch (Throwable e) { + if (isDebug()) { + e.printStackTrace(); + } + supportRenderScript = false; + } + } + }.start(); + } + + public static boolean DEBUGMODE = false; + + static boolean isDebug() { + return DEBUGMODE && DialogX.DEBUGMODE; + } + + private static void log(Object o) { + if (isDebug()) Log.i(">>>", "DialogX.BlurView: " + o.toString()); + } + + public static void error(Object o) { + if (isDebug()) Log.e(">>>", o.toString()); + } + + public BlurView setOverrideOverlayColor(boolean overrideOverlayColor) { + log("setOverrideOverlayColor: " + overrideOverlayColor); + this.overrideOverlayColor = overrideOverlayColor; + return this; + } +} \ No newline at end of file diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/DialogListView.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/DialogListView.java new file mode 100644 index 0000000..e9c08bb --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/DialogListView.java @@ -0,0 +1,129 @@ +package com.kongzue.dialogx.util.views; + +import android.content.Context; +import android.content.res.Resources; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.widget.ListAdapter; +import android.widget.ListView; + +import androidx.appcompat.view.ContextThemeWrapper; + +import com.kongzue.dialogx.interfaces.BottomMenuListViewTouchEvent; +import com.kongzue.dialogx.interfaces.DialogConvertViewInterface; +import com.kongzue.dialogx.interfaces.ScrollController; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/6 23:42 + */ +public class DialogListView extends ListView implements ScrollController { + + private BottomMenuListViewTouchEvent bottomMenuListViewTouchEvent; + + public DialogListView(Context context) { + super(context); + } + + public DialogListView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public DialogListView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + private DialogConvertViewInterface dialogImpl; + + public DialogListView(DialogConvertViewInterface dialog, Context context) { + super(context); + dialogImpl = dialog; + setVerticalScrollBarEnabled(false); + } + + public DialogListView(DialogConvertViewInterface dialog, Context context, int theme) { + super(new ContextThemeWrapper(context, theme)); + dialogImpl = dialog; + setVerticalScrollBarEnabled(false); + } + +// private int dip2px(float dpValue) { +// final float scale = Resources.getSystem().getDisplayMetrics().density; +// return (int) (dpValue * scale + 0.5f); +// } +// +// private int mPosition; +// private float touchDownY; +// + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + final int actionMasked = ev.getActionMasked() & MotionEvent.ACTION_MASK; + switch (actionMasked){ + case MotionEvent.ACTION_DOWN: + if (bottomMenuListViewTouchEvent != null) { + bottomMenuListViewTouchEvent.down(ev); + } + break; + case MotionEvent.ACTION_MOVE: + if (bottomMenuListViewTouchEvent != null) { + bottomMenuListViewTouchEvent.move(ev); + } + break; + case MotionEvent.ACTION_UP: + if (bottomMenuListViewTouchEvent != null) { + bottomMenuListViewTouchEvent.up(ev); + } + break; + } + return super.dispatchTouchEvent(ev); + } + + public BottomMenuListViewTouchEvent getBottomMenuListViewTouchEvent() { + return bottomMenuListViewTouchEvent; + } + + public DialogListView setBottomMenuListViewTouchEvent(BottomMenuListViewTouchEvent bottomMenuListViewTouchEvent) { + this.bottomMenuListViewTouchEvent = bottomMenuListViewTouchEvent; + return this; + } + + @Override + public int getScrollDistance() { + View c = getChildAt(0); + if (c == null) { + return 0; + } + int firstVisiblePosition = getFirstVisiblePosition(); + int top = c.getTop(); + int scrollY = -top + firstVisiblePosition * c.getHeight(); + return scrollY; + } + + @Override + public boolean isCanScroll() { + return true; + } + + boolean lockScroll; + + @Override + public void lockScroll(boolean lockScroll) { + this.lockScroll = lockScroll; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (lockScroll) return false; + return super.onTouchEvent(event); + } + + @Override + public boolean isLockScroll() { + return lockScroll; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/DialogScrollView.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/DialogScrollView.java new file mode 100644 index 0000000..2d254f9 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/DialogScrollView.java @@ -0,0 +1,73 @@ +package com.kongzue.dialogx.util.views; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.widget.ScrollView; +import com.kongzue.dialogx.interfaces.ScrollController; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/11/17 15:29 + */ +public class DialogScrollView extends ScrollView implements ScrollController { + + public DialogScrollView(Context context) { + super(context); + } + + public DialogScrollView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public DialogScrollView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public DialogScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + boolean lockScroll; + + @Override + public boolean isLockScroll() { + return lockScroll; + } + + public void lockScroll(boolean lockScroll) { + this.lockScroll = lockScroll; + } + + @Override + public int getScrollDistance() { + return getScrollY(); + } + + @Override + public boolean isCanScroll() { + View child = getChildAt(0); + if (child != null) { + int childHeight = child.getHeight(); + return getHeight() < childHeight; + } + return false; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + if (lockScroll) { + return false; + } + return super.onTouchEvent(ev); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + return super.dispatchTouchEvent(ev); + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/DialogXBaseRelativeLayout.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/DialogXBaseRelativeLayout.java new file mode 100644 index 0000000..3b9d234 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/DialogXBaseRelativeLayout.java @@ -0,0 +1,484 @@ +package com.kongzue.dialogx.util.views; + +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.TypedArray; +import android.graphics.Rect; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.util.AttributeSet; +import android.util.Log; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; +import android.widget.RelativeLayout; + +import androidx.annotation.Px; +import androidx.core.graphics.Insets; +import androidx.core.view.WindowInsetsCompat; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.interfaces.DialogXBaseBottomDialog; +import com.kongzue.dialogx.interfaces.DialogXSafetyModeInterface; +import com.kongzue.dialogx.interfaces.NoTouchInterface; +import com.kongzue.dialogx.interfaces.OnSafeInsetsChangeListener; + +import java.lang.ref.WeakReference; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/22 13:53 + */ +public class DialogXBaseRelativeLayout extends RelativeLayout { + + public static boolean debugMode = false; + + private OnSafeInsetsChangeListener onSafeInsetsChangeListener; + private WeakReference parentDialog; + private boolean autoUnsafePlacePadding = true; + private boolean focusable = true; + private boolean interceptBack = true; + + private OnLifecycleCallBack onLifecycleCallBack; + private PrivateBackPressedListener onBackPressedListener; + + private FitSystemBarUtils fitSystemBarUtils; + + public FitSystemBarUtils getFitSystemBarUtils() { + return fitSystemBarUtils; + } + + public DialogXBaseRelativeLayout(Context context) { + super(context); + init(null); + } + + public DialogXBaseRelativeLayout(Context context, AttributeSet attrs) { + super(context, attrs); + init(attrs); + } + + public DialogXBaseRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(attrs); + } + + private boolean isInited = false; + + private void init(AttributeSet attrs) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + setForceDarkAllowed(false); + } + if (!isInited) { + if (attrs != null) { + TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.DialogXBaseRelativeLayout); + focusable = a.getBoolean(R.styleable.DialogXBaseRelativeLayout_baseFocusable, true); + autoUnsafePlacePadding = a.getBoolean(R.styleable.DialogXBaseRelativeLayout_autoSafeArea, true); + interceptBack = a.getBoolean(R.styleable.DialogXBaseRelativeLayout_interceptBack, true); + a.recycle(); + isInited = true; + } + if (focusable) { + setFocusable(true); + setFocusableInTouchMode(true); + } + setBkgAlpha(0f); + if (getParentDialog() != null && getParentDialog().getDialogImplMode() != DialogX.IMPL_MODE.VIEW) { + setFitsSystemWindows(true); + } + setClipChildren(false); + setClipToPadding(false); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + setDefaultFocusHighlightEnabled(false); + } + + //新增的 设置监听 OnApplyWindowInsetsListener + log("KONGZUE DEBUG DIALOGX: create fitSystemBarUtils"); + fitSystemBarUtils = FitSystemBarUtils.attachView(this, new FitSystemBarUtils.CallBack() { + @Override + public boolean isEnable(FitSystemBarUtils.Orientation orientation) { + return true; + } + + @Override + public void unsafeRect(int start, int top, int end, int bottom) { + log("KONGZUE DEBUG DIALOGX: unsafeRect t=" + top + " b=" + bottom); + if (unsafePlace == null) { + unsafePlace = new Rect(); + } + // TODO Fix bug #370 在这里对这个重新做下处理,未详细测试,比如键盘弹起时的情况 + Insets systemBarInsets = null; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && getRootWindowInsets() != null) { + WindowInsetsCompat windowInsetsCompat = WindowInsetsCompat.toWindowInsetsCompat(getRootWindowInsets()); + boolean navigationBarsVisible = windowInsetsCompat.isVisible(WindowInsetsCompat.Type.navigationBars()); + boolean imeVisible = windowInsetsCompat.isVisible(WindowInsetsCompat.Type.ime()); + if (!imeVisible && navigationBarsVisible) { + systemBarInsets = windowInsetsCompat.getInsets(WindowInsetsCompat.Type.systemBars()); + if (systemBarInsets.bottom == bottom && systemBarInsets.top == top && systemBarInsets.left == start && systemBarInsets.right == end) { + systemBarInsets = null; + } + } + } + if (systemBarInsets != null) { + unsafePlace.left = Math.max(systemBarInsets.left, start); + unsafePlace.top = Math.max(systemBarInsets.top, top); + unsafePlace.right = Math.max(systemBarInsets.right, end); + unsafePlace.bottom = Math.max(systemBarInsets.bottom, bottom); + } else { + unsafePlace.left = start; + unsafePlace.top = top; + unsafePlace.right = end; + unsafePlace.bottom = bottom; + } + + if (onSafeInsetsChangeListener != null) { + onSafeInsetsChangeListener.onChange(unsafePlace); + } + + setUnsafePadding(unsafePlace.left, unsafePlace.top, unsafePlace.right, unsafePlace.bottom); + } + + @Override + public int initialPadding(FitSystemBarUtils.Orientation orientation) { + switch (orientation) { + case Start: + return extraPadding[0]; + case Top: + return extraPadding[1]; + case End: + return extraPadding[2]; + case Bottom: + return extraPadding[3]; + } + return 0; + } + }); + } + } + + public void setUnsafePadding(@Px int start, @Px int top, @Px int end, @Px int bottom) { + log("KONGZUE DEBUG DIALOGX: setUnsafePadding=" + getParentDialog() + " t=" + top + " b=" + bottom); + if (DialogX.ignoreUnsafeInsetsHorizontal) { + log(" KONGZUE DEBUG DIALOGX: ignoreUnsafeInsetsHorizontal, start and end set 0"); + start = 0; + end = 0; + } + if (isAlignBottomDialog(getParentDialog())) { + log(" KONGZUE DEBUG DIALOGX: Dialog is align bottom"); + View dialogXSafetyArea = findViewWithTag("DialogXSafetyArea"); + if (dialogXSafetyArea instanceof DialogXSafetyModeInterface) { + int dialogxSafetyMode = ((DialogXSafetyModeInterface) dialogXSafetyArea).getDialogXSafetyMode(); + boolean hasTop = (dialogxSafetyMode & 0x1) != 0; + boolean hasLeft = (dialogxSafetyMode & 0x2) != 0; + boolean hasBottom = (dialogxSafetyMode & 0x4) != 0; + boolean hasRight = (dialogxSafetyMode & 0x8) != 0; + log(" KONGZUE DEBUG DIALOGX: dialogXSafetyArea" + dialogXSafetyArea + " hasLeft=" + hasLeft + "hasTop=" + hasTop + " hasRight=" + hasRight + " hasBottom=" + hasBottom); + dialogXSafetyArea.setPadding(hasLeft ? start : 0, hasTop ? top : 0, hasRight ? end : 0, hasBottom ? bottom : 0); + if (hasTop) { + top = 0; + } + if (hasLeft) { + start = 0; + } + if (hasRight) { + end = 0; + } + if (hasBottom) { + bottom = 0; + } + } else { + ViewGroup bkgView = findViewById(R.id.bkg); + if (!((DialogXBaseBottomDialog) getParentDialog()).isBottomNonSafetyAreaBySelf() && bkgView != null) { + log(" KONGZUE DEBUG DIALOGX: bkgView.setPadding b=" + bottom); + bkgView.setPadding(0, 0, 0, bottom); + } + bottom = 0; + } + } + if (isAutoUnsafePlacePadding()) { + log(" KONGZUE DEBUG DIALOGX: root.setPadding t=" + top + " b=" + bottom); + setPadding(start, top, end, bottom); + } + } + + private boolean isAlignBottomDialog(BaseDialog parentDialog) { + return getParentDialog() instanceof DialogXBaseBottomDialog || findViewWithTag("DialogXSafetyArea") instanceof DialogXSafetyModeInterface; + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + log("#dispatchKeyEvent: KeyCode=" + event.getKeyCode()); + if (isAttachedToWindow() && event.getAction() == KeyEvent.ACTION_UP && event.getKeyCode() == KeyEvent.KEYCODE_BACK && interceptBack) { + if (onBackPressedListener != null && !parentDialog.get().isHide()) { + return onBackPressedListener.onBackPressed(); + } + } + return super.dispatchKeyEvent(event); + } + + boolean touch; + float touchDownX, touchDownY; + + @Override + public boolean onTouchEvent(MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + touch = true; + touchDownX = event.getX(); + touchDownY = event.getY(); + break; + case MotionEvent.ACTION_UP: + if (touch && findFocus() != this && getParentDialog() != null) { + float deltaTouchOffset = getParentDialog().dip2px(5); + if (Math.abs(event.getX() - touchDownX) <= deltaTouchOffset && Math.abs(event.getY() - touchDownY) <= deltaTouchOffset) { + callOnClick(); + } + } + break; + } + if (getParentDialog() instanceof NoTouchInterface) { + return super.onTouchEvent(event); + } + InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + return super.onTouchEvent(event); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (!isInEditMode()) { + if (getParentDialog() == null || getParentDialog().getOwnActivity() == null) return; + if (onLifecycleCallBack != null) { + onLifecycleCallBack.onShow(); + } + isLightMode = (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_NO; + if (focusable) { + requestFocus(); + } + } + } + + @Override + protected void onDetachedFromWindow() { + if (onLifecycleCallBack != null) { + onLifecycleCallBack.onDismiss(); + } + if (fitSystemBarUtils != null) { + fitSystemBarUtils.recycle(); + } + fitSystemBarUtils = null; + onSafeInsetsChangeListener = null; + super.onDetachedFromWindow(); + } + + @Override + public boolean performClick() { + if (!isEnabled()) return false; + return super.performClick(); + } + + @Override + public boolean callOnClick() { + if (!isEnabled()) return false; + return super.callOnClick(); + } + + public DialogXBaseRelativeLayout setOnLifecycleCallBack(OnLifecycleCallBack onLifecycleCallBack) { + this.onLifecycleCallBack = onLifecycleCallBack; + return this; + } + + public float getSafeHeight() { + return getMeasuredHeight() - unsafePlace.bottom - unsafePlace.top; + } + + private WeakReference requestFocusView; + + public void bindFocusView(View view) { + if (view != this) { + requestFocusView = new WeakReference<>(view); + } + } + + @Override + public boolean requestFocus(int direction, Rect previouslyFocusedRect) { + if (getParentDialog() != null && getParentDialog() instanceof NoTouchInterface) { + return false; + } + if (direction == View.FOCUS_DOWN && requestFocusView != null && requestFocusView.get() != null && requestFocusView.get() != this) { + return requestFocusView.get().requestFocus(); + } + View findFocusView = findFocus(); + if (findFocusView != null && findFocusView != this) { + findFocusView.requestFocus(); + return true; + } + return super.requestFocus(direction, previouslyFocusedRect); + } + + public void requestFocusOnResume() { + View findFocusView = findFocus(); + if (findFocusView != null && findFocusView != this) { + findFocusView.requestFocus(); + return; + } + requestFocus(); + } + + public abstract static class OnLifecycleCallBack { + public void onShow() { + } + + public abstract void onDismiss(); + } + + protected Rect unsafePlace = new Rect(); + + public DialogXBaseRelativeLayout setOnBackPressedListener(PrivateBackPressedListener onBackPressedListener) { + this.onBackPressedListener = onBackPressedListener; + return this; + } + + public OnSafeInsetsChangeListener getOnSafeInsetsChangeListener() { + return onSafeInsetsChangeListener; + } + + public DialogXBaseRelativeLayout setOnSafeInsetsChangeListener(OnSafeInsetsChangeListener onSafeInsetsChangeListener) { + this.onSafeInsetsChangeListener = onSafeInsetsChangeListener; + return this; + } + + public boolean isAutoUnsafePlacePadding() { + return autoUnsafePlacePadding; + } + + public Rect getUnsafePlace() { + return unsafePlace; + } + + public DialogXBaseRelativeLayout setAutoUnsafePlacePadding(boolean autoUnsafePlacePadding) { + this.autoUnsafePlacePadding = autoUnsafePlacePadding; + return this; + } + + public BaseDialog getParentDialog() { + return parentDialog == null ? null : parentDialog.get(); + } + + public DialogXBaseRelativeLayout setParentDialog(BaseDialog parentDialog) { + this.parentDialog = new WeakReference<>(parentDialog); + if (parentDialog != null && parentDialog.getDialogImplMode() != DialogX.IMPL_MODE.VIEW) { + setFitsSystemWindows(true); + } + if (unsafePlace != null) { + log("KONGZUE DEBUG DIALOGX: setParentDialog()=" + getParentDialog()); + setUnsafePadding(unsafePlace.left, unsafePlace.top, unsafePlace.right, unsafePlace.bottom); + } else { + log("KONGZUE DEBUG DIALOGX: setParentDialog() unsafePlace is null"); + } + return this; + } + + boolean isLightMode = true; + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + boolean newLightStatus = ((newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_NO); + if (isLightMode != newLightStatus && DialogX.globalTheme == DialogX.THEME.AUTO && getParentDialog() != null) { + getParentDialog().restartDialog(); + } + } + + float nowBkgAlphaValue; + + public DialogXBaseRelativeLayout setBkgAlpha(float alpha) { + nowBkgAlphaValue = alpha; + if (getBackground() != null) getBackground().mutate().setAlpha((int) (alpha * 255)); + return this; + } + + @Override + public void setBackground(Drawable background) { + background.setAlpha((int) (nowBkgAlphaValue * 255)); + super.setBackground(background); + } + + @Override + public void setBackgroundColor(int color) { + setBackground(new ColorDrawable(color)); + } + + public boolean isBaseFocusable() { + return focusable; + } + + public boolean isInterceptBack() { + return interceptBack; + } + + public DialogXBaseRelativeLayout setInterceptBack(boolean interceptBack) { + this.interceptBack = interceptBack; + return this; + } + + @Override + public void setVisibility(int visibility) { + if (visibility == GONE && getAlpha() == 0f) { + setAlpha(0.01f); + } + super.setVisibility(visibility); + } + + public interface PrivateBackPressedListener { + boolean onBackPressed(); + } + + int[] extraPadding = new int[4]; + + public void setRootPadding(int left, int top, int right, int bottom) { + extraPadding[0] = left; + extraPadding[1] = top; + extraPadding[2] = right; + extraPadding[3] = bottom; + } + + public int getRootPaddingLeft() { + return extraPadding[0]; + } + + public int getRootPaddingTop() { + return extraPadding[1]; + } + + public int getRootPaddingRight() { + return extraPadding[2]; + } + + public int getRootPaddingBottom() { + return extraPadding[3]; + } + + public int getUseAreaWidth() { + return getWidth() - getRootPaddingRight(); + } + + public int getUseAreaHeight() { + return getHeight() - getRootPaddingBottom(); + } + + protected void log(String s) { + if (debugMode && DialogX.DEBUGMODE) { + Log.e(">>>", s); + } + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/ExtendChildLayoutParamsFrameLayout.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/ExtendChildLayoutParamsFrameLayout.java new file mode 100644 index 0000000..9d6773f --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/ExtendChildLayoutParamsFrameLayout.java @@ -0,0 +1,31 @@ +package com.kongzue.dialogx.util.views; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.RelativeLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public class ExtendChildLayoutParamsFrameLayout extends FrameLayout { + public ExtendChildLayoutParamsFrameLayout(@NonNull Context context) { + super(context); + } + + public ExtendChildLayoutParamsFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public ExtendChildLayoutParamsFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + setLayoutParams(new RelativeLayout.LayoutParams(params.width, params.height)); + super.addView(child, index, params); + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/FitSystemBarUtils.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/FitSystemBarUtils.java new file mode 100644 index 0000000..38d5ebf --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/FitSystemBarUtils.java @@ -0,0 +1,637 @@ +package com.kongzue.dialogx.util.views; + +import static androidx.core.view.WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE; + +import android.app.Activity; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Rect; +import android.os.Build; +import android.util.Log; +import android.view.Surface; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.WindowInsets; +import android.view.WindowInsetsController; +import android.view.WindowManager; + +import androidx.annotation.NonNull; +import androidx.core.graphics.Insets; +import androidx.core.view.DisplayCutoutCompat; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsAnimationCompat; +import androidx.core.view.WindowInsetsCompat; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.interfaces.BaseDialog; + +import java.util.List; + +public class FitSystemBarUtils { + + //当前是否正在平滑填充中 + private boolean inSmoothingPadding = false; + + public boolean isInSmoothingPadding() { + return inSmoothingPadding; + } + + //是否需要处理挖孔屏,刘海屏 + public boolean safeCutOutPadding = true; + + //是否需要平滑填充,仅在android R及以上生效,默认开启 + public boolean smoothPadding = true; + + private View contentView; + + private CallBack callBack; + + private BaseDialog dialog; + + /** + * 绑定到View + * + * @param view 指定View + */ + public static FitSystemBarUtils attachView(View view) { + return attachView(view, new CallBack() { + @Override + public boolean isEnable(Orientation orientation) { + return true; + } + + @Override + public void unsafeRect(int start, int top, int end, int bottom) { + + } + + @Override + public int initialPadding(Orientation orientation) { + return 0; + } + }); + } + + /** + * 绑定到View + * + * @param view 指定View + * @param callBack 用于判断是否需要某条边的padding,会非常频繁的调用,所以不要在里面做需要耗时的操作,以免卡UI线程 + */ + public static FitSystemBarUtils attachView(View view, CallBack callBack) { + return new FitSystemBarUtils(view, callBack); + } + + + /** + * 绑定到View + * + * @param view 指定View + * @param start 是否需要paddingStart + * @param top 是否需要paddingTop + * @param end 是否需要paddingEnd + * @param bottom 是否需要paddingBottom + */ + public static FitSystemBarUtils attachView( + View view, + boolean start, + boolean top, + boolean end, + boolean bottom + ) { + return attachView(view, new CallBack() { + @Override + public boolean isEnable(Orientation orientation) { + switch (orientation) { + case Start: + return start; + case Top: + return top; + case End: + return end; + case Bottom: + return bottom; + } + return false; + } + + @Override + public void unsafeRect(int start, int top, int end, int bottom) { + + } + + @Override + public int initialPadding(Orientation orientation) { + return 0; + } + }); + } + + private FitSystemBarUtils() { + } + + public FitSystemBarUtils(View view, CallBack callBack) { +// view.setFitsSystemWindows(true); + contentView = view; + this.callBack = callBack; + if (view instanceof DialogXBaseRelativeLayout) { + dialog = ((DialogXBaseRelativeLayout) view).getParentDialog(); + } + applyWindowInsets(); + } + + View.OnLayoutChangeListener rootViewLayoutChangeListener; + + public void applyWindowInsets() { +// view.fitsSystemWindows = true + //创建原始padding的快照 + RelativePadding initialPadding = new RelativePadding( + ViewCompat.getPaddingStart(contentView), + contentView.getPaddingTop(), + ViewCompat.getPaddingEnd(contentView), + contentView.getPaddingBottom() + ); + //不带平滑变化的 + ViewCompat.setOnApplyWindowInsetsListener(contentView, (view, insets) -> { + if (inSmoothingPadding) return insets; + formatInsets(insets, new RelativePadding(initialPadding)); + return insets; + }); + //带平滑变化的,但是支支持android R及以上 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + log("FitSystemBarUtils: setWindowInsetsAnimationCallback"); + ViewCompat.setWindowInsetsAnimationCallback(contentView, + new WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_CONTINUE_ON_SUBTREE) { + + @NonNull + @Override + public WindowInsetsCompat onProgress( + @NonNull WindowInsetsCompat insets, + @NonNull List runningAnimations + ) { + log("FitSystemBarUtils: setWindowInsetsAnimationCallback#onProgress: " + insets); + if (smoothPadding) { + formatInsets(insets, new RelativePadding(initialPadding)); + } + return insets; + } + + @Override + public void onEnd(@NonNull WindowInsetsAnimationCompat animation) { + log("FitSystemBarUtils: setWindowInsetsAnimationCallback#onEnd "); + inSmoothingPadding = false; + super.onEnd(animation); + } + + @Override + public void onPrepare(@NonNull WindowInsetsAnimationCompat animation) { + inSmoothingPadding = smoothPadding; + super.onPrepare(animation); + } + }); + } + + if (ViewCompat.isAttachedToWindow(contentView)) { + log("FitSystemBarUtils: AttachedToWindow ok"); + ViewCompat.requestApplyInsets(contentView); + } else { + log("FitSystemBarUtils: wait AttachedToWindow"); + contentView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View view) { + view.removeOnAttachStateChangeListener(this); + + log("FitSystemBarUtils: onViewAttachedToWindow"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH && (Build.VERSION.SDK_INT < Build.VERSION_CODES.R || getAppTargetSDKVersion() < Build.VERSION_CODES.R)) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + //修复<=API29的部分设备上存在的非安全区不回调的问题 + View parentView = (View) view.getParent(); + if (rootViewLayoutChangeListener != null) { + parentView.removeOnLayoutChangeListener(rootViewLayoutChangeListener); + } + rootViewLayoutChangeListener = new View.OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { + WindowInsets windowInsets = v.getRootView().getRootWindowInsets(); + if (windowInsets != null) { + log(" FitSystemBarUtils: RootView get Insets"); + formatInsets(WindowInsetsCompat.toWindowInsetsCompat(windowInsets), new RelativePadding(initialPadding)); + } else { + log(" FitSystemBarUtils: RootView not get Insets"); + } + } + }; + parentView.addOnLayoutChangeListener(rootViewLayoutChangeListener); + parentView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + } + + @Override + public void onViewDetachedFromWindow(View v) { + parentView.removeOnLayoutChangeListener(rootViewLayoutChangeListener); + } + }); + } else { + View parentView = (View) view.getParent(); + ViewTreeObserver.OnPreDrawListener preDrawListener = new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + // 获取状态栏高度 + Rect insets = new Rect(); + parentView.getWindowVisibleDisplayFrame(insets); + int statusBarHeight = insets.top; + + // 获取导航栏高度 + int navigationBarHeight = 0; + Resources resources = parentView.getResources(); + int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android"); + if (resourceId > 0) { + navigationBarHeight = resources.getDimensionPixelSize(resourceId); + } + + log("FitSystemBarUtils: below Android M use support mode: statusBarHeight=" + statusBarHeight + ", navigationBarHeight=" + navigationBarHeight); + int deviceOrientation = checkOrientationAndStatusBarSide(); + log(" FitSystemBarUtils: deviceOrientation = " + deviceOrientation); + switch (deviceOrientation) { + case 1: + case -1: + initialPadding.end = navigationBarHeight; + initialPadding.start = 0; + break; + default: + initialPadding.top = statusBarHeight; + initialPadding.bottom = navigationBarHeight; + break; + } + + applyCallBack(initialPadding); + + parentView.getViewTreeObserver().removeOnPreDrawListener(this); + + return true; + } + }; + parentView.getViewTreeObserver().addOnPreDrawListener(preDrawListener); + + } + } + + ViewCompat.requestApplyInsets(view); + } + + @Override + public void onViewDetachedFromWindow(View view) { + + } + }); + } + } + + RelativePadding relativePaddingCache; + + /** + * 针对不同版本处理Insets + */ + private void formatInsets(WindowInsetsCompat insetsCompat, RelativePadding initialPadding) { + if (contentView == null || insetsCompat == null || initialPadding == null) return; + + relativePaddingCache = initialPadding; + int cutoutPaddingLeft = 0; + int cutoutPaddingTop = 0; + int cutoutPaddingRight = 0; + int cutoutPaddingBottom = 0; + + int systemWindowInsetLeft = 0; + int systemWindowInsetTop = 0; + int systemWindowInsetRight = 0; + int systemWindowInsetBottom = 0; + + if (safeCutOutPadding) { + DisplayCutoutCompat displayCutout = insetsCompat.getDisplayCutout(); + if (null != displayCutout) { + cutoutPaddingTop = displayCutout.getSafeInsetTop(); + cutoutPaddingLeft = displayCutout.getSafeInsetLeft(); + cutoutPaddingRight = displayCutout.getSafeInsetRight(); + cutoutPaddingBottom = displayCutout.getSafeInsetRight(); + } + } + Insets systemBars = insetsCompat.getInsets( + WindowInsetsCompat.Type.ime() | WindowInsetsCompat.Type.systemBars() + ); + systemWindowInsetLeft = systemBars.left; + systemWindowInsetRight = systemBars.right; + + //对api低于30的设备,做额外判断,api 30+的不需要这个 + int suv = contentView.getRootView().getWindowSystemUiVisibility(); + boolean statusBar = true; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + statusBar = (suv & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0; + } + boolean naviBar = true; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + naviBar = (suv & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0; + } + if (naviBar && (insetsCompat.isVisible(WindowInsetsCompat.Type.ime()) + || insetsCompat.isVisible(WindowInsetsCompat.Type.navigationBars())) + ) { + systemWindowInsetBottom = systemBars.bottom; + } + if (statusBar && insetsCompat.isVisible(WindowInsetsCompat.Type.statusBars())) { + systemWindowInsetTop = systemBars.top; + } + if (isWrongInsets(systemBars)) { + log(" FitSystemBarUtils: isWrongInsets try special mode..."); + int deviceOrientation = checkOrientationAndStatusBarSide(); + log(" FitSystemBarUtils: deviceOrientation = " + deviceOrientation); + switch (deviceOrientation) { + case 1: + initialPadding.end = getStatusBarHeight(); + initialPadding.start = getNavigationBarHeight(); + break; + default: + initialPadding.top = getStatusBarHeight(); + initialPadding.bottom = getNavigationBarHeight(); + break; + } + addListenerWhenImeHeightChanged(); + } else { + specialMode = false; + if (callBack.isEnable(Orientation.Top)) { + initialPadding.top += Math.max(systemWindowInsetTop, cutoutPaddingTop); + } + if (callBack.isEnable(Orientation.Bottom)) { + initialPadding.bottom += Math.max(systemWindowInsetBottom, cutoutPaddingBottom); + } + + boolean isRtl = + ViewCompat.getLayoutDirection(contentView) == ViewCompat.LAYOUT_DIRECTION_RTL; + if (callBack.isEnable(Orientation.Start)) { + if (isRtl) { + initialPadding.start += Math.max(systemWindowInsetRight, cutoutPaddingRight); + } else { + initialPadding.start += Math.max(systemWindowInsetLeft, cutoutPaddingLeft); + } + } + if (callBack.isEnable(Orientation.End)) { + if (isRtl) { + initialPadding.end += Math.max(systemWindowInsetLeft, cutoutPaddingLeft); + } else { + initialPadding.end += Math.max(systemWindowInsetRight, cutoutPaddingRight); + } + } + } + + applyCallBack(initialPadding); + } + + private void applyCallBack() { + if (relativePaddingCache != null) { + applyCallBack(relativePaddingCache); + } + } + + private void applyCallBack(RelativePadding initialPadding) { + if (callBack == null) { + return; + } + //加上用户自定义的 + initialPadding.start += callBack.initialPadding(Orientation.Start); + initialPadding.top += callBack.initialPadding(Orientation.Top); + initialPadding.end += callBack.initialPadding(Orientation.End); + initialPadding.bottom += callBack.initialPadding(Orientation.Bottom); + + initialPadding.applyToView(contentView); + //四边 非安全区 传递回去 + log(" KONGZUE DEBUG DIALOGX FitSystemBarUtils callBack: left=" + initialPadding.start + " top=" + initialPadding.top + + " right=" + initialPadding.end + " bottom=" + initialPadding.bottom + " specialMode=" + specialMode + + " specialModeImeHeight=" + specialModeImeHeight + ); + callBack.unsafeRect( + initialPadding.start, + initialPadding.top, + initialPadding.end, + initialPadding.bottom + (specialMode ? specialModeImeHeight : 0) + ); + } + + private boolean isWrongInsets(Insets systemBars) { + return systemBars.top == 0 && systemBars.bottom == 0 && systemBars.left == 0 && systemBars.right == 0; + } + + public interface CallBack { + + //是否启用指定边的insets + boolean isEnable(Orientation orientation); + + //非安全区 + void unsafeRect(int start, int top, int end, int bottom); + + //用户自定义的padding + int initialPadding(Orientation orientation); + } + + public static class RelativePadding { + int start; + int top; + int end; + int bottom; + + public RelativePadding(int start, int top, int end, int bottom) { + this.start = start; + this.top = top; + this.end = end; + this.bottom = bottom; + } + + public RelativePadding(RelativePadding other) { + start = other.start; + top = other.top; + end = other.end; + bottom = other.bottom; + } + + /** + * Applies this relative padding to the view. + */ + public void applyToView(View view) { + if (view instanceof DialogXBaseRelativeLayout) { + //((DialogXBaseRelativeLayout) view).setUnsafePadding(start, top, end, bottom); + } else { + ViewCompat.setPaddingRelative(view, start, top, end, bottom); + } + } + } + + enum Orientation { + Start, Top, End, Bottom + } + + protected void log(String s) { + if (DialogXBaseRelativeLayout.debugMode && DialogX.DEBUGMODE) { + Log.e(">>>", s); + } + } + + private int getStatusBarHeight() { + if (isFullScreen() || getDecorView() == null) { + return 0; + } + WindowInsetsController insetsController = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) ? getDecorView().getWindowInsetsController() : null; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && insetsController != null && (insetsController.getSystemBarsBehavior() & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) == 0) { + return 0; + } + Resources res; + if (contentView == null || contentView.getContext() == null) { + res = Resources.getSystem(); + } else { + res = contentView.getContext().getResources(); + } + int result = 0; + int resourceId = res.getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + result = res.getDimensionPixelSize(resourceId); + } + return result; + } + + private int getNavigationBarHeight() { + if (isFullScreen() || getDecorView() == null) { + return 0; + } + WindowInsetsController insetsController = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) ? getDecorView().getWindowInsetsController() : null; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && insetsController != null && (insetsController.getSystemBarsBehavior() & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) == 0) { + log("getNavigationBarHeight =0"); + return 0; + } + Resources res; + if (contentView == null || contentView.getContext() == null) { + res = Resources.getSystem(); + } else { + res = contentView.getContext().getResources(); + } + int result = 0; + int resourceId = res.getIdentifier("navigation_bar_height", "dimen", "android"); + if (resourceId > 0) { + result = res.getDimensionPixelSize(resourceId); + } + return result; + } + + private boolean isFullScreen() { + Activity activity = getActivity(); + if (activity == null) { + return false; + } + // 通过检查窗口标志来判断 + int flags = activity.getWindow().getAttributes().flags; + if ((flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0) { + return true; + } + // 通过检查系统 UI 标志来判断 + int uiOptions = activity.getWindow().getDecorView().getSystemUiVisibility(); + if ((uiOptions & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0) { + return true; + } + return false; + } + + private int checkOrientationAndStatusBarSide() { + Activity activity = getActivity(); + if (activity == null) { + return 0; + } + // 判断是否为横屏 + if (activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { + // 判断状态栏位置 + int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); + switch (rotation) { + case Surface.ROTATION_90: + // 设备旋转了 90 度,状态栏在左侧 + return -1; + case Surface.ROTATION_270: + // 设备旋转了 270 度,状态栏在右侧 + return 1; + default: + // 其他情况,不应当发生在横屏状态 + return 0; + } + } + return 0; + } + + private int specialModeImeHeight; + private boolean specialMode; + private ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener; + + private View getDecorView() { + Activity activity = getActivity(); + if (activity == null) { + return null; + } + return activity.getWindow().getDecorView(); + } + + private void addListenerWhenImeHeightChanged() { + specialMode = true; + View decorView = getDecorView(); + if (decorView == null) return; + if (onGlobalLayoutListener != null) { + decorView.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener); + } + decorView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + Rect r = new Rect(); + decorView.getWindowVisibleDisplayFrame(r); + int screenHeight = decorView.getHeight(); + WindowInsetsController insetsController = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) ? getDecorView().getWindowInsetsController() : null; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && insetsController != null && (insetsController.getSystemBarsBehavior() & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) == 0) { + r.bottom = screenHeight; + } + int keypadHeight = screenHeight - r.bottom; + if (keypadHeight != specialModeImeHeight) { + specialModeImeHeight = keypadHeight; + log(" FitSystemBarUtils: specialModeImeHeight=" + specialModeImeHeight); + applyCallBack(); + } + } + }); + } + + private int getAppTargetSDKVersion() { + try { + Context context = BaseDialog.getApplicationContext(); + if (context == null) { + return -1; + } + PackageManager pm = context.getPackageManager(); + ApplicationInfo applicationInfo = pm.getApplicationInfo(context.getPackageName(), 0); + int targetSdkVersion = applicationInfo.targetSdkVersion; + return targetSdkVersion; + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return -1; + } + + private Activity getActivity() { + if (dialog == null) return BaseDialog.getTopActivity(); + return dialog.getOwnActivity(); + } + + public void recycle() { + View decorView = getDecorView(); + if (decorView != null && onGlobalLayoutListener != null) { + decorView.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener); + } + onGlobalLayoutListener = null; + callBack = null; + contentView = null; + dialog = null; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/MaxLinearLayout.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/MaxLinearLayout.java new file mode 100644 index 0000000..dc37368 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/MaxLinearLayout.java @@ -0,0 +1,111 @@ +package com.kongzue.dialogx.util.views; + +import static android.view.View.MeasureSpec.EXACTLY; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; + +import androidx.annotation.Nullable; + +import com.kongzue.dialogx.R; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2021/8/26 11:01 + */ +public class MaxLinearLayout extends LinearLayout { + + private int maxWidth; + private int maxHeight; + private int minWidth; + private int minHeight; + + public MaxLinearLayout(Context context) { + super(context); + init(context, null); + } + + public MaxLinearLayout(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public MaxLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context, attrs); + } + + private void init(Context context, AttributeSet attrs) { + if (attrs != null) { + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DialogXMaxLayout); + maxWidth = a.getDimensionPixelSize(R.styleable.DialogXMaxLayout_maxLayoutWidth, 0); + maxHeight = a.getDimensionPixelSize(R.styleable.DialogXMaxLayout_maxLayoutHeight, 0); + minWidth = a.getDimensionPixelSize(R.styleable.DialogXMaxLayout_minLayoutWidth, 0); + minHeight = a.getDimensionPixelSize(R.styleable.DialogXMaxLayout_minLayoutHeight, 0); + + a.recycle(); + } + minWidth = minWidth == 0 ? getMinimumWidth() : minWidth; + minHeight = minHeight == 0 ? getMinimumHeight() : minHeight; + } + + public MaxLinearLayout setMaxHeight(int maxHeight) { + this.maxHeight = maxHeight; + return this; + } + + public MaxLinearLayout setMaxWidth(int maxWidth) { + this.maxWidth = maxWidth; + return this; + } + + private int preWidth = -1; + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + int widthSize = MeasureSpec.getSize(widthMeasureSpec); + + if (preWidth == -1 && widthSize != 0) { + preWidth = widthSize; + } + if (heightSize > maxHeight && maxHeight != 0) { + heightSize = maxHeight; + } + if (widthSize > maxWidth && maxWidth != 0) { + widthSize = maxWidth; + } + + int maxHeightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode); + int maxWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, widthMode); + super.onMeasure(maxWidthMeasureSpec, maxHeightMeasureSpec); + } + + public int dip2px(float dpValue) { + final float scale = getResources().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } + + @Override + public void setMinimumHeight(int minHeight) { + this.minHeight = minHeight; + super.setMinimumHeight(minHeight); + } + + @Override + public void setMinimumWidth(int minWidth) { + this.minWidth = minWidth; + super.setMinimumWidth(minWidth); + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/MaxRelativeLayout.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/MaxRelativeLayout.java new file mode 100644 index 0000000..5766b03 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/MaxRelativeLayout.java @@ -0,0 +1,223 @@ +package com.kongzue.dialogx.util.views; + +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.os.Build; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.widget.RelativeLayout; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.DialogXSafetyModeInterface; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2019/9/24 17:34 + */ +public class MaxRelativeLayout extends RelativeLayout implements DialogXSafetyModeInterface { + + private int maxWidth; + private int maxHeight; + private int minWidth; + private int minHeight; + private boolean lockWidth; + private boolean interceptTouch = true; + private View contentView; + private int dialogXSafetyMode; + + public MaxRelativeLayout(Context context) { + super(context); + init(context, null); + } + + public MaxRelativeLayout(Context context, AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public MaxRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context, attrs); + } + + private float startAnimValue = 0, endAnimValue = 0; + + private void init(Context context, AttributeSet attrs) { + if (attrs != null) { + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DialogXMaxLayout); + maxWidth = a.getDimensionPixelSize(R.styleable.DialogXMaxLayout_maxLayoutWidth, 0); + maxHeight = a.getDimensionPixelSize(R.styleable.DialogXMaxLayout_maxLayoutHeight, 0); + minWidth = a.getDimensionPixelSize(R.styleable.DialogXMaxLayout_minLayoutWidth, 0); + minHeight = a.getDimensionPixelSize(R.styleable.DialogXMaxLayout_minLayoutHeight, 0); + lockWidth = a.getBoolean(R.styleable.DialogXMaxLayout_lockWidth, false); + interceptTouch = a.getBoolean(R.styleable.DialogXMaxLayout_interceptTouch, true); + dialogXSafetyMode = a.getInt(R.styleable.DialogXMaxLayout_dialogXSafetyMode, 0); + a.recycle(); + } + minWidth = minWidth == 0 ? getMinimumWidth() : minWidth; + minHeight = minHeight == 0 ? getMinimumHeight() : minHeight; + + if (!isInEditMode()) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + animate().setUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float progress = (float) animation.getAnimatedValue(); + long value = (long) (startAnimValue + (endAnimValue - startAnimValue) * progress); + if (onYChangedListener != null) onYChangedListener.y(value); + } + }); + } + } + } + + public MaxRelativeLayout setMaxHeight(int maxHeight) { + if (maxHeight > 0) this.maxHeight = maxHeight; + return this; + } + + public MaxRelativeLayout setMaxWidth(int maxWidth) { + if (maxWidth > 0) this.maxWidth = maxWidth; + return this; + } + + public void setMinHeight(int minHeight) { + if (minHeight > 0) this.minHeight = minHeight; + } + + public void setMinWidth(int minWidth) { + if (minWidth > 0) this.minWidth = minWidth; + } + + private int preWidth = -1; + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + int widthSize = MeasureSpec.getSize(widthMeasureSpec); + + if (preWidth == -1 && widthSize != 0) { + preWidth = widthSize; + } + if (lockWidth) { + maxWidth = Math.min(maxWidth, Math.min(widthSize, preWidth)); + } + if (heightSize > maxHeight && maxHeight != 0) { + heightSize = maxHeight + getPaddingBottom() + getPaddingTop(); + } + if (widthSize > maxWidth && maxWidth != 0) { + widthSize = maxWidth + getPaddingLeft() + getPaddingRight(); + } + + int maxHeightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode); + int maxWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, widthMode); + super.onMeasure(maxWidthMeasureSpec, maxHeightMeasureSpec); + } + + public int dip2px(float dpValue) { + final float scale = getResources().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } + + public boolean isLockWidth() { + return lockWidth; + } + + public MaxRelativeLayout setLockWidth(boolean lockWidth) { + this.lockWidth = lockWidth; + return this; + } + + private OnYChanged onYChangedListener; + + int navBarHeight; + Paint navBarPaint; + + public void setNavBarHeight(int height) { + navBarHeight = height; + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (navBarHeight != 0 && DialogX.bottomDialogNavbarColor != 0) { + if (navBarPaint == null) { + navBarPaint = new Paint(); + navBarPaint.setColor(DialogX.bottomDialogNavbarColor); + } + canvas.drawRect(0, getHeight() - navBarHeight, getWidth(), getHeight(), navBarPaint); + } + } + + public void setContentView(View contentView) { + this.contentView = contentView; + } + + public interface OnYChanged { + void y(float y); + } + + @Override + public void setY(float y) { + super.setY(y); + } + + public OnYChanged getOnYChanged() { + return onYChangedListener; + } + + public MaxRelativeLayout setOnYChanged(OnYChanged onYChanged) { + this.onYChangedListener = onYChanged; + return this; + } + + @Override + public void setTranslationY(float translationY) { + super.setTranslationY(translationY); + if (onYChangedListener != null) onYChangedListener.y(translationY); + } + + private OnTouchListener onTouchListener; + + @Override + public void setOnTouchListener(OnTouchListener l) { + onTouchListener = l; + } + + boolean reInterceptTouch; + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (onTouchListener != null) { + reInterceptTouch = onTouchListener.onTouch(this, ev); + } + return super.dispatchTouchEvent(ev); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + return reInterceptTouch; + } + + public int getDialogXSafetyMode() { + return dialogXSafetyMode; + } + + public MaxRelativeLayout setDialogXSafetyMode(int dialogXSafetyMode) { + this.dialogXSafetyMode = dialogXSafetyMode; + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/NoArticulatedProgressView.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/NoArticulatedProgressView.java new file mode 100644 index 0000000..5172e8a --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/NoArticulatedProgressView.java @@ -0,0 +1,452 @@ +package com.kongzue.dialogx.util.views; + +import android.animation.TimeInterpolator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.HapticFeedbackConstants; +import android.view.View; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.LinearInterpolator; + +import androidx.annotation.Nullable; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.ProgressViewInterface; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2021/3/14 20:58 + */ +public class NoArticulatedProgressView extends View implements ProgressViewInterface { + public static final int STATUS_LOADING = 0; + public static final int STATUS_SUCCESS = 1; + public static final int STATUS_WARNING = 2; + public static final int STATUS_ERROR = 3; + public static final int STATUS_PROGRESSING = 4; + + private int status = STATUS_LOADING; + + private int width = dip2px(2); + private int color = Color.WHITE; + + public NoArticulatedProgressView(Context context) { + super(context); + init(null); + } + + public NoArticulatedProgressView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(attrs); + } + + public NoArticulatedProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(attrs); + } + + public NoArticulatedProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + init(attrs); + } + + private ValueAnimator rotateAnimator; + private ValueAnimator followAnimator; + + private float currentRotateDegrees; + private float followRotateDegrees; + + //跟随点度数做正弦值变化,halfSweepAMinValue为最低相较目标点度数差值,halfSweepAMaxValue为最大相较目标点度数差值 + private float halfSweepAMaxValue = 180; + private float halfSweepAMinValue = 80; + //正弦函数的半径 + private float halfSweepA; + + Paint mPaint = new Paint(); + + private boolean isInited = false; + + private void init(AttributeSet attrs) { + synchronized (NoArticulatedProgressView.class) { + if (isInited) { + return; + } + isInited = true; + if (attrs != null) { + TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ProgressView); + width = a.getDimensionPixelSize(R.styleable.ProgressView_progressStrokeWidth, dip2px(2)); + color = a.getDimensionPixelSize(R.styleable.ProgressView_progressStrokeColor, color); + + a.recycle(); + } + + mPaint.setAntiAlias(true); + mPaint.setStyle(Paint.Style.STROKE); + mPaint.setStrokeWidth(width); + mPaint.setStrokeCap(Paint.Cap.ROUND); + mPaint.setColor(color); + + if (!isInEditMode()) { + halfSweepA = (halfSweepAMaxValue - halfSweepAMinValue) / 2; + + rotateAnimator = ValueAnimator.ofFloat(0, 365); + rotateAnimator.setDuration(1000); + rotateAnimator.setInterpolator(new LinearInterpolator()); + rotateAnimator.setRepeatCount(-1); + rotateAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + currentRotateDegrees = (float) animation.getAnimatedValue(); + invalidate(); + } + }); + + followAnimator = ValueAnimator.ofFloat(0, 365); + followAnimator.setDuration(1500); + followAnimator.setInterpolator(new LinearInterpolator()); + followAnimator.setRepeatCount(-1); + followAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + followRotateDegrees = (float) animation.getAnimatedValue(); + } + }); + + followAnimator.start(); + rotateAnimator.start(); + } + } + } + + //旋转圆的中心坐标 + private float mCenterX; + private float mCenterY; + //半径 + private float mRadius = 100; + private RectF oval; + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + mCenterX = w * 1f / 2; + mCenterY = h * 1f / 2; + mRadius = Math.min(getWidth(), getHeight()) / 2 - width / 2; + oval = new RectF(mCenterX - mRadius, mCenterY - mRadius, mCenterX + mRadius, mCenterY + mRadius); + } + + protected float oldAnimAngle; + private int successStep = 0; + + @Override + protected void onDraw(Canvas canvas) { + if (isInEditMode()) { + canvas.drawArc(oval, 0, 365, false, mPaint); + return; + } + if (noShowLoading) { + canvas.drawArc(oval, 0, 365, false, mPaint); + successStep = 2; + drawDoneMark(status, canvas); + return; + } + + float sweepAngle = (float) (halfSweepA * Math.sin(Math.toRadians(followRotateDegrees))) + halfSweepA + halfSweepAMinValue / 2; + switch (status) { + case STATUS_LOADING: + canvas.drawArc(oval, currentRotateDegrees, -sweepAngle, false, mPaint); + break; + case STATUS_SUCCESS: + case STATUS_WARNING: + case STATUS_ERROR: + canvas.drawArc(oval, 0, 360, false, mPaint); + drawDoneMark(status, canvas); + break; + case STATUS_PROGRESSING: + canvas.drawArc(oval, -90, currentRotateDegrees, false, mPaint); + if (waitProgressingRunnable != null) { + waitProgressingRunnable.run(); + waitProgressingRunnable = null; + } + break; + } + } + + private void drawDoneMark(int status, Canvas canvas) { + if (rotateAnimator.getInterpolator() != interpolator) { + rotateAnimator.setInterpolator(interpolator); + } + if (tickShowRunnable != null) { + tickShowRunnable.run(); + if (DialogX.useHaptic)performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + tickShowRunnable = null; + } + switch (status) { + case STATUS_SUCCESS: + showSuccessTick(canvas); + break; + case STATUS_WARNING: + showWarningTick(canvas); + break; + case STATUS_ERROR: + showErrorTick(canvas); + break; + } + } + + private int line1X = 0; + private int line1Y = 0; + private int line2X = 0; + private int line2Y = 0; + + private int tickStep = 0; + + //绘制对号 + private void showSuccessTick(Canvas canvas) { + int tickLeftPoint = (int) (mCenterX - mRadius * 1 / 2); + int tickTurnLeftPoint = (int) (mCenterX - mRadius / 10); + int tickRightPoint = (int) (mRadius * 0.99f); + int speed = 2; + switch (tickStep) { + case 0: + if (tickLeftPoint + line1X < tickTurnLeftPoint) { + line1X = line1X + speed; + line1Y = line1Y + speed; + } else { + line2X = line1X; + line2Y = line1Y; + tickStep = 1; + } + break; + case 1: + if (line2X < tickRightPoint) { + line2X = line2X + 4; + line2Y = line2Y - 5; + } + break; + } + canvas.drawLine(tickLeftPoint, mCenterY, tickLeftPoint + line1X, mCenterY + line1Y, mPaint); + canvas.drawLine(tickLeftPoint + line1X, mCenterY + line1Y, tickLeftPoint + line2X, mCenterY + line2Y, mPaint); + + postInvalidateDelayed(1); + } + + //绘制感叹号 + private void showWarningTick(Canvas canvas) { + int tickLeftPoint = (int) mCenterX; + int line1StartY = (int) (mCenterY - mRadius * 1 / 2); + int line1EndY = (int) (mCenterY + mRadius * 1 / 8); + int line2StartY = (int) (mCenterY + mRadius * 3 / 7); + int speed = 4; + switch (tickStep) { + case 0: + if (line1Y < line1EndY - line1StartY) { + line1Y = line1Y + speed; + } else { + line1Y = line1EndY - line1StartY; + tickStep = 1; + } + break; + case 1: + if (line2Y != line2StartY) { + canvas.drawLine(tickLeftPoint, line2StartY, tickLeftPoint, line2StartY + 1, mPaint); + } + break; + } + canvas.drawLine(tickLeftPoint, line1StartY, tickLeftPoint, line1StartY + line1Y, mPaint); + postInvalidateDelayed(tickStep == 1 ? 100 : 1); + } + + //绘制错误符号 + private void showErrorTick(Canvas canvas) { + int tickLeftPoint = (int) (mCenterX - mRadius * 4 / 10); + int tickRightPoint = (int) (mCenterX + mRadius * 4 / 10); + int tickTopPoint = (int) (mCenterY - mRadius * 4 / 10); + int speed = 4; + + switch (tickStep) { + case 0: + if (tickRightPoint - line1X > tickLeftPoint) { + line1X = line1X + speed; + line1Y = line1Y + speed; + } else { + tickStep = 1; + canvas.drawLine(tickRightPoint, tickTopPoint, tickRightPoint - line1X, tickTopPoint + line1Y, mPaint); + postInvalidateDelayed(150); + return; + } + break; + case 1: + if (tickLeftPoint + line2X < tickRightPoint) { + line2X = line2X + speed; + line2Y = line2Y + speed; + } + canvas.drawLine(tickLeftPoint, tickTopPoint, tickLeftPoint + line2X, tickTopPoint + line2Y, mPaint); + break; + } + canvas.drawLine(tickRightPoint, tickTopPoint, tickRightPoint - line1X, tickTopPoint + line1Y, mPaint); + postInvalidateDelayed(1); + } + + private TimeInterpolator interpolator; + private Runnable waitProgressingRunnable; + + public void success() { + if (status == STATUS_PROGRESSING) { + progress(1f); + waitProgressingRunnable = new Runnable() { + @Override + public void run() { + tickStep = 0; + successStep = 2; + interpolator = new AccelerateDecelerateInterpolator(); + status = STATUS_SUCCESS; + } + }; + return; + } + tickStep = 0; + interpolator = new AccelerateDecelerateInterpolator(); + status = STATUS_SUCCESS; + invalidate(); + } + + public void warning() { + if (status == STATUS_PROGRESSING) { + progress(1f); + waitProgressingRunnable = new Runnable() { + @Override + public void run() { + tickStep = 0; + successStep = 2; + interpolator = new DecelerateInterpolator(2); + status = STATUS_WARNING; + } + }; + return; + } + tickStep = 0; + interpolator = new DecelerateInterpolator(2); + status = STATUS_WARNING; + invalidate(); + } + + public void error() { + if (status == STATUS_PROGRESSING) { + progress(1f); + waitProgressingRunnable = new Runnable() { + @Override + public void run() { + tickStep = 0; + successStep = 2; + interpolator = new DecelerateInterpolator(2); + status = STATUS_ERROR; + } + }; + return; + } + tickStep = 0; + interpolator = new DecelerateInterpolator(2); + status = STATUS_ERROR; + invalidate(); + } + + public void progress(float progress) { + if (rotateAnimator != null) rotateAnimator.cancel(); + if (followAnimator != null) followAnimator.cancel(); + if (status != STATUS_PROGRESSING) { + currentRotateDegrees = 0; + } + rotateAnimator = ValueAnimator.ofFloat(currentRotateDegrees, 365 * progress); + rotateAnimator.setDuration(1000); + rotateAnimator.setInterpolator(new DecelerateInterpolator(2)); + rotateAnimator.setRepeatCount(0); + rotateAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + currentRotateDegrees = (float) animation.getAnimatedValue(); + invalidate(); + } + }); + rotateAnimator.start(); + status = STATUS_PROGRESSING; + } + + private Runnable tickShowRunnable; + + public NoArticulatedProgressView whenShowTick(Runnable runnable) { + tickShowRunnable = runnable; + return this; + } + + public void loading() { + noShowLoading = false; + oldAnimAngle = 0; + successStep = 0; + line1X = 0; + line1Y = 0; + line2X = 0; + line2Y = 0; + status = STATUS_LOADING; + if (rotateAnimator != null) rotateAnimator.cancel(); + if (followAnimator != null) followAnimator.cancel(); + isInited = false; + init(null); + } + + public int getStatus() { + return status; + } + + @Override + protected void onDetachedFromWindow() { + if (rotateAnimator != null) { + rotateAnimator.cancel(); + } + if (followAnimator != null) { + followAnimator.cancel(); + } + super.onDetachedFromWindow(); + } + + public int getStrokeWidth() { + return width; + } + + public NoArticulatedProgressView setStrokeWidth(int width) { + this.width = width; + if (mPaint != null) mPaint.setStrokeWidth(width); + return this; + } + + public int getColor() { + return color; + } + + public NoArticulatedProgressView setColor(int color) { + this.color = color; + if (mPaint != null) mPaint.setColor(color); + return this; + } + + private boolean noShowLoading; + + public void noLoading() { + noShowLoading = true; + } + + private int dip2px(float dpValue) { + final float scale = Resources.getSystem().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/PopMenuListView.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/PopMenuListView.java new file mode 100644 index 0000000..319aaf6 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/PopMenuListView.java @@ -0,0 +1,66 @@ +package com.kongzue.dialogx.util.views; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.BaseAdapter; +import android.widget.ListView; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2021/8/18 17:48 + */ +public class PopMenuListView extends ListView { + + private float maxHeight = -1; + + public PopMenuListView(Context context) { + super(context); + } + + public PopMenuListView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public PopMenuListView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public boolean isCanScroll() { + boolean canScroll; + int count = getCount(); + int firstVisiblePosition = getFirstVisiblePosition(); + int lastVisiblePosition = getLastVisiblePosition(); + canScroll = firstVisiblePosition != 0 || count != lastVisiblePosition + 1; + return canScroll; + } + + public float getMaxHeight() { + return maxHeight; + } + + public PopMenuListView setMaxHeight(float maxHeight) { + this.maxHeight = maxHeight; + return this; + } + + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int specSize = MeasureSpec.getSize(heightMeasureSpec); + if (maxHeight <= specSize && maxHeight > -1) { + heightMeasureSpec = MeasureSpec.makeMeasureSpec(Float.valueOf(maxHeight).intValue(), MeasureSpec.AT_MOST); + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + protected void layoutChildren() { + try { + super.layoutChildren(); + } catch (IllegalStateException e) { + ((BaseAdapter) getAdapter()).notifyDataSetChanged(); + super.layoutChildren(); + } + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/ProgressView.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/ProgressView.java new file mode 100644 index 0000000..8d2e8f3 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/ProgressView.java @@ -0,0 +1,540 @@ +package com.kongzue.dialogx.util.views; + +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RectF; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Display; +import android.view.HapticFeedbackConstants; +import android.view.View; +import android.view.WindowManager; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; + +import androidx.annotation.Nullable; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.ProgressViewInterface; +import com.kongzue.dialogx.util.DialogXValueAnimator; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/27 16:16 + * @license: Apache License 2.0 + */ +public class ProgressView extends View implements ProgressViewInterface { + + //提示动画持续时间 + public static long TIP_ANIMATOR_DURATION = 300; + //进度动画中的逐渐跟随动画时长 + public static long PROGRESSING_ANIMATOR_DURATION = 1000; + + public static final int STATUS_LOADING = 0; + public static final int STATUS_SUCCESS = 1; + public static final int STATUS_WARNING = 2; + public static final int STATUS_ERROR = 3; + public static final int STATUS_PROGRESSING = 4; + + private int status = STATUS_LOADING; + + private int width = dip2px(2); + private int color = Color.WHITE; + + public ProgressView(Context context) { + super(context); + init(null); + } + + public ProgressView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(attrs); + } + + public ProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(attrs); + } + + private DialogXValueAnimator rotateAnimator; + private DialogXValueAnimator followAnimator; + + private float currentRotateDegrees; + private float followRotateDegrees; + + //跟随点度数做正弦值变化,halfSweepAMinValue为最低相较目标点度数差值,halfSweepAMaxValue为最大相较目标点度数差值 + private float halfSweepAMaxValue = 180; + private float halfSweepAMinValue = 80; + //正弦函数的半径 + private float halfSweepA; + + Paint mPaint = new Paint(); + + private boolean isInited = false; + + private void init(AttributeSet attrs) { + synchronized (ProgressView.class) { + if (isInited) { + return; + } + isInited = true; + if (attrs != null) { + TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ProgressView); + width = a.getDimensionPixelSize(R.styleable.ProgressView_progressStrokeWidth, dip2px(2)); + color = a.getDimensionPixelSize(R.styleable.ProgressView_progressStrokeColor, color); + + a.recycle(); + } + + mPaint.setAntiAlias(true); + mPaint.setStyle(Paint.Style.STROKE); + mPaint.setStrokeWidth(width); + mPaint.setStrokeCap(Paint.Cap.ROUND); + mPaint.setColor(color); + + if (!isInEditMode()) { + int refreshInterval = (int) calculateMillisPerFrame(getContext()); + + halfSweepA = (halfSweepAMaxValue - halfSweepAMinValue) / 2; + + rotateAnimator = DialogXValueAnimator.ofFloat(0, 365); + rotateAnimator.setDuration(1000); + rotateAnimator.setInterpolator(new LinearInterpolator()); + rotateAnimator.setRepeatCount(-1); + rotateAnimator.setRefreshInterval(refreshInterval); + rotateAnimator.addUpdateListener(new DialogXValueAnimator.ValueUpdateListener() { + @Override + public void onValueUpdate(float animatedValue) { + if (!isAttachedToWindow()){ + return; + } + currentRotateDegrees = animatedValue; + invalidate(); + } + }); + + followAnimator = DialogXValueAnimator.ofFloat(0, 365); + followAnimator.setDuration(1500); + followAnimator.setRefreshInterval(refreshInterval); + followAnimator.setInterpolator(new LinearInterpolator()); + followAnimator.setRepeatCount(-1); + followAnimator.addUpdateListener(new DialogXValueAnimator.ValueUpdateListener() { + @Override + public void onValueUpdate(float animatedValue) { + followRotateDegrees = animatedValue; + } + }); + + followAnimator.start(); + rotateAnimator.start(); + } + } + } + + //旋转圆的中心坐标 + private float mCenterX; + private float mCenterY; + //半径 + private float mRadius = 100; + private RectF oval; + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + mCenterX = w * 1f / 2; + mCenterY = h * 1f / 2; + mRadius = Math.min(getWidth(), getHeight()) / 2 - width / 2; + oval = new RectF(mCenterX - mRadius, mCenterY - mRadius, mCenterX + mRadius, mCenterY + mRadius); + } + + private int successStep = 0; + private float nowLoadingProgressValue; + private float nowLoadingProgressEndAngle; + private float changeStatusAngle; + + @Override + protected void onDraw(Canvas canvas) { + if (isInEditMode()) { + canvas.drawArc(oval, 0, 365, false, mPaint); + return; + } + if (noShowLoading) { + canvas.drawArc(oval, 0, 365, false, mPaint); + successStep = 2; + drawDoneMark(status, canvas); + return; + } + switch (status) { + case STATUS_LOADING: + float sweepAngle = (float) (halfSweepA * Math.sin(Math.toRadians(followRotateDegrees))) + halfSweepA + halfSweepAMinValue / 2; + nowLoadingProgressValue = currentRotateDegrees - sweepAngle; + if (nowLoadingProgressValue < 0) { + nowLoadingProgressValue = 360 + nowLoadingProgressValue; + } + nowLoadingProgressEndAngle = sweepAngle; + changeStatusAngle = sweepAngle < 0 ? 360 - sweepAngle : sweepAngle; + canvas.drawArc(oval, currentRotateDegrees, -sweepAngle, false, mPaint); + break; + case STATUS_SUCCESS: + case STATUS_WARNING: + case STATUS_ERROR: + switch (successStep) { + case 0: + nowLoadingProgressEndAngle = nowLoadingProgressEndAngle + 5; + canvas.drawArc(oval, nowLoadingProgressValue, nowLoadingProgressEndAngle, false, mPaint); + + if (nowLoadingProgressEndAngle - (360 - changeStatusAngle) >= nowLoadingProgressValue) { + successStep = 1; + if (waitArticulationAnimationRunnable != null) { + waitArticulationAnimationRunnable.run(); + waitArticulationAnimationRunnable = null; + } + } + break; + case 1: + canvas.drawArc(oval, 0, 360, false, mPaint); + drawDoneMark(status, canvas); + break; + } + break; + case STATUS_PROGRESSING: + switch (successStep) { + case 0: + canvas.drawArc(oval, -90, currentRotateDegrees, false, mPaint); + if (currentRotateDegrees == 365) { + successStep = 1; + if (waitArticulationAnimationRunnable != null) { + waitArticulationAnimationRunnable.run(); + waitArticulationAnimationRunnable = null; + } + } + break; + case 1: + canvas.drawArc(oval, 0, 360, false, mPaint); + drawDoneMark(status, canvas); + break; + } + } + } + + private void drawDoneMark(int status, Canvas canvas) { + if (rotateAnimator.getInterpolator() != interpolator) { + rotateAnimator.setInterpolator(interpolator); + } + if (tickShowRunnable != null) { + tickShowRunnable.run(); + tickShowRunnable = null; + + if (DialogX.useHaptic) { + switch (status) { + case STATUS_SUCCESS: + performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + break; + case STATUS_WARNING: + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + @Override + public void run() { + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + } + }, (long) (TIP_ANIMATOR_DURATION * 0.8f)); + break; + case STATUS_ERROR: + performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + @Override + public void run() { + performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + } + }, (long) (TIP_ANIMATOR_DURATION * 0.5f)); + break; + } + } + } + switch (status) { + case STATUS_SUCCESS: + showSuccessTick(canvas); + break; + case STATUS_WARNING: + showWarningTick(canvas); + break; + case STATUS_ERROR: + showErrorTick(canvas); + break; + } + } + + private int line1X = 0; + private int line1Y = 0; + private int line2X = 0; + private int line2Y = 0; + + private ValueAnimator tickAnimator; + private float tickAnimatorValue; + + //绘制对号 + private void showSuccessTick(Canvas canvas) { + int verticalAxisOffset = (int) (mRadius / 20); //纵轴向下偏移量 + int tickTurnLeftPoint = (int) (mCenterX - mRadius / 10 - verticalAxisOffset); //转折点 + int startX = (int) (mCenterX - mRadius / 2); + int startY = (int) (mCenterY + verticalAxisOffset); + int endX = (int) (mCenterX + mRadius / 2); + int tickAnimatorX = (int) (startX + ((endX - startX) * tickAnimatorValue)); + + Path path = new Path(); + path.moveTo(startX, startY); + if (tickAnimatorX < tickTurnLeftPoint) { + line1X = tickAnimatorX; + line1Y = (int) (startY + (tickAnimatorX - startX)); + path.lineTo(line1X, line1Y); + } else { + line1X = tickTurnLeftPoint; + line1Y = (int) (startY + (line1X - startX)); + path.lineTo(line1X, line1Y); + + line2X = tickAnimatorX; + line2Y = line1Y - (tickAnimatorX - line1X); + path.lineTo(line2X, line2Y); + } + canvas.drawPath(path, mPaint); + } + + //绘制感叹号 + private void showWarningTick(Canvas canvas) { + int x = (int) mCenterX; + int startY = (int) (mCenterY - mRadius * 1 / 2); + int endY = (int) (mCenterY + mRadius * 1 / 8); + int line2Y = (int) (mCenterY + mRadius * 3 / 7); + + if (tickAnimatorValue < 0.9f) { + canvas.drawLine(x, startY, x, startY + (endY - startY) * tickAnimatorValue, mPaint); + } else { + canvas.drawLine(x, startY, x, endY, mPaint); + canvas.drawLine(x, line2Y, x, line2Y + 1, mPaint); + } + } + + //绘制错误符号 + private void showErrorTick(Canvas canvas) { + int start = (int) (mCenterY - mRadius * 4 / 10); + int end = (int) (mCenterX + mRadius * 4 / 10); + + if (tickAnimatorValue < 0.5f) { + line1X = (int) (start + (tickAnimatorValue * 2) * (end - start)); + line1Y = (int) (start + (tickAnimatorValue * 2) * (end - start)); + canvas.drawLine(start, start, line1X, line1Y, mPaint); + } else { + line1X = (int) (start + (tickAnimatorValue * 2) * (end - start)); + line1Y = (int) (start + (tickAnimatorValue * 2) * (end - start)); + canvas.drawLine(start, start, end, end, mPaint); + + line2X = (int) (end - ((tickAnimatorValue - 0.5f) * 2) * (end - start)); + line2Y = (int) (start + ((tickAnimatorValue - 0.5f) * 2) * (end - start)); + canvas.drawLine(end, start, line2X, line2Y, mPaint); + } + } + + private Interpolator interpolator; + + public void success() { + if (status == STATUS_PROGRESSING) { + progress(1f); + waitArticulationAnimationRunnable = new Runnable() { + @Override + public void run() { + initTipAnimator(STATUS_SUCCESS, new AccelerateDecelerateInterpolator()); + } + }; + return; + } + initTipAnimator(STATUS_SUCCESS, new AccelerateDecelerateInterpolator()); + } + + public void warning() { + if (status == STATUS_PROGRESSING) { + progress(1f); + waitArticulationAnimationRunnable = new Runnable() { + @Override + public void run() { + initTipAnimator(STATUS_WARNING, new AccelerateInterpolator(2f)); + } + }; + return; + } + initTipAnimator(STATUS_WARNING, new AccelerateInterpolator(2f)); + } + + public void error() { + if (status == STATUS_PROGRESSING) { + progress(1f); + waitArticulationAnimationRunnable = new Runnable() { + @Override + public void run() { + initTipAnimator(STATUS_ERROR, new DecelerateInterpolator(2)); + } + }; + return; + } + initTipAnimator(STATUS_ERROR, new DecelerateInterpolator(2)); + } + + Runnable waitArticulationAnimationRunnable; //等待衔接完成后再执行 + + private void initTipAnimator(int s, Interpolator i) { + interpolator = i; + status = s; + if (successStep == 0) { + waitArticulationAnimationRunnable = new Runnable() { + @Override + public void run() { + initTipAnimator(status, interpolator); + } + }; + return; + } + + if (tickAnimator != null) { + tickAnimator.cancel(); + tickAnimator = null; + } + tickAnimatorValue = 0; + tickAnimator = ValueAnimator.ofFloat(0f, 1f); + tickAnimator.setDuration(TIP_ANIMATOR_DURATION); + tickAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + tickAnimatorValue = (float) animation.getAnimatedValue(); + invalidate(); + } + }); + tickAnimator.start(); + } + + public void progress(float progress) { + if (rotateAnimator != null) rotateAnimator.cancel(); + if (followAnimator != null) followAnimator.cancel(); + if (status != STATUS_PROGRESSING) { + currentRotateDegrees = 0; + } + noShowLoading = false; + status = STATUS_PROGRESSING; + rotateAnimator = DialogXValueAnimator.ofFloat(currentRotateDegrees, 365 * progress); + rotateAnimator.setDuration(PROGRESSING_ANIMATOR_DURATION); + rotateAnimator.setInterpolator(new DecelerateInterpolator(2)); + rotateAnimator.setRepeatCount(0); + rotateAnimator.addUpdateListener(new DialogXValueAnimator.ValueUpdateListener() { + @Override + public void onValueUpdate(float animatedValue) { + currentRotateDegrees = animatedValue; + invalidate(); + } + }); + rotateAnimator.start(); + } + + private Runnable tickShowRunnable; + + public ProgressView whenShowTick(Runnable runnable) { + tickShowRunnable = runnable; + return this; + } + + public void loading() { + noShowLoading = false; + successStep = 0; + line1X = 0; + line1Y = 0; + line2X = 0; + line2Y = 0; + status = STATUS_LOADING; + if (rotateAnimator != null) rotateAnimator.cancel(); + if (followAnimator != null) followAnimator.cancel(); + isInited = false; + init(null); + } + + public int getStatus() { + return status; + } + + @Override + protected void onDetachedFromWindow() { + if (rotateAnimator != null) { + rotateAnimator.cancel(); + } + if (followAnimator != null) { + followAnimator.cancel(); + } + super.onDetachedFromWindow(); + } + + public int getStrokeWidth() { + return width; + } + + public ProgressView setStrokeWidth(int width) { + this.width = width; + if (mPaint != null) mPaint.setStrokeWidth(width); + return this; + } + + public int getColor() { + return color; + } + + public ProgressView setColor(int color) { + this.color = color; + if (mPaint != null) mPaint.setColor(color); + return this; + } + + private boolean noShowLoading; + + public void noLoading() { + noShowLoading = true; + } + + private int dip2px(float dpValue) { + final float scale = Resources.getSystem().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } + + private float getRefreshRate(Context context) { + float refreshRate = 60.0f; // 默认值 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + Display display = context.getSystemService(WindowManager.class).getDefaultDisplay(); + Display.Mode mode = display.getMode(); + refreshRate = mode.getRefreshRate(); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); + refreshRate = display.getRefreshRate(); + } + + return refreshRate; + } + + private long calculateMillisPerFrame(Context context) { + float refreshRate = getRefreshRate(context); + + if (refreshRate > 0) { + return (long) (1000.0 / refreshRate); + } else { + return 16; // 获取刷新率失败 + } + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/RoundView.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/RoundView.java new file mode 100644 index 0000000..c457993 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/RoundView.java @@ -0,0 +1,56 @@ +package com.kongzue.dialogx.util.views; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Path; +import android.graphics.Rect; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.widget.RelativeLayout; + +public class RoundView extends RelativeLayout { + + private float mRadius = 0; + private Path mBoundPath = null; + + public RoundView(Context context) { + this(context, null); + } + + public RoundView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public RoundView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setWillNotDraw(false); + mRadius = 50; + } + + public void setRadius(float radius) { + if (mRadius == radius) + return; + this.mRadius = radius; + postInvalidate(); + } + + public float getRadius() { + return mRadius; + } + + public void draw(Canvas canvas) { + Rect rect = new Rect(); + getLocalVisibleRect(rect); + mBoundPath = caculateRoundRectPath(rect); + canvas.clipPath(mBoundPath); + super.draw(canvas); + } + + private Path caculateRoundRectPath(Rect r) { + Path path = new Path(); + float radius = getRadius(); + float elevation = 0; + path.addRoundRect(new RectF(r.left + elevation, r.top + elevation, r.right - elevation, r.bottom - elevation), radius, radius, Path.Direction.CW); + return path; + } +} \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_alpha_enter.xml b/DialogX/src/main/res/anim/anim_dialogx_alpha_enter.xml new file mode 100644 index 0000000..a5e3c05 --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_alpha_enter.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_bottom_enter.xml b/DialogX/src/main/res/anim/anim_dialogx_bottom_enter.xml new file mode 100644 index 0000000..815d566 --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_bottom_enter.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_bottom_exit.xml b/DialogX/src/main/res/anim/anim_dialogx_bottom_exit.xml new file mode 100644 index 0000000..d60af3f --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_bottom_exit.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_default_alpha_enter.xml b/DialogX/src/main/res/anim/anim_dialogx_default_alpha_enter.xml new file mode 100644 index 0000000..f42dab9 --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_default_alpha_enter.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_default_enter.xml b/DialogX/src/main/res/anim/anim_dialogx_default_enter.xml new file mode 100644 index 0000000..6d3cae4 --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_default_enter.xml @@ -0,0 +1,18 @@ + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_default_exit.xml b/DialogX/src/main/res/anim/anim_dialogx_default_exit.xml new file mode 100644 index 0000000..9b62ada --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_default_exit.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_left_enter.xml b/DialogX/src/main/res/anim/anim_dialogx_left_enter.xml new file mode 100644 index 0000000..d3b1c6a --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_left_enter.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_left_exit.xml b/DialogX/src/main/res/anim/anim_dialogx_left_exit.xml new file mode 100644 index 0000000..60fe2ab --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_left_exit.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_notification_enter.xml b/DialogX/src/main/res/anim/anim_dialogx_notification_enter.xml new file mode 100644 index 0000000..2c77041 --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_notification_enter.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_notification_exit.xml b/DialogX/src/main/res/anim/anim_dialogx_notification_exit.xml new file mode 100644 index 0000000..93e7244 --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_notification_exit.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_right_enter.xml b/DialogX/src/main/res/anim/anim_dialogx_right_enter.xml new file mode 100644 index 0000000..3746a76 --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_right_enter.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_right_exit.xml b/DialogX/src/main/res/anim/anim_dialogx_right_exit.xml new file mode 100644 index 0000000..be93c12 --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_right_exit.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_top_enter.xml b/DialogX/src/main/res/anim/anim_dialogx_top_enter.xml new file mode 100644 index 0000000..95efebb --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_top_enter.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_top_exit.xml b/DialogX/src/main/res/anim/anim_dialogx_top_exit.xml new file mode 100644 index 0000000..771dbfa --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_top_exit.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable-v21/button_dialogx_material_light.xml b/DialogX/src/main/res/drawable-v21/button_dialogx_material_light.xml new file mode 100644 index 0000000..65e5908 --- /dev/null +++ b/DialogX/src/main/res/drawable-v21/button_dialogx_material_light.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable-v21/button_dialogx_material_night.xml b/DialogX/src/main/res/drawable-v21/button_dialogx_material_night.xml new file mode 100644 index 0000000..e2f8fb7 --- /dev/null +++ b/DialogX/src/main/res/drawable-v21/button_dialogx_material_night.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/button_dialogx_material_light.xml b/DialogX/src/main/res/drawable/button_dialogx_material_light.xml new file mode 100644 index 0000000..8cb8c1d --- /dev/null +++ b/DialogX/src/main/res/drawable/button_dialogx_material_light.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/button_dialogx_material_night.xml b/DialogX/src/main/res/drawable/button_dialogx_material_night.xml new file mode 100644 index 0000000..5157816 --- /dev/null +++ b/DialogX/src/main/res/drawable/button_dialogx_material_night.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/rect_dialogx_defalut_edittxt_cursor.xml b/DialogX/src/main/res/drawable/rect_dialogx_defalut_edittxt_cursor.xml new file mode 100644 index 0000000..6bdd322 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_defalut_edittxt_cursor.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/rect_dialogx_low_api_material_button_press.xml b/DialogX/src/main/res/drawable/rect_dialogx_low_api_material_button_press.xml new file mode 100644 index 0000000..07c0254 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_low_api_material_button_press.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/rect_dialogx_low_api_material_button_press_night.xml b/DialogX/src/main/res/drawable/rect_dialogx_low_api_material_button_press_night.xml new file mode 100644 index 0000000..d41f3a9 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_low_api_material_button_press_night.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_bkg_light.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_bkg_light.xml new file mode 100644 index 0000000..505db92 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_bkg_light.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_bkg_night.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_bkg_night.xml new file mode 100644 index 0000000..a90496c --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_bkg_night.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_bottom_bkg_light.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_bottom_bkg_light.xml new file mode 100644 index 0000000..e500950 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_bottom_bkg_light.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_bottom_bkg_night.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_bottom_bkg_night.xml new file mode 100644 index 0000000..edc0eaf --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_bottom_bkg_night.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_button_light_forword.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_button_light_forword.xml new file mode 100644 index 0000000..261c420 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_button_light_forword.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_button_night_forword.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_button_night_forword.xml new file mode 100644 index 0000000..518437f --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_button_night_forword.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_dialogtap.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_dialogtap.xml new file mode 100644 index 0000000..07c0254 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_dialogtap.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_dialogtap_night.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_dialogtap_night.xml new file mode 100644 index 0000000..d41f3a9 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_dialogtap_night.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_menu_split_divider.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_menu_split_divider.xml new file mode 100644 index 0000000..aa97988 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_menu_split_divider.xml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_menu_split_divider_night.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_menu_split_divider_night.xml new file mode 100644 index 0000000..29b7e21 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_menu_split_divider_night.xml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_popnotification_bkg.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_popnotification_bkg.xml new file mode 100644 index 0000000..db95930 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_popnotification_bkg.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_popnotification_bkg_night.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_popnotification_bkg_night.xml new file mode 100644 index 0000000..e367970 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_popnotification_bkg_night.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_poptip_bkg.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_poptip_bkg.xml new file mode 100644 index 0000000..db95930 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_poptip_bkg.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_poptip_bkg_night.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_poptip_bkg_night.xml new file mode 100644 index 0000000..e367970 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_poptip_bkg_night.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_wait_bkg.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_wait_bkg.xml new file mode 100644 index 0000000..96091ee --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_wait_bkg.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/DialogX/src/main/res/layout/item_dialogx_material_bottom_menu_normal_text.xml b/DialogX/src/main/res/layout/item_dialogx_material_bottom_menu_normal_text.xml new file mode 100644 index 0000000..b6aaab6 --- /dev/null +++ b/DialogX/src/main/res/layout/item_dialogx_material_bottom_menu_normal_text.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/item_dialogx_material_context_menu_normal_text.xml b/DialogX/src/main/res/layout/item_dialogx_material_context_menu_normal_text.xml new file mode 100644 index 0000000..7fad54e --- /dev/null +++ b/DialogX/src/main/res/layout/item_dialogx_material_context_menu_normal_text.xml @@ -0,0 +1,34 @@ + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_bottom_material.xml b/DialogX/src/main/res/layout/layout_dialogx_bottom_material.xml new file mode 100644 index 0000000..99fd5ef --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_bottom_material.xml @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_bottom_material_dark.xml b/DialogX/src/main/res/layout/layout_dialogx_bottom_material_dark.xml new file mode 100644 index 0000000..3b74ade --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_bottom_material_dark.xml @@ -0,0 +1,172 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_custom.xml b/DialogX/src/main/res/layout/layout_dialogx_custom.xml new file mode 100644 index 0000000..ac85bb7 --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_custom.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_empty.xml b/DialogX/src/main/res/layout/layout_dialogx_empty.xml new file mode 100644 index 0000000..24f3d6b --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_empty.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_fullscreen.xml b/DialogX/src/main/res/layout/layout_dialogx_fullscreen.xml new file mode 100644 index 0000000..6366b9c --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_fullscreen.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/DialogX/src/main/res/layout/layout_dialogx_fullscreen_dark.xml b/DialogX/src/main/res/layout/layout_dialogx_fullscreen_dark.xml new file mode 100644 index 0000000..ea065c0 --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_fullscreen_dark.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/DialogX/src/main/res/layout/layout_dialogx_material.xml b/DialogX/src/main/res/layout/layout_dialogx_material.xml new file mode 100644 index 0000000..3df864f --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_material.xml @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_material_dark.xml b/DialogX/src/main/res/layout/layout_dialogx_material_dark.xml new file mode 100644 index 0000000..ce3c4b3 --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_material_dark.xml @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_popmenu_material.xml b/DialogX/src/main/res/layout/layout_dialogx_popmenu_material.xml new file mode 100644 index 0000000..241e249 --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_popmenu_material.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_popmenu_material_dark.xml b/DialogX/src/main/res/layout/layout_dialogx_popmenu_material_dark.xml new file mode 100644 index 0000000..297cc75 --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_popmenu_material_dark.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_popnotification_material.xml b/DialogX/src/main/res/layout/layout_dialogx_popnotification_material.xml new file mode 100644 index 0000000..7c1f0e1 --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_popnotification_material.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_popnotification_material_dark.xml b/DialogX/src/main/res/layout/layout_dialogx_popnotification_material_dark.xml new file mode 100644 index 0000000..e76fce1 --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_popnotification_material_dark.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_poptip_material.xml b/DialogX/src/main/res/layout/layout_dialogx_poptip_material.xml new file mode 100644 index 0000000..365e34d --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_poptip_material.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_poptip_material_dark.xml b/DialogX/src/main/res/layout/layout_dialogx_poptip_material_dark.xml new file mode 100644 index 0000000..0fc708e --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_poptip_material_dark.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_wait.xml b/DialogX/src/main/res/layout/layout_dialogx_wait.xml new file mode 100644 index 0000000..b9ac007 --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_wait.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/mipmap-xxhdpi/ico_dialogx_error.png b/DialogX/src/main/res/mipmap-xxhdpi/ico_dialogx_error.png new file mode 100644 index 0000000..73f17eb Binary files /dev/null and b/DialogX/src/main/res/mipmap-xxhdpi/ico_dialogx_error.png differ diff --git a/DialogX/src/main/res/mipmap-xxhdpi/ico_dialogx_success.png b/DialogX/src/main/res/mipmap-xxhdpi/ico_dialogx_success.png new file mode 100644 index 0000000..76e9ca7 Binary files /dev/null and b/DialogX/src/main/res/mipmap-xxhdpi/ico_dialogx_success.png differ diff --git a/DialogX/src/main/res/mipmap-xxhdpi/ico_dialogx_warning.png b/DialogX/src/main/res/mipmap-xxhdpi/ico_dialogx_warning.png new file mode 100644 index 0000000..b50b115 Binary files /dev/null and b/DialogX/src/main/res/mipmap-xxhdpi/ico_dialogx_warning.png differ diff --git a/DialogX/src/main/res/mipmap-xxhdpi/img_dialogx_bottom_menu_material_item_multi_selection.png b/DialogX/src/main/res/mipmap-xxhdpi/img_dialogx_bottom_menu_material_item_multi_selection.png new file mode 100644 index 0000000..feebea1 Binary files /dev/null and b/DialogX/src/main/res/mipmap-xxhdpi/img_dialogx_bottom_menu_material_item_multi_selection.png differ diff --git a/DialogX/src/main/res/mipmap-xxhdpi/img_dialogx_bottom_menu_material_item_non_multi_select.png b/DialogX/src/main/res/mipmap-xxhdpi/img_dialogx_bottom_menu_material_item_non_multi_select.png new file mode 100644 index 0000000..2b23ee6 Binary files /dev/null and b/DialogX/src/main/res/mipmap-xxhdpi/img_dialogx_bottom_menu_material_item_non_multi_select.png differ diff --git a/DialogX/src/main/res/mipmap-xxhdpi/img_dialogx_bottom_menu_material_item_non_select.png b/DialogX/src/main/res/mipmap-xxhdpi/img_dialogx_bottom_menu_material_item_non_select.png new file mode 100644 index 0000000..005c878 Binary files /dev/null and b/DialogX/src/main/res/mipmap-xxhdpi/img_dialogx_bottom_menu_material_item_non_select.png differ diff --git a/DialogX/src/main/res/mipmap-xxhdpi/img_dialogx_bottom_menu_material_item_selection.png b/DialogX/src/main/res/mipmap-xxhdpi/img_dialogx_bottom_menu_material_item_selection.png new file mode 100644 index 0000000..fd1d9d6 Binary files /dev/null and b/DialogX/src/main/res/mipmap-xxhdpi/img_dialogx_bottom_menu_material_item_selection.png differ diff --git a/DialogX/src/main/res/mipmap-xxhdpi/img_drawable_down.png b/DialogX/src/main/res/mipmap-xxhdpi/img_drawable_down.png new file mode 100644 index 0000000..42057f6 Binary files /dev/null and b/DialogX/src/main/res/mipmap-xxhdpi/img_drawable_down.png differ diff --git a/DialogX/src/main/res/values-v21/styles.xml b/DialogX/src/main/res/values-v21/styles.xml new file mode 100644 index 0000000..30ee2b0 --- /dev/null +++ b/DialogX/src/main/res/values-v21/styles.xml @@ -0,0 +1,16 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/values/attrs.xml b/DialogX/src/main/res/values/attrs.xml new file mode 100644 index 0000000..dfc2a32 --- /dev/null +++ b/DialogX/src/main/res/values/attrs.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/values/colors.xml b/DialogX/src/main/res/values/colors.xml new file mode 100644 index 0000000..bc44d77 --- /dev/null +++ b/DialogX/src/main/res/values/colors.xml @@ -0,0 +1,42 @@ + + + #2196F3 + + #353535 + #000 + #E6000000 + #CC000000 + #BF000000 + #B3000000 + #99000000 + #80000000 + #66000000 + #4D000000 + #40000000 + #33000000 + #1A000000 + #0D000000 + #00000000 + + #fff + #E6ffffff + #CCffffff + #BFffffff + #B3ffffff + #99ffffff + #80ffffff + #66ffffff + #4Dffffff + #40ffffff + #33ffffff + #1Affffff + #0DFFFFFF + + #2196F3 + + #343434 + #F5F6F7 + #232323 + + #86B3D6 + \ No newline at end of file diff --git a/DialogX/src/main/res/values/ids.xml b/DialogX/src/main/res/values/ids.xml new file mode 100644 index 0000000..50fa4bf --- /dev/null +++ b/DialogX/src/main/res/values/ids.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/values/styles.xml b/DialogX/src/main/res/values/styles.xml new file mode 100644 index 0000000..8a15e21 --- /dev/null +++ b/DialogX/src/main/res/values/styles.xml @@ -0,0 +1,37 @@ + + + + + + + + \ No newline at end of file diff --git a/DialogXInterface/.gitignore b/DialogXInterface/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/DialogXInterface/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/DialogXInterface/build.gradle b/DialogXInterface/build.gradle new file mode 100644 index 0000000..2ef58bd --- /dev/null +++ b/DialogXInterface/build.gradle @@ -0,0 +1,8 @@ +apply plugin: 'java-library' + +dependencies { + compileOnly files('libs\\android.jar') +} + +sourceCompatibility = "1.8" +targetCompatibility = "1.8" \ No newline at end of file diff --git a/app/release/GovAffairsCloud_app_v1.0_20250523090051.apk b/DialogXInterface/libs/android.jar similarity index 60% rename from app/release/GovAffairsCloud_app_v1.0_20250523090051.apk rename to DialogXInterface/libs/android.jar index 9fd7d92..5e69644 100644 Binary files a/app/release/GovAffairsCloud_app_v1.0_20250523090051.apk and b/DialogXInterface/libs/android.jar differ diff --git a/DialogXInterface/src/main/java/com/kongzue/dialogx/interfaces/DialogXStyle.java b/DialogXInterface/src/main/java/com/kongzue/dialogx/interfaces/DialogXStyle.java new file mode 100644 index 0000000..3fa4243 --- /dev/null +++ b/DialogXInterface/src/main/java/com/kongzue/dialogx/interfaces/DialogXStyle.java @@ -0,0 +1,627 @@ +package com.kongzue.dialogx.interfaces; + +import android.content.Context; + +public abstract class DialogXStyle { + + /** + * DialogXStyle 版本 + * 相关文档请参阅:https://github.com/kongzue/DialogX/wiki/%E8%87%AA%E5%AE%9A%E4%B9%89-DialogX-%E4%B8%BB%E9%A2%98 + */ + public static final int styleVer = 6; + + /** + * 按钮类型常量 + */ + public static final int BUTTON_OK = 1; + public static final int BUTTON_CANCEL = 2; + public static final int BUTTON_OTHER = 3; + public static final int SPACE = 4; + public static final int SPLIT = 5; + + /** + * 重写基础对话框布局资源 + *

+ * 重写 layout 方法用于设置默认的消息对话框的 layout 布局文件,其中 light 参数用于判断是否为亮色模式, + * 你可以在这里查看亮色默认主题布局文件的实现 和 暗色默认主题布局文件的实现, + * 请参照 Demo 布局的格式进行布局设计,不建议修改或去掉布局中的含有 id 的组件。 + * + * @param light 亮暗色 + * @return 布局资源 id + */ + public int layout(boolean light) { + return 0; + } + + /** + * 修改默认对话框启动动画效果 + *

+ * 你可以自定义消息对话框的启动动画效果,当你return 0时则采用默认实现, + * 要自定义动画文件,可以参考:默认对话框启动动画文件 https://github.com/kongzue/DialogX/blob/master/DialogX/src/main/res/anim/anim_dialogx_default_enter.xml + * + * @return 布局资源 id + */ + public int enterAnimResId() { + return 0; + } + + /** + * 修改默认对话框关闭动画效果 + *

+ * 你可以自定义消息对话框的关闭动画效果,当你return 0时则采用默认实现, + * 要自定义动画文件,可以参考:默认对话框启动动画文件 https://github.com/kongzue/DialogX/blob/master/DialogX/src/main/res/anim/anim_dialogx_default_exit.xml + * + * @return 布局资源 id + */ + public int exitAnimResId() { + return 0; + } + + /** + * 修改默认按钮排序(纵向) + *

+ * 在特殊情况下,你可以调整对话框的按钮布局顺序,例如,在横向时,我们一般采用“其他、间隔、取消、确定”的逻辑,要修改按钮排序顺序,你可以重写以下接口。 + *

+ * eg. new int[]{BUTTON_OTHER, SPLIT, BUTTON_CANCEL, BUTTON_OK} + * + * @return 排序数组 + */ + public int[] verticalButtonOrder() { + return new int[]{BUTTON_OTHER, SPLIT, BUTTON_CANCEL, BUTTON_OK}; + } + + /** + * 修改默认按钮排序(横向) + *

+ * 在特殊情况下,你可以调整对话框的按钮布局顺序,例如,在纵向时,我们可以调整为“确定、其他、取消”的顺序,要修改按钮排序顺序,你可以重写以下接口。 + *

+ * eg. new int[]{BUTTON_OTHER, BUTTON_CANCEL, BUTTON_OK} + * + * @return 排序数组 + */ + public int[] horizontalButtonOrder() { + return new int[]{BUTTON_OTHER, BUTTON_CANCEL, BUTTON_OK}; + } + + /** + * 按钮分割线宽度 + *

+ * 需要调整分隔线的宽度,可以重写以下接口进行设置,这些设置也可以使用 return 0不进行设置。 + * + * @return 像素值 + */ + public int splitWidthPx() { + return 1; + } + + /** + * 按钮分割线颜色 + *

+ * 需要调整分隔线的颜色,可以重写以下接口进行设置,这些设置也可以使用 return 0不进行设置。 + * + * @return 颜色资源 id + */ + public int splitColorRes(boolean light) { + return 0x1A000000; + } + + /** + * 模糊背景 + *

+ * 默认对话框支持模糊化背景,如果需要模糊的背景效果,你可以重写以下接口: + * + * @return BlurBackgroundSetting + */ + public BlurBackgroundSetting messageDialogBlurSettings() { + return null; + } + + /** + * 自定义按钮样式 + *

+ * 一些主题要求我们在显示按钮时提供不同的样式,你可以通过两个接口来进行自定义:当对话框按钮处于横向/纵向时, + * 或者当按钮显示数量为1个、2个或3个时,呈现不同的样式。 + *

+ * 例如 iOS 主题,横向且只显示一个按钮(OkButton)时,需要左下角和右下角都为圆角的按钮样式, + * 当显示多个按钮时则为仅右下角为圆角的按钮样式,如果需要基于不同的情况进行相应按钮样式调整,你可以重写以下接口: + * + * @return HorizontalButtonRes + */ + public HorizontalButtonRes overrideHorizontalButtonRes() { + return null; + } + + public VerticalButtonRes overrideVerticalButtonRes() { + return null; + } + +// /** +// * 消息对话框正文文本 UI 定义 +// * +// * @return null 代表使用默认定义 +// */ +// public UIAttributeDefinition messageDialogMessageTextViewUIAD() { +// return null; +// } +// +// /** +// * InputDialog 输入框 UI 定义 +// * +// * @return null 代表使用默认定义 +// */ +// public UIAttributeDefinition inputEditTextUIAD() { +// return null; +// } + + /** + * 修改等待/提示框的样式 + *

+ * 如果需要修改等待/提示框的样式效果,你可以重写这个接口: + * + * @return WaitTipRes + */ + public WaitTipRes overrideWaitTipRes() { + return null; + } + + /** + * 自定义底部对话框/菜单样式 + *

+ * 底部对话框是 DialogX 中第二个具有丰富功能的组件,您可以实现overrideBottomDialogRes()方法以自定义底部对话框的样式细节。 + * 同样的,当return null 时使用默认样式(Material 主题)。 + * + * @return BottomDialogRes + */ + public BottomDialogRes overrideBottomDialogRes() { + return null; + } + + /** + * 自定义 PopTip 样式 + * + * @return PopTipSettings + */ + public PopTipSettings popTipSettings() { + return null; + } + + /** + * 自定义 PopNotificationSettings 样式 + * + * @return PopNotificationSettings + */ + public PopNotificationSettings popNotificationSettings() { + return null; + } + + /** + * 自定义 PopMenu 样式 + * + * @return PopMenuSettings + */ + public PopMenuSettings popMenuSettings() { + return null; + } + + /** + * 模糊背景设置 + *

+ * 如果不需要可以return null进行默认处理。 + *

+ * 此接口需要重写一个BlurBackgroundSetting,其中包含三个子接口, + * 分别是blurBackground()用于判断是否开启模糊, + * blurForwardColorRes(boolean light)用于处理前景色(建议设置一定的透明度保证可以看到背后的模糊效果), + * blurBackgroundRoundRadiusPx()用于给定模糊效果的圆角半径,单位为像素(Px)。 + */ + public abstract class BlurBackgroundSetting { + + public boolean blurBackground() { + return false; + } + + public int blurForwardColorRes(boolean light) { + return 0; + } + + public int blurBackgroundRoundRadiusPx() { + return 0; + } + } + + /** + * 自定义按钮样式设置 + *

+ * 其中 visibleButtonCount 参数为当前显示按钮的数量,light参数为当前对话框的亮/暗色模式。 + *

+ * drawable 只接受 xml 配置,可以定义不同状态时的按钮样式效果,如有需要请参考 iOS drawable 样式 + *

+ * overrideHorizontalButtonRes和overrideVerticalButtonRes返回值return null时默认不进行样式修改设置。 + */ + public abstract class HorizontalButtonRes { + + public int overrideHorizontalOkButtonBackgroundRes(int visibleButtonCount, boolean light) { + return 0; + } + + public int overrideHorizontalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) { + return 0; + } + + public int overrideHorizontalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) { + return 0; + } + } + + public abstract class VerticalButtonRes { + + public int overrideVerticalOkButtonBackgroundRes(int visibleButtonCount, boolean light) { + return 0; + } + + public int overrideVerticalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) { + return 0; + } + + public int overrideVerticalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) { + return 0; + } + } + + /** + * 修改等待/提示框的样式设置 + *

+ * 此接口return null时采用默认样式。 + */ + public abstract class WaitTipRes { + + //重写布局资源 + public int overrideWaitLayout(boolean light) { + return 0; + } + + //设置圆角像素 + public int overrideRadiusPx() { + return 0; + } + + //判断是否需要模糊背景效果 + public boolean blurBackground() { + return false; + } + + //重新设置前景色 + public int overrideBackgroundColorRes(boolean light) { + return 0; + } + + //设置在亮/暗色时的文字颜色,注意,此颜色也将修改进度动画的颜色。 + public int overrideTextColorRes(boolean light) { + return 0; + } + + /** + * 自定义等待提示动画组件接口 ProgressViewInterface + *

+ * 需要在自定义的 View 中实现这些方法,以实现在等待提示框处于不同状态时调整显示的样式。 + * 需要注意的是,默认等待提示框在加载中到完成/警告/错误等状态时,会有衔接过渡的过程,这个过程导致目标状态可能会延后几十到几百毫秒后才真正切换,因此有一个whenShowTick(Runnable runnable)接口需要实现,在衔接过程结束后执行runnable.run()即可,若无衔接过程,直接在重写后的方法中执行runnable.run()即可。 + * 在亮暗色模式切换后,等待提示框组件应该遵循颜色的变化调整颜色,这个颜色会从setColor(int color)接口中给出,注意此处的参数color是色值,并非资源值,建议直接赋值给画笔 Paint 进行绘制操作。 + * 如需参照 Demo,您可以查看 iOS 样式的 ProgressView 实现:ProgressView.java + *

+ * //停止加载动画 + * noLoading(); + *

+ * //切换至完成状态 + * success(); + *

+ * //切换至警告状态 + * warning(); + *

+ * //切换至错误状态 + * error(); + *

+ * //切换至进度(取值0f-1f) + * progress(floatprogress); + *

+ * //切换至加载状态 + * loading(); + *

+ * //不同状态切换时,衔接动画完成后执行 + * ProgressViewInterface whenShowTick(Runnablerunnable); + *

+ * //设置颜色 + * ProgressViewInterface setColor(intcolor); + * + * @param context 上下文 + * @param light 是否为亮色模式 + * @return ProgressViewInterface + */ + public ProgressViewInterface overrideWaitView(Context context, boolean light) { + return null; + } + } + + /** + * 自定义底部对话框/菜单样式设置 + *

+ *

+ * overrideSelectionMenuBackgroundColor(boolean light)接口用于 + *

+ * selectionImageTint(boolean light)接口 + *

+ * overrideSelectionImage(boolean light, boolean isSelected)用于 + */ + public abstract class BottomDialogRes { + + //定义是否支持滑动关闭操作。 + public boolean touchSlide() { + return false; + } + + //用于设置底部对话框的布局,如需修改布局样式,请参照 底部对话框默认亮色布局 和 底部对话框默认暗色布局,请参照 Demo 布局的格式进行布局设计,不建议修改或去掉布局中的含有 id 的组件,当return 0时使用默认实现。 + public int overrideDialogLayout(boolean light) { + return 0; + } + + //用于修改默认分隔线的粗细,单位像素。 + public int overrideMenuDividerDrawableRes(boolean light) { + return 0; + } + + //修改默认分隔线的粗细,单位像素。 + public int overrideMenuDividerHeight(boolean light) { + return 0; + } + + //修改默认菜单文字的颜色,值采用为 color 的资源 ID。 + public int overrideMenuTextColor(boolean light) { + return 0; + } + + //设置默认情况下,当底部对话框内容大于屏幕可显示高度时, + //默认启动后显示的高度比例,值为浮点型,例如设置为 0.6f 时,则当内容大于可显示高度时,启动后对话框只从屏幕底部弹出 0.6×屏幕高度的大小,需要再次向上拖拽才能展开全部对话框, + //此功能需要和 touchSlide() 配合使用。 + public float overrideBottomDialogMaxHeight() { + return 0; + } + + /** + * 定义菜单条目的布局样式。 + * 例如当使用 iOS 样式时,第一条菜单默认采用左上角和右上角都为圆角的样式,当显示菜单标题、正文或自定义布局时, + * 则第一条菜单使用无圆角样式。当index == count - 1时为最后一个菜单,使用 iOS 样式时, + * 最后一个菜单应该采用左下角和右下角都为圆角的样式。菜单的布局请参照 底部菜单亮色样式参考布局 和 底部菜单暗色样式参考布局 。 + *

+ * 此接口return 0时使用默认实现。 + * + * @param light 判断亮/暗色模式 + * @param index 当前菜单项的索引值 + * @param count 菜单数量 + * @param isContentVisibility 确认当菜单显示时,是否还有其他内容显示(例如对话框标题、正文或自定义布局) + * @return 条目布局资源 + */ + public int overrideMenuItemLayout(boolean light, int index, int count, boolean isContentVisibility) { + return 0; + } + + //定义已选中的菜单默认背景颜色, + // 例如在使用 MIUI 主题样式且开启了单选模式时,默认打开菜单后会选中上次已选择的条目,此接口用预设定已选中菜单的背景颜色。 + public int overrideSelectionMenuBackgroundColor(boolean light) { + return 0; + } + + //用于确定使用此主题时,默认会不会重定义图标的颜色, + // 若开启,那么所有菜单图标会根据主题的亮/暗色的文字颜色重新覆盖颜色,若关闭,则使用图标原本的颜色。 + public boolean selectionImageTint(boolean light) { + return false; + } + + //设置默认单选菜单已选择/未选择时的图标资源,可使用 mipmap 图像或者 drawable 资源。 + public int overrideSelectionImage(boolean light, boolean isSelected) { + return 0; + } + + //设置默认多选菜单已选择/未选择时的图标资源,可使用 mipmap 图像或者 drawable 资源。 + public int overrideMultiSelectionImage(boolean light, boolean isSelected) { + return 0; + } + } + + /** + * 自定义 PopTip 样式设置 + *

+ * 当return null 时使用默认样式。 + */ + public abstract static class PopTipSettings { + + //PopTip 的默认布局样式,请参考具体布局实现:PopTip 默认亮色布局 和 PopTip 默认暗色布局 + public int layout(boolean light) { + return 0; + } + + /** + * align()接口用于判断 PopTip 的弹出规则,支持的值如下: + */ + public ALIGN align() { + return ALIGN.CENTER; + } + + public enum ALIGN { + CENTER, //屏幕中央弹出 + TOP, //屏幕顶端弹出(非安全区) + BOTTOM, //屏幕底部弹出(非安全区) + TOP_INSIDE, //屏幕顶端安全区内弹出 + BOTTOM_INSIDE //屏幕底部安全区内弹出 + } + + //设置启动动画效果。 + public int enterAnimResId(boolean light) { + return 0; + } + + //设置关闭动画效果。 + public int exitAnimResId(boolean light) { + return 0; + } + + //使图标颜色和文字保持一致 + public boolean tintIcon() { + return true; + } + + //默认完成图标 + public int defaultIconSuccess() { + return 0; + } + + //默认警告图标 + public int defaultIconWarning() { + return 0; + } + + //默认错误图标 + public int defaultIconError() { + return 0; + } + } + + public abstract class PopMenuSettings { + + //PopMenu 的默认布局样式,请参考具体布局实现:PopMenu 默认亮色布局 和 PopMenu 默认暗色布局 + public int layout(boolean light) { + return 0; + } + + public BlurBackgroundSetting blurBackgroundSettings() { + return null; + } + + public int backgroundMaskColorRes() { + return 0; + } + + //用于修改默认分隔线的粗细,单位像素。 + public int overrideMenuDividerDrawableRes(boolean light) { + return 0; + } + + //修改默认分隔线的粗细,单位像素。 + public int overrideMenuDividerHeight(boolean light) { + return 0; + } + + //修改默认菜单文字的颜色,值采用为 color 的资源 ID。 + public int overrideMenuTextColor(boolean light) { + return 0; + } + + //自定义菜单的布局资源 ID。 + public int overrideMenuItemLayoutRes(boolean light) { + return 0; + } + + /** + * 定义菜单条目的布局背景资源。 + * 例如当使用 iOS 样式时,第一条菜单默认采用左上角和右上角都为圆角的样式,当显示菜单标题、正文或自定义布局时, + * 则第一条菜单背景使用无圆角样式。当index == count - 1时为最后一个菜单,使用 iOS 样式时, + * 最后一个菜单应该采用左下角和右下角都为圆角的背景样式。菜单的布局请参照 底部菜单亮色样式参考布局 和 底部菜单暗色样式参考布局 。 + *

+ * 此接口return 0时使用默认实现。 + * + * @param light 判断亮/暗色模式 + * @param index 当前菜单项的索引值 + * @param count 菜单数量 + * @param isContentVisibility 确认当菜单显示时,是否还有其他内容显示(例如对话框标题、正文或自定义布局) + * @return 条目布局背景资源 + */ + public int overrideMenuItemBackgroundRes(boolean light, int index, int count, boolean isContentVisibility) { + return 0; + } + + //定义已选中的菜单默认背景颜色, + // 例如在使用 MIUI 主题样式且开启了单选模式时,默认打开菜单后会选中上次已选择的条目,此接口用预设定已选中菜单的背景颜色。 + public int overrideSelectionMenuBackgroundColor(boolean light) { + return 0; + } + + //用于确定使用此主题时,默认会不会重定义图标的颜色, + // 若开启,那么所有菜单图标会根据主题的亮/暗色的文字颜色重新覆盖颜色,若关闭,则使用图标原本的颜色。 + public boolean selectionImageTint(boolean light) { + return false; + } + + //PopMenu 的顶部和底部的额外 padding + public int paddingVertical() { + return 0; + } + } + + /** + * 自定义 PopNotificationSettings 样式设置 + *

+ * 当return null 时使用默认样式。 + */ + public abstract static class PopNotificationSettings { + + //PopNotificationSettings 的默认布局样式,请参考具体布局实现:PopNotificationSettings 默认亮色布局 和 PopNotificationSettings 默认暗色布局 + public int layout(boolean light) { + return 0; + } + + /** + * align()接口用于判断 PopNotificationSettings 的弹出规则,支持的值如下: + */ + public ALIGN align() { + return ALIGN.CENTER; + } + + public enum ALIGN { + CENTER, //屏幕中央弹出 + TOP, //屏幕顶端弹出(非安全区) + BOTTOM, //屏幕底部弹出(非安全区) + TOP_INSIDE, //屏幕顶端安全区内弹出 + BOTTOM_INSIDE //屏幕底部安全区内弹出 + } + + //设置启动动画效果。 + public int enterAnimResId(boolean light) { + return 0; + } + + //设置关闭动画效果。 + public int exitAnimResId(boolean light) { + return 0; + } + + //使图标颜色和文字保持一致 + public boolean tintIcon() { + return true; + } + + //默认完成图标 + public int defaultIconSuccess() { + return 0; + } + + //默认警告图标 + public int defaultIconWarning() { + return 0; + } + + //默认错误图标 + public int defaultIconError() { + return 0; + } + + public BlurBackgroundSetting blurBackgroundSettings() { + return null; + } + } + + /** + * 是否需要在设置对话框背景色时对按钮背景也进行染色 + * + * @return false 不进行染色 + */ + public boolean tintButtonBackground() { + return false; + } +} \ No newline at end of file diff --git a/DialogXInterface/src/main/java/com/kongzue/dialogx/interfaces/ProgressViewInterface.java b/DialogXInterface/src/main/java/com/kongzue/dialogx/interfaces/ProgressViewInterface.java new file mode 100644 index 0000000..b4b14ad --- /dev/null +++ b/DialogXInterface/src/main/java/com/kongzue/dialogx/interfaces/ProgressViewInterface.java @@ -0,0 +1,35 @@ +package com.kongzue.dialogx.interfaces; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/11/3 20:39 + */ +public interface ProgressViewInterface { + + //停止加载动画 + void noLoading(); + + //切换至完成状态 + void success(); + + //切换至警告状态 + void warning(); + + //切换至错误状态 + void error(); + + //切换至进度(取值 0f-1f) + void progress(float progress); + + //切换至加载状态 + void loading(); + + //不同状态切换时,衔接动画完成后执行 + ProgressViewInterface whenShowTick(Runnable runnable); + + //设置颜色 + ProgressViewInterface setColor(int color); +} diff --git a/DialogXMaterialYou/.gitignore b/DialogXMaterialYou/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/DialogXMaterialYou/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/DialogXMaterialYou/build.gradle b/DialogXMaterialYou/build.gradle new file mode 100644 index 0000000..5d57144 --- /dev/null +++ b/DialogXMaterialYou/build.gradle @@ -0,0 +1,33 @@ +plugins { + id 'com.android.library' +} + +android { + compileSdkVersion 33 + + defaultConfig { + minSdkVersion 21 + targetSdkVersion 33 + versionCode 1 + versionName "1.0" + + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + implementation project(path: ':DialogX') + implementation 'androidx.appcompat:appcompat:1.4.1' + compileOnly project(path: ':DialogXInterface') +} \ No newline at end of file diff --git a/.gradle/7.3.3/gc.properties b/DialogXMaterialYou/consumer-rules.pro similarity index 100% rename from .gradle/7.3.3/gc.properties rename to DialogXMaterialYou/consumer-rules.pro diff --git a/DialogXMaterialYou/proguard-rules.pro b/DialogXMaterialYou/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/DialogXMaterialYou/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/AndroidManifest.xml b/DialogXMaterialYou/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8ae6a9f --- /dev/null +++ b/DialogXMaterialYou/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/java/com/kongzue/dialogxmaterialyou/style/MaterialYouStyle.java b/DialogXMaterialYou/src/main/java/com/kongzue/dialogxmaterialyou/style/MaterialYouStyle.java new file mode 100644 index 0000000..5f3b94e --- /dev/null +++ b/DialogXMaterialYou/src/main/java/com/kongzue/dialogxmaterialyou/style/MaterialYouStyle.java @@ -0,0 +1,336 @@ +package com.kongzue.dialogxmaterialyou.style; + +import android.content.Context; + +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.ProgressViewInterface; +import com.kongzue.dialogx.util.views.ProgressView; +import com.kongzue.dialogxmaterialyou.R; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/26 13:09 + */ +public class MaterialYouStyle extends DialogXStyle { + + public static MaterialYouStyle style() { + return new MaterialYouStyle(); + } + + @Override + public int layout(boolean light) { + return light ? R.layout.layout_dialogx_material_you : R.layout.layout_dialogx_material_you_dark; + } + + @Override + public int enterAnimResId() { + return com.kongzue.dialogx.R.anim.anim_dialogx_default_enter; + } + + @Override + public int exitAnimResId() { + return com.kongzue.dialogx.R.anim.anim_dialogx_default_exit; + } + + @Override + public int[] verticalButtonOrder() { + return new int[]{BUTTON_OK, BUTTON_OTHER, BUTTON_CANCEL}; + } + + @Override + public int[] horizontalButtonOrder() { + return new int[]{BUTTON_OTHER, SPACE, BUTTON_CANCEL, BUTTON_OK}; + } + + @Override + public int splitWidthPx() { + return 1; + } + + @Override + public int splitColorRes(boolean light) { + return 0; + } + + @Override + public BlurBackgroundSetting messageDialogBlurSettings() { + return null; + } + + @Override + public HorizontalButtonRes overrideHorizontalButtonRes() { + return new DefaultHorizontalButtonRes(); + } + + public class DefaultHorizontalButtonRes extends HorizontalButtonRes{ + @Override + public int overrideHorizontalOkButtonBackgroundRes(int visibleButtonCount, boolean light) { + return light ? R.drawable.button_dialogx_material_you_light : R.drawable.button_dialogx_material_you_night; + } + + @Override + public int overrideHorizontalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) { + return light ? R.drawable.button_dialogx_material_you_light : R.drawable.button_dialogx_material_you_night; + } + + @Override + public int overrideHorizontalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) { + return light ? R.drawable.button_dialogx_material_you_light : R.drawable.button_dialogx_material_you_night; + } + } + + @Override + public VerticalButtonRes overrideVerticalButtonRes() { + return new DefaultVerticalButtonRes(); + } + + public class DefaultVerticalButtonRes extends VerticalButtonRes{ + @Override + public int overrideVerticalOkButtonBackgroundRes(int visibleButtonCount, boolean light) { + return light ? R.drawable.button_dialogx_material_you_light : R.drawable.button_dialogx_material_you_night; + } + + @Override + public int overrideVerticalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) { + return light ? R.drawable.button_dialogx_material_you_light : R.drawable.button_dialogx_material_you_night; + } + + @Override + public int overrideVerticalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) { + return light ? R.drawable.button_dialogx_material_you_light : R.drawable.button_dialogx_material_you_night; + } + } + + @Override + public WaitTipRes overrideWaitTipRes() { + return new DefaultWaitTipRes(); + } + + public class DefaultWaitTipRes extends WaitTipRes{ + @Override + public int overrideWaitLayout(boolean light) { + return com.kongzue.dialogx.R.layout.layout_dialogx_wait; + } + + @Override + public int overrideRadiusPx() { + return -1; + } + + @Override + public boolean blurBackground() { + return false; + } + + @Override + public int overrideBackgroundColorRes(boolean light) { + return 0; + } + + @Override + public int overrideTextColorRes(boolean light) { + return light ? com.kongzue.dialogx.R.color.white : com.kongzue.dialogx.R.color.black; + } + + @Override + public ProgressViewInterface overrideWaitView(Context context, boolean light) { + return new ProgressView(context); + } + } + + @Override + public BottomDialogRes overrideBottomDialogRes() { + return new DefaultBottomDialogRes(); + } + + public class DefaultBottomDialogRes extends BottomDialogRes{ + @Override + public boolean touchSlide() { + return true; + } + + @Override + public int overrideDialogLayout(boolean light) { + return light ? R.layout.layout_dialogx_bottom_material_you: R.layout.layout_dialogx_bottom_material_you_dark; + } + + @Override + public int overrideMenuDividerDrawableRes(boolean light) { + return light ? com.kongzue.dialogx.R.drawable.rect_dialogx_material_menu_split_divider : com.kongzue.dialogx.R.drawable.rect_dialogx_material_menu_split_divider_night; + } + + @Override + public int overrideMenuDividerHeight(boolean light) { + return 1; + } + + @Override + public int overrideMenuTextColor(boolean light) { + return light ? com.kongzue.dialogx.R.color.black90 : com.kongzue.dialogx.R.color.white90; + } + + @Override + public float overrideBottomDialogMaxHeight() { + return 0.6f; + } + + @Override + public int overrideMenuItemLayout(boolean light, int index, int count, boolean isContentVisibility) { + return 0; + } + + @Override + public int overrideSelectionMenuBackgroundColor(boolean light) { + return 0; + } + + @Override + public boolean selectionImageTint(boolean light) { + return false; + } + + @Override + public int overrideSelectionImage(boolean light, boolean isSelected) { + return isSelected ? com.kongzue.dialogx.R.mipmap.img_dialogx_bottom_menu_material_item_selection : com.kongzue.dialogx.R.mipmap.img_dialogx_bottom_menu_material_item_non_select; + } + + @Override + public int overrideMultiSelectionImage(boolean light, boolean isSelected) { + return isSelected ? com.kongzue.dialogx.R.mipmap.img_dialogx_bottom_menu_material_item_multi_selection : com.kongzue.dialogx.R.mipmap.img_dialogx_bottom_menu_material_item_non_multi_select; + } + } + + @Override + public PopTipSettings popTipSettings() { + return new DefaultPopTipSettings(); + } + + public class DefaultPopTipSettings extends PopTipSettings { + @Override + public int layout(boolean light) { + return light ? R.layout.layout_dialogx_poptip_material_you : R.layout.layout_dialogx_poptip_material_you_dark; + } + + @Override + public ALIGN align() { + return ALIGN.BOTTOM; + } + + @Override + public int enterAnimResId(boolean light) { + return com.kongzue.dialogx.R.anim.anim_dialogx_default_enter; + } + + @Override + public int exitAnimResId(boolean light) { + return com.kongzue.dialogx.R.anim.anim_dialogx_default_exit; + } + + @Override + public boolean tintIcon() { + return true; + } + } + + @Override + public PopMenuSettings popMenuSettings() { + return new DefaultPopMenuSettings(); + } + + public class DefaultPopMenuSettings extends PopMenuSettings{ + @Override + public int layout(boolean light) { + return light?R.layout.layout_dialogx_popmenu_material_you:R.layout.layout_dialogx_popmenu_material_you_dark; + } + + @Override + public BlurBackgroundSetting blurBackgroundSettings() { + return null; + } + + @Override + public int backgroundMaskColorRes() { + return 0; + } + + @Override + public int overrideMenuDividerDrawableRes(boolean b) { + return 0; + } + + @Override + public int overrideMenuDividerHeight(boolean b) { + return 0; + } + + @Override + public int overrideMenuTextColor(boolean b) { + return 0; + } + + @Override + public int overrideMenuItemLayoutRes(boolean b) { + return 0; + } + + @Override + public int overrideMenuItemBackgroundRes(boolean b, int i, int i1, boolean b1) { + return 0; + } + + @Override + public int overrideSelectionMenuBackgroundColor(boolean b) { + return 0; + } + + @Override + public boolean selectionImageTint(boolean b) { + return false; + } + + @Override + public int paddingVertical() { + return 0; + } + } + + @Override + public PopNotificationSettings popNotificationSettings() { + return new DefaultPopNotificationSettings() ; + } + + public class DefaultPopNotificationSettings extends PopNotificationSettings{ + @Override + public int layout(boolean light) { + return light ? R.layout.layout_dialogx_popnotification_material_you : R.layout.layout_dialogx_popnotification_material_you_dark; + } + + @Override + public PopNotificationSettings.ALIGN align() { + return ALIGN.TOP; + } + + @Override + public int enterAnimResId(boolean light) { + return com.kongzue.dialogx.R.anim.anim_dialogx_notification_enter; + } + + @Override + public int exitAnimResId(boolean light) { + return com.kongzue.dialogx.R.anim.anim_dialogx_notification_exit; + } + + @Override + public boolean tintIcon() { + return false; + } + } + + @Override + public boolean tintButtonBackground() { + return true; + } +} diff --git a/DialogXMaterialYou/src/main/res/drawable-v21/button_dialogx_material_you_light.xml b/DialogXMaterialYou/src/main/res/drawable-v21/button_dialogx_material_you_light.xml new file mode 100644 index 0000000..abc170b --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable-v21/button_dialogx_material_you_light.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable-v21/button_dialogx_material_you_night.xml b/DialogXMaterialYou/src/main/res/drawable-v21/button_dialogx_material_you_night.xml new file mode 100644 index 0000000..2507cd5 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable-v21/button_dialogx_material_you_night.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/button_dialogx_material_you_light.xml b/DialogXMaterialYou/src/main/res/drawable/button_dialogx_material_you_light.xml new file mode 100644 index 0000000..8cb8c1d --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/button_dialogx_material_you_light.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/button_dialogx_material_you_night.xml b/DialogXMaterialYou/src/main/res/drawable/button_dialogx_material_you_night.xml new file mode 100644 index 0000000..5157816 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/button_dialogx_material_you_night.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_low_api_material_you_button_press.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_low_api_material_you_button_press.xml new file mode 100644 index 0000000..53015e9 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_low_api_material_you_button_press.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_low_api_material_you_button_press_night.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_low_api_material_you_button_press_night.xml new file mode 100644 index 0000000..b53891b --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_low_api_material_you_button_press_night.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_bkg_light.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_bkg_light.xml new file mode 100644 index 0000000..3f2f3af --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_bkg_light.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_bkg_night.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_bkg_night.xml new file mode 100644 index 0000000..3f2f3af --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_bkg_night.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_bottom_bkg_light.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_bottom_bkg_light.xml new file mode 100644 index 0000000..43eea43 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_bottom_bkg_light.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_bottom_bkg_night.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_bottom_bkg_night.xml new file mode 100644 index 0000000..43eea43 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_bottom_bkg_night.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_button_light_forword.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_button_light_forword.xml new file mode 100644 index 0000000..69a7569 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_button_light_forword.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_button_night_forword.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_button_night_forword.xml new file mode 100644 index 0000000..69a7569 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_button_night_forword.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_dialogtap.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_dialogtap.xml new file mode 100644 index 0000000..f10aebb --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_dialogtap.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_dialogtap_night.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_dialogtap_night.xml new file mode 100644 index 0000000..d32ce17 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_dialogtap_night.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_popmenu_bkg.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_popmenu_bkg.xml new file mode 100644 index 0000000..d7f2371 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_popmenu_bkg.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_popmenu_bkg_night.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_popmenu_bkg_night.xml new file mode 100644 index 0000000..d7f2371 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_popmenu_bkg_night.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_popnotification_bkg.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_popnotification_bkg.xml new file mode 100644 index 0000000..ccee0a8 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_popnotification_bkg.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_popnotification_bkg_night.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_popnotification_bkg_night.xml new file mode 100644 index 0000000..ccee0a8 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_popnotification_bkg_night.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_poptip_bkg.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_poptip_bkg.xml new file mode 100644 index 0000000..045aae3 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_poptip_bkg.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_poptip_bkg_night.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_poptip_bkg_night.xml new file mode 100644 index 0000000..045aae3 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_poptip_bkg_night.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/DialogXMaterialYou/src/main/res/layout/layout_dialogx_bottom_material_you.xml b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_bottom_material_you.xml new file mode 100644 index 0000000..c6d3535 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_bottom_material_you.xml @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/layout/layout_dialogx_bottom_material_you_dark.xml b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_bottom_material_you_dark.xml new file mode 100644 index 0000000..d0c70e2 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_bottom_material_you_dark.xml @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DialogXMaterialYou/src/main/res/layout/layout_dialogx_fullscreen_material_you.xml b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_fullscreen_material_you.xml new file mode 100644 index 0000000..d29ac81 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_fullscreen_material_you.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/DialogXMaterialYou/src/main/res/layout/layout_dialogx_fullscreen_material_you_dark.xml b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_fullscreen_material_you_dark.xml new file mode 100644 index 0000000..e3662fb --- /dev/null +++ b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_fullscreen_material_you_dark.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/DialogXMaterialYou/src/main/res/layout/layout_dialogx_material_you.xml b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_material_you.xml new file mode 100644 index 0000000..4d965d2 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_material_you.xml @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/layout/layout_dialogx_material_you_dark.xml b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_material_you_dark.xml new file mode 100644 index 0000000..a4744e2 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_material_you_dark.xml @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/layout/layout_dialogx_popmenu_material_you.xml b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_popmenu_material_you.xml new file mode 100644 index 0000000..3616c85 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_popmenu_material_you.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/layout/layout_dialogx_popmenu_material_you_dark.xml b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_popmenu_material_you_dark.xml new file mode 100644 index 0000000..d83ad8b --- /dev/null +++ b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_popmenu_material_you_dark.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/layout/layout_dialogx_popnotification_material_you.xml b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_popnotification_material_you.xml new file mode 100644 index 0000000..e90794f --- /dev/null +++ b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_popnotification_material_you.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/layout/layout_dialogx_popnotification_material_you_dark.xml b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_popnotification_material_you_dark.xml new file mode 100644 index 0000000..c6dbf19 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_popnotification_material_you_dark.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/layout/layout_dialogx_poptip_material_you.xml b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_poptip_material_you.xml new file mode 100644 index 0000000..8e78523 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_poptip_material_you.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/layout/layout_dialogx_poptip_material_you_dark.xml b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_poptip_material_you_dark.xml new file mode 100644 index 0000000..64d8f1a --- /dev/null +++ b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_poptip_material_you_dark.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/values/colors.xml b/DialogXMaterialYou/src/main/res/values/colors.xml new file mode 100644 index 0000000..fb1ce67 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/values/colors.xml @@ -0,0 +1,5 @@ + + + #F2F6FC + #2E3032 + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/values/styles.xml b/DialogXMaterialYou/src/main/res/values/styles.xml new file mode 100644 index 0000000..ee360e8 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/values/styles.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json deleted file mode 100644 index ac87c7f..0000000 --- a/app/release/output-metadata.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "version": 3, - "artifactType": { - "type": "APK", - "kind": "Directory" - }, - "applicationId": "com.tairui.gov_affairs_cloud", - "variantName": "release", - "elements": [ - { - "type": "SINGLE", - "filters": [], - "attributes": [], - "versionCode": 1, - "versionName": "1.0", - "outputFile": "GovAffairsCloud_app_v1.0_20250523090051.apk" - } - ], - "elementType": "File" -} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2b19c62..ae8dbbc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -129,7 +129,27 @@ android:exported="false" android:screenOrientation="portrait" /> + + + + diff --git a/app/src/main/java/com/tairui/gov_affairs_cloud/entity/Api.java b/app/src/main/java/com/tairui/gov_affairs_cloud/entity/Api.java index b34f0a3..96412f4 100644 --- a/app/src/main/java/com/tairui/gov_affairs_cloud/entity/Api.java +++ b/app/src/main/java/com/tairui/gov_affairs_cloud/entity/Api.java @@ -2,8 +2,8 @@ package com.tairui.gov_affairs_cloud.entity; public class Api { -// public static String BASE_HOST = "http://192.168.18.14:8080"; - public static String BASE_HOST = "http://47.109.205.240:8080"; + public static String BASE_HOST = "http://192.168.18.9:8080"; + // public static String BASE_HOST = "http://47.109.205.240:8080"; public static String OSS_HOST = "http://gov-cloud.oss-cn-chengdu.aliyuncs.com/"; /** @@ -35,12 +35,17 @@ public class Api { public static final String LAND_OVERVIEW = BASE_HOST + "/land-resource/analysis/overview"; public static final String LAND_SUB_AREA = BASE_HOST + "/land-resource/analysis/subArea"; public static final String LAND_AREA_REGION = BASE_HOST + "/system/area/region"; - public static final String LAND_GRID = BASE_HOST + "/land-resource/gridManage/app/getGridInfo"; + public static final String LAND_GRID_HOME = BASE_HOST + "/land-resource/gridManage/app/getGridInfo"; + public static final String LAND_GRID_LIST = BASE_HOST + "/land-resource/gridManage/page"; public static final String ADD_LAND = BASE_HOST + "/land-resource/landManage/save"; public static final String EDIT_LAND = BASE_HOST + "/land-resource/landManage/edit"; public static final String LAND_GROUD_TYPE = BASE_HOST + "/land-resource/baseInfo/soilTypePage"; public static final String LAND_TYPE = BASE_HOST + "/land-resource/baseInfo/landTree"; public static final String LAND_RESOURCE_TYPE_NO_CHILD = BASE_HOST + "/land-resource/baseInfo/landTypeNoChild"; public static final String LAND_RESOURCE_LIST = BASE_HOST + "/land-resource/landManage/page"; + public static final String PLANT_PLAN_HOME = BASE_HOST + "/land-resource/annualManage/app/main"; + public static final String PLANT_PLAN_LIST = BASE_HOST + "/land-resource/annualManage/page"; + public static final String PLANT_PLAN_CROPS = BASE_HOST + "/land-resource/crops/list"; + public static final String PLANT_PLAN_DETAIL = BASE_HOST + "/land-resource/annualManage/"; } diff --git a/app/src/main/java/com/tairui/gov_affairs_cloud/entity/Constant.java b/app/src/main/java/com/tairui/gov_affairs_cloud/entity/Constant.java index e00a852..61acbfa 100644 --- a/app/src/main/java/com/tairui/gov_affairs_cloud/entity/Constant.java +++ b/app/src/main/java/com/tairui/gov_affairs_cloud/entity/Constant.java @@ -6,4 +6,8 @@ public class Constant { public static String LOGIN_USER_NAME = "login_user_name"; + public static String SELECT_REGION = "select_region"; + + public static String SELECT_GRID = "select_grid"; + } diff --git a/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/AddLandActivity.java b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/AddLandActivity.java index 9d64082..fdc0c79 100644 --- a/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/AddLandActivity.java +++ b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/AddLandActivity.java @@ -28,7 +28,7 @@ import com.tairui.gov_affairs_cloud.entity.UploadEntity; import com.tairui.gov_affairs_cloud.http.OnError; import com.tairui.gov_affairs_cloud.ui.land.entity.AddLandEntity; import com.tairui.gov_affairs_cloud.ui.land.entity.LandAreaRegionEntity; -import com.tairui.gov_affairs_cloud.ui.land.entity.LandGridEntity; +import com.tairui.gov_affairs_cloud.ui.land.entity.LandGridHomeEntity; import com.tairui.gov_affairs_cloud.ui.land.entity.LandGroudTypeEntity; import com.tairui.gov_affairs_cloud.ui.land.entity.LandTypeEntity; import com.tairui.gov_affairs_cloud.util.ArrayUtil; @@ -61,8 +61,8 @@ public class AddLandActivity extends BaseActivity { private LandTypeEntity selectLandType; private List landAreaRegionData; private LandAreaRegionEntity selectRegion; - private LandGridEntity landGridData; - private LandGridEntity selectGrid; + private LandGridHomeEntity landGridData; + private LandGridHomeEntity selectGrid; private LandGroudTypeEntity landGroudTypeData; private LandGroudTypeEntity.RecordsEntity selectLandGroudType; @@ -185,8 +185,8 @@ public class AddLandActivity extends BaseActivity { } private void getGridData() { - RxHttp.get(Api.LAND_GRID) - .asResponse(LandGridEntity.class) + RxHttp.get(Api.LAND_GRID_HOME) + .asResponse(LandGridHomeEntity.class) .as(RxLife.asOnMain(this)) .subscribe(data -> { landGridData = data; diff --git a/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/AddPlantPlanActivity.java b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/AddPlantPlanActivity.java new file mode 100644 index 0000000..c6dd38d --- /dev/null +++ b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/AddPlantPlanActivity.java @@ -0,0 +1,325 @@ +package com.tairui.gov_affairs_cloud.ui.land; + +import java.util.ArrayList; +import java.util.List; + +import com.bigkoo.pickerview.builder.OptionsPickerBuilder; +import com.bigkoo.pickerview.view.OptionsPickerView; +import com.contrarywind.interfaces.IPickerViewData; +import com.rxjava.rxlife.RxLife; +import com.tairui.gov_affairs_cloud.base.BaseActivity; +import com.tairui.gov_affairs_cloud.databinding.ActivityAddPlantPlanBinding; +import com.tairui.gov_affairs_cloud.entity.Api; +import com.tairui.gov_affairs_cloud.http.OnError; +import com.tairui.gov_affairs_cloud.ui.land.entity.CropsEntity; +import com.tairui.gov_affairs_cloud.ui.land.entity.LandAreaRegionEntity; +import com.tairui.gov_affairs_cloud.ui.land.entity.LandGridEntity; +import com.tairui.gov_affairs_cloud.util.ArrayUtil; +import com.tairui.gov_affairs_cloud.util.SingleClickListener; + +import android.graphics.Color; +import android.view.View; +import rxhttp.RxHttp; + +public class AddPlantPlanActivity extends BaseActivity { + + private LandAreaRegionEntity selectRegion; + private LandGridEntity.RecordsEntity selectGrid; + private CropsEntity selectCrop; + private String selectMonth; + private String growthCycle; + private int growthCycleUnit = -1; //成长周期单位 1:天 2:周 3:月 4:年 + + private List landAreaRegionData; + private List cropsData; + private LandGridEntity landGridData; + + private OptionsPickerView landRegionPickerView; + private OptionsPickerView landGridPickerView; + private OptionsPickerView cropsPickerView; + private OptionsPickerView monthPickerView; + private OptionsPickerView cycleUnitPickerView; + + @Override + protected Class getBindingClass() { + return ActivityAddPlantPlanBinding.class; + } + + @Override + protected void onBindListener() { + binding.btnBack.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + finish(); + } + }); + binding.layoutRegion.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + showRegionDialog(); + } + }); + binding.layoutGrid.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + if (selectRegion == null) { + showError("请先选择所属行政区域"); + return; + } + if (selectGrid == null) { + getGridData(); + } else { + showLandGridDialog(); + } + } + }); + binding.layoutCrops.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + showCropsDialog(); + } + }); + binding.layoutMonth.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + showMonthDialog(); + } + }); + binding.layoutCycle.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + showCycleUnitDialog(); + } + }); + } + + @Override + protected void onApplyData() { + getRegionData(); + getCropsData(); + } + + private void getRegionData() { + RxHttp.get(Api.LAND_AREA_REGION) + .add("areaCode", "530926") + .asResponseList(LandAreaRegionEntity.class) + .as(RxLife.asOnMain(this)) + .subscribe(data -> { + landAreaRegionData = data; + }, (OnError) error -> showToast(error.getErrorMsg())); + } + + private void showRegionDialog() { + if (landRegionPickerView == null) { + List> subLandData = new ArrayList<>(); + for (LandAreaRegionEntity itemData : landAreaRegionData.get(0).getAreaChildVOS()) { + subLandData.add(itemData.getAreaChildVOS()); + } + + landRegionPickerView = new OptionsPickerBuilder(this, (options1, options2, options3, v) -> { + selectRegion = landAreaRegionData.get(0).getAreaChildVOS().get(options1).getAreaChildVOS().get(options2); + setText(binding.tvRegion, selectRegion.getAreaName()); + selectGrid = null; + }).setTitleText("土地区域选择").setContentTextSize(20) + .setSelectOptions(0, 0) + .setTitleBgColor(Color.WHITE) + .isRestoreItem(true)//切换时是否还原,设置默认选中第一项。 + .isCenterLabel(false) //是否只显示中间选中项的label文字,false则每项item全部都带有label。 + .setOutSideColor(0x00000000) //设置外部遮罩颜色 + .build(); + + landRegionPickerView.setPicker(landAreaRegionData.get(0).getAreaChildVOS(), subLandData);//二级选择器 + } + landRegionPickerView.show(); + } + + private void getGridData() { + showLoading(); + RxHttp.get(Api.LAND_GRID_LIST) + .add("regionCode", selectRegion.getAreaCode()) + .asResponse(LandGridEntity.class) + .as(RxLife.asOnMain(this)) + .subscribe(data -> { + hideLoading(); + if (ArrayUtil.isEmpty(data.getRecords())) { + showToast("当前区域下没有网格,请重新选择"); + binding.layoutGrid.postDelayed(() -> showRegionDialog(), 500); + } else { + landGridData = data; + showLandGridDialog(); + } + }, (OnError) error -> showToast(error.getErrorMsg())); + } + + private void showLandGridDialog() { + if (landGridPickerView == null) { + landGridPickerView = new OptionsPickerBuilder(this, (options1, options2, options3, v) -> { + selectGrid = landGridData.getRecords().get(options1); + setText(binding.tvGrid, selectGrid.getGridName()); + }).setTitleText("土地网格选择").setContentTextSize(20) + .setSelectOptions(0) + .setTitleBgColor(Color.WHITE) + .isRestoreItem(true) + .isCenterLabel(false) + .setOutSideColor(0x00000000) + .build(); + + landGridPickerView.setPicker(landGridData.getRecords()); + } + landGridPickerView.show(); + } + + private void getCropsData() { + RxHttp.get(Api.PLANT_PLAN_CROPS) + .asResponseList(CropsEntity.class) + .as(RxLife.asOnMain(this)) + .subscribe(data -> { + cropsData = data; + }, (OnError) error -> showToast(error.getErrorMsg())); + } + + private void showCropsDialog() { + if (cropsPickerView == null) { + cropsPickerView = new OptionsPickerBuilder(this, (options1, options2, options3, v) -> { + selectCrop = cropsData.get(options1); + setText(binding.tvCrops, selectCrop.getCropsName()); + }).setTitleText("种植作物选择").setContentTextSize(20) + .setSelectOptions(0) + .setTitleBgColor(Color.WHITE) + .isRestoreItem(true) + .isCenterLabel(false) + .setOutSideColor(0x00000000) + .build(); + + cropsPickerView.setPicker(cropsData); + } + cropsPickerView.show(); + } + + private void showMonthDialog() { + if (monthPickerView == null) { + List monthData = new ArrayList<>(); + for (int i = 0; i < 12; i++) { + monthData.add(new PickerEntity((i + 1) + "月")); + } + monthPickerView = new OptionsPickerBuilder(this, (options1, options2, options3, v) -> { + selectMonth = String.valueOf(options1 + 1); + setText(binding.tvMonth, selectMonth + "月"); + }).setTitleText("种植月份选择").setContentTextSize(20) + .setSelectOptions(0) + .setTitleBgColor(Color.WHITE) + .isRestoreItem(true) + .isCenterLabel(false) + .setOutSideColor(0x00000000) + .build(); + + monthPickerView.setPicker(monthData); + } + monthPickerView.show(); + } + + private void showCycleUnitDialog() { + if (cycleUnitPickerView == null) { + List unitData = new ArrayList<>(); + unitData.add(new PickerEntity("天")); + unitData.add(new PickerEntity("周")); + unitData.add(new PickerEntity("月")); + unitData.add(new PickerEntity("年")); + cycleUnitPickerView = new OptionsPickerBuilder(this, (options1, options2, options3, v) -> { + growthCycleUnit = options1 + 1; + showCyclDialog(); + }).setTitleText("种植周期单位选择").setContentTextSize(20) + .setSelectOptions(0) + .setTitleBgColor(Color.WHITE) + .isRestoreItem(true) + .isCenterLabel(false) + .setOutSideColor(0x00000000) + .build(); + + cycleUnitPickerView.setPicker(unitData); + } + cycleUnitPickerView.show(); + } + + private void showCyclDialog() { + if (growthCycleUnit == -1) { + showError("请先选择生长周期单位"); + return; + } + List cycleData = new ArrayList<>(); + if (growthCycleUnit == 1) { + for (int i = 1; i < 32; i++) { + cycleData.add(new PickerEntity(i + "天")); + } + } else if (growthCycleUnit == 2) { + for (int i = 1; i < 53; i++) { + cycleData.add(new PickerEntity(i + "周")); + } + } else if (growthCycleUnit == 3) { + for (int i = 1; i < 13; i++) { + cycleData.add(new PickerEntity(i + "月")); + } + } else if (growthCycleUnit == 4) { + for (int i = 1; i < 6; i++) { + cycleData.add(new PickerEntity(i + "年")); + } + } + OptionsPickerView cyclePickerView = new OptionsPickerBuilder(this, (options1, options2, options3, v) -> { + growthCycle = String.valueOf(options1 + 1); + setText(binding.tvCycle, growthCycle + getCycleUnit(growthCycleUnit)); + }).setTitleText("种植周期时长选择").setContentTextSize(20) + .setSelectOptions(0) + .setTitleBgColor(Color.WHITE) + .isRestoreItem(true) + .isCenterLabel(false) + .setOutSideColor(0x00000000) + .build(); + + cyclePickerView.setPicker(cycleData); + cyclePickerView.show(); + } + + private class PickerEntity implements IPickerViewData { + private String name; + + public PickerEntity(String m) { + this.name = m; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String getPickerViewText() { + return name; + } + } + + private String getCycleUnit(int unit) { + String unitStr; + switch (unit) { + case 1: + unitStr = "天"; + break; + case 2: + unitStr = "周"; + break; + case 3: + unitStr = "月"; + break; + case 4: + unitStr = "年"; + break; + default: + unitStr = ""; + break; + } + return unitStr; + } + +} diff --git a/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/EditLandBaseInfoActivity.java b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/EditLandBaseInfoActivity.java index bcf6306..d40ec13 100644 --- a/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/EditLandBaseInfoActivity.java +++ b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/EditLandBaseInfoActivity.java @@ -27,7 +27,7 @@ import com.tairui.gov_affairs_cloud.entity.EventMessage; import com.tairui.gov_affairs_cloud.entity.UploadEntity; import com.tairui.gov_affairs_cloud.http.OnError; import com.tairui.gov_affairs_cloud.ui.land.entity.LandAreaRegionEntity; -import com.tairui.gov_affairs_cloud.ui.land.entity.LandGridEntity; +import com.tairui.gov_affairs_cloud.ui.land.entity.LandGridHomeEntity; import com.tairui.gov_affairs_cloud.ui.land.entity.LandGroudTypeEntity; import com.tairui.gov_affairs_cloud.ui.land.entity.LandResourceEntity; import com.tairui.gov_affairs_cloud.ui.land.entity.LandTypeEntity; @@ -63,8 +63,8 @@ public class EditLandBaseInfoActivity extends BaseActivity landAreaRegionData; private LandAreaRegionEntity selectRegion; - private LandGridEntity landGridData; - private LandGridEntity selectGrid; + private LandGridHomeEntity landGridData; + private LandGridHomeEntity selectGrid; private LandGroudTypeEntity landGroudTypeData; private LandGroudTypeEntity.RecordsEntity selectLandGroudType; @@ -199,8 +199,8 @@ public class EditLandBaseInfoActivity extends BaseActivity { landGridData = data; diff --git a/app/src/main/java/com/tairui/gov_affairs_cloud/ui/grid/GridInfoActivity.java b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/GridInfoActivity.java similarity index 89% rename from app/src/main/java/com/tairui/gov_affairs_cloud/ui/grid/GridInfoActivity.java rename to app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/GridInfoActivity.java index 15473bc..6918ad0 100644 --- a/app/src/main/java/com/tairui/gov_affairs_cloud/ui/grid/GridInfoActivity.java +++ b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/GridInfoActivity.java @@ -1,4 +1,4 @@ -package com.tairui.gov_affairs_cloud.ui.grid; +package com.tairui.gov_affairs_cloud.ui.land; import java.util.List; @@ -10,7 +10,7 @@ import com.tairui.gov_affairs_cloud.base.BaseActivity; import com.tairui.gov_affairs_cloud.databinding.ActivityGridInfoBinding; import com.tairui.gov_affairs_cloud.entity.Api; import com.tairui.gov_affairs_cloud.http.OnError; -import com.tairui.gov_affairs_cloud.ui.land.entity.LandGridEntity; +import com.tairui.gov_affairs_cloud.ui.land.entity.LandGridHomeEntity; import com.tairui.gov_affairs_cloud.util.SingleClickListener; import com.tairui.gov_affairs_cloud.widget.blankview.interfaces.BlankViewClickListener; @@ -24,7 +24,7 @@ import rxhttp.RxHttp; public class GridInfoActivity extends BaseActivity { - private LandGridEntity mData; + private LandGridHomeEntity mData; private GridAdapter mAdapter; @Override @@ -62,8 +62,8 @@ public class GridInfoActivity extends BaseActivity { setGone(binding.loadingView, true); setGone(binding.contentLayout, false); setGone(binding.blankView, false); - RxHttp.get(Api.LAND_GRID) - .asResponse(LandGridEntity.class) + RxHttp.get(Api.LAND_GRID_HOME) + .asResponse(LandGridHomeEntity.class) .as(RxLife.asOnMain(this)) .subscribe(data -> { mData = data; @@ -81,8 +81,8 @@ public class GridInfoActivity extends BaseActivity { setText(binding.tvCooperativeCount, mData.getCooperativeCount().toString()); setText(binding.tvGridCount, mData.getTotal().toString()); - LandGridEntity headerItem = new LandGridEntity(); - List childList = mData.getChildren(); + LandGridHomeEntity headerItem = new LandGridHomeEntity(); + List childList = mData.getChildren(); childList.add(0, headerItem); mAdapter.setNewData(childList); setGone(binding.loadingView, false); @@ -90,14 +90,14 @@ public class GridInfoActivity extends BaseActivity { setGone(binding.blankView, false); } - private class GridAdapter extends BaseQuickAdapter { + private class GridAdapter extends BaseQuickAdapter { public GridAdapter() { super(R.layout.item_grid_info); } @Override - protected void convert(@NonNull BaseViewHolder holder, LandGridEntity entity) { + protected void convert(@NonNull BaseViewHolder holder, LandGridHomeEntity entity) { if (TextUtils.isEmpty(entity.getRegionCode())) { holder.setTextColor(R.id.tvItemGridName, getResColor(R.color.color_txt_black)); holder.setText(R.id.tvItemGridName, "网格名称"); diff --git a/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/PlantPlanActivity.java b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/PlantPlanActivity.java new file mode 100644 index 0000000..4ff8395 --- /dev/null +++ b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/PlantPlanActivity.java @@ -0,0 +1,279 @@ +package com.tairui.gov_affairs_cloud.ui.land; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import com.bigkoo.pickerview.builder.OptionsPickerBuilder; +import com.bigkoo.pickerview.view.OptionsPickerView; +import com.kongzue.dialogx.dialogs.MessageDialog; +import com.orhanobut.hawk.Hawk; +import com.rxjava.rxlife.RxLife; +import com.tairui.gov_affairs_cloud.R; +import com.tairui.gov_affairs_cloud.base.BaseActivity; +import com.tairui.gov_affairs_cloud.databinding.ActivityPlantPlanBinding; +import com.tairui.gov_affairs_cloud.entity.Api; +import com.tairui.gov_affairs_cloud.entity.Constant; +import com.tairui.gov_affairs_cloud.http.OnError; +import com.tairui.gov_affairs_cloud.ui.land.entity.LandAreaRegionEntity; +import com.tairui.gov_affairs_cloud.ui.land.entity.LandGridEntity; +import com.tairui.gov_affairs_cloud.ui.land.entity.PlantPlanHomeEntity; +import com.tairui.gov_affairs_cloud.util.ArrayUtil; +import com.tairui.gov_affairs_cloud.util.DateUtils; +import com.tairui.gov_affairs_cloud.util.DensityUtils; +import com.tairui.gov_affairs_cloud.util.IntentUtil; +import com.tairui.gov_affairs_cloud.util.SingleClickListener; +import com.tairui.gov_affairs_cloud.widget.charts.bean.BarBean; + +import android.graphics.Color; +import android.os.Bundle; +import android.view.View; +import rxhttp.RxHttp; + +public class PlantPlanActivity extends BaseActivity { + + private LandAreaRegionEntity selectRegion; + private LandGridEntity.RecordsEntity selectGrid; + + private List landAreaRegionData; + private LandGridEntity landGridData; + + private OptionsPickerView landRegionPickerView; + private OptionsPickerView landGridPickerView; + + private PlantPlanHomeEntity mData; + + @Override + protected Class getBindingClass() { + return ActivityPlantPlanBinding.class; + } + + @Override + protected void onQueryArguments() { + if (Hawk.contains(Constant.SELECT_REGION)) { + selectRegion = Hawk.get(Constant.SELECT_REGION); + } + if (Hawk.contains(Constant.SELECT_GRID)) { + selectGrid = Hawk.get(Constant.SELECT_GRID); + } + } + + @Override + protected void onFindView(Bundle savedInstanceState) { + binding.plantChart.setLoading(true); + binding.plantChart.setBarNum(2); + binding.plantChart.setBarColor(new int[] {Color.parseColor("#007AFF"), Color.parseColor("#61d007")}); + binding.plantChart.setBarItemSpace(DensityUtils.dp2px(mContext, 16)); + binding.plantChart.setBarWidth(DensityUtils.dp2px(mContext, 8)); + binding.plantChart.setBarSpace(DensityUtils.dp2px(mContext, 4)); + binding.plantChart.setBackColor(getResColor(R.color.white)); + binding.plantChart.setTextColorTag(getResColor(R.color.color_txt_black)); + + binding.gridChart.setLoading(true); + binding.gridChart.setBarNum(1); + binding.gridChart.setBarColor(new int[] {Color.parseColor("#007AFF")}); + binding.gridChart.setBarItemSpace(DensityUtils.dp2px(mContext, 20)); + binding.gridChart.setBarWidth(DensityUtils.dp2px(mContext, 10)); + binding.gridChart.setBackColor(getResColor(R.color.white)); + binding.gridChart.setTextColorTag(getResColor(R.color.color_txt_black)); + } + + @Override + protected void onBindListener() { + binding.btnBack.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + finish(); + } + }); + binding.layoutRegion.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + showRegionDialog(); + } + }); + binding.layoutGrid.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + showLandGridDialog(); + } + }); + binding.layoutPlanSummarize.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + goList(); + } + }); + binding.layoutPlanSummarize.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + goList(); + } + }); + } + + private void goList() { + Bundle bundle = new Bundle(); + bundle.putSerializable(Constant.SELECT_REGION, (Serializable) selectRegion); + bundle.putSerializable(Constant.SELECT_GRID, (Serializable) selectGrid); + IntentUtil.startActivity(mContext, PlantPlanListActivity.class, bundle); + } + + @Override + protected void onApplyData() { + getRegionData(); + if (selectRegion != null) { + getGridData(); + } + if (selectRegion != null && selectGrid != null) { + getPlantPlanData(); + } + } + + private void showRegionNoticeMsg() { + MessageDialog.show("选择区域", "请先选择区域信息", "确定", "取消") + .setOkButton((baseDialog, v1) -> { + showRegionDialog(); + return false; + }).setCancelButton((dialog, v) -> { + finish(); + return false; + }); + } + + private void getRegionData() { + RxHttp.get(Api.LAND_AREA_REGION) + .add("areaCode", "530926") + .asResponseList(LandAreaRegionEntity.class) + .as(RxLife.asOnMain(this)) + .subscribe(data -> { + landAreaRegionData = data; + if (selectRegion == null) { + showRegionNoticeMsg(); + } + }, (OnError) error -> showToast(error.getErrorMsg())); + } + + private void getGridData() { + RxHttp.get(Api.LAND_GRID_LIST) + .add("regionCode", selectRegion.getAreaCode()) + .asResponse(LandGridEntity.class) + .as(RxLife.asOnMain(this)) + .subscribe(data -> { + hideLoading(); + if (ArrayUtil.isEmpty(data.getRecords())) { + showToast("当前区域下没有网格,请重新选择"); + binding.layoutGrid.postDelayed(() -> showRegionDialog(), 500); + } else { + landGridData = data; + if (selectGrid == null) { + showLandGridDialog(); + } + } + }, (OnError) error -> showToast(error.getErrorMsg())); + } + + private void showRegionDialog() { + if (landRegionPickerView == null) { + List> subLandData = new ArrayList<>(); + for (LandAreaRegionEntity itemData : landAreaRegionData.get(0).getAreaChildVOS()) { + subLandData.add(itemData.getAreaChildVOS()); + } + + landRegionPickerView = new OptionsPickerBuilder(this, (options1, options2, options3, v) -> { + selectRegion = landAreaRegionData.get(0).getAreaChildVOS().get(options1).getAreaChildVOS().get(options2); + Hawk.put(Constant.SELECT_REGION, selectRegion); + selectGrid = null; + Hawk.delete(Constant.SELECT_GRID); + showLoading(); + getGridData(); + }).setTitleText("土地区域选择").setContentTextSize(20) + .setSelectOptions(0, 0) + .setTitleBgColor(Color.WHITE) + .isRestoreItem(true)//切换时是否还原,设置默认选中第一项。 + .isCenterLabel(false) //是否只显示中间选中项的label文字,false则每项item全部都带有label。 + .setOutSideColor(0x00000000) //设置外部遮罩颜色 + .addOnCancelClickListener(view -> finish()) + .build(); + + landRegionPickerView.setPicker(landAreaRegionData.get(0).getAreaChildVOS(), subLandData);//二级选择器 + } + landRegionPickerView.show(); + } + + private void showGridNoticeMsg() { + MessageDialog.show("选择网格", "不继续选择网格信息了吗?", "确定", "取消") + .setOkButton((baseDialog, v1) -> { + finish(); + return false; + }); + } + + private void showLandGridDialog() { + if (landGridPickerView == null) { + landGridPickerView = new OptionsPickerBuilder(this, (options1, options2, options3, v) -> { + selectGrid = landGridData.getRecords().get(options1); + Hawk.put(Constant.SELECT_GRID, selectGrid); + showLoading(); + getPlantPlanData(); + }).setTitleText("土地网格选择").setContentTextSize(20) + .setSelectOptions(0) + .setTitleBgColor(Color.WHITE) + .isRestoreItem(true) + .isCenterLabel(false) + .setOutSideColor(0x00000000) + .addOnCancelClickListener(view -> { + if (selectGrid == null) { + showGridNoticeMsg(); + } + }) + .build(); + + landGridPickerView.setPicker(landGridData.getRecords()); + } + landGridPickerView.show(); + } + + private void getPlantPlanData() { + RxHttp.get(Api.PLANT_PLAN_HOME) + .add("year", String.valueOf(DateUtils.getCurrentYear())) + .add("gridId", selectGrid.getId()) + .add("regionCode", selectRegion.getAreaCode()) + .asResponse(PlantPlanHomeEntity.class) + .as(RxLife.asOnMain(this)) + .subscribe(data -> { + mData = data; + hideLoading(); + initView(); + }, (OnError) error -> showToast(error.getErrorMsg())); + } + + private void initView() { + setText(binding.tvRegion, selectRegion.getAreaName()); + setText(binding.tvGrid, selectGrid.getGridName()); + setText(binding.tvYear, DateUtils.getCurrentYear() + "年种植计划"); + setText(binding.tvPlanArea, String.valueOf(mData.getTotal().getTotalPlanArea())); + setText(binding.tvRealArea, String.valueOf(mData.getTotal().getTotalActualArea())); + + List planXList = new ArrayList<>(); + List> planDataList = new ArrayList<>(); + List> gridDataList = new ArrayList<>(); + for (PlantPlanHomeEntity.CropsEntity item : mData.getCrops()) { + List listPlan = new ArrayList<>(); + List listGrid = new ArrayList<>(); + listPlan.add(new BarBean(item.getArea().getTotalPlanArea(), "计划种植")); + listPlan.add(new BarBean(item.getArea().getTotalActualArea(), "实际种植")); + listGrid.add(new BarBean(item.getCurrentProgress(), "完成率")); + planDataList.add(listPlan); + gridDataList.add(listGrid); + planXList.add(item.getCropsName()); + } + binding.plantChart.setLoading(false); + binding.plantChart.setData(planDataList, planXList); + binding.gridChart.setLoading(false); + binding.gridChart.setData(gridDataList, planXList); + + setGone(binding.contentLayout, true); + } + +} diff --git a/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/PlantPlanDetailActivity.java b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/PlantPlanDetailActivity.java new file mode 100644 index 0000000..77f4c38 --- /dev/null +++ b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/PlantPlanDetailActivity.java @@ -0,0 +1,134 @@ +package com.tairui.gov_affairs_cloud.ui.land; + +import com.rxjava.rxlife.RxLife; +import com.tairui.gov_affairs_cloud.R; +import com.tairui.gov_affairs_cloud.base.BaseActivity; +import com.tairui.gov_affairs_cloud.databinding.ActivityPlantPlanDetailBinding; +import com.tairui.gov_affairs_cloud.entity.Api; +import com.tairui.gov_affairs_cloud.http.OnError; +import com.tairui.gov_affairs_cloud.ui.land.entity.PlantPlanDetailEntity; +import com.tairui.gov_affairs_cloud.util.AppUtil; +import com.tairui.gov_affairs_cloud.util.SingleClickListener; +import com.tairui.gov_affairs_cloud.util.glide.GlideLoader; + +import android.text.TextUtils; +import android.view.View; +import rxhttp.RxHttp; + +public class PlantPlanDetailActivity extends BaseActivity { + + private String planId; + private PlantPlanDetailEntity mData; + + @Override + protected Class getBindingClass() { + return ActivityPlantPlanDetailBinding.class; + } + + @Override + protected void onQueryArguments() { + planId = getIntent().getStringExtra("plan_id"); + if (TextUtils.isEmpty(planId)) { + finish(); + } + } + + @Override + protected void onBindListener() { + binding.btnBack.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + finish(); + } + }); + binding.btnEditPlan.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + + } + }); + binding.btnAddRealPlantInfo.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + + } + }); + } + + @Override + protected void onApplyData() { + showLoadingView(); + RxHttp.get(Api.PLANT_PLAN_DETAIL + planId) + .asResponse(PlantPlanDetailEntity.class) + .as(RxLife.asOnMain(this)) + .subscribe(data -> { + mData = data; + initView(); + showContentView(); + }, (OnError) error -> { + binding.blankView.setBlankView(R.mipmap.img_no_content, error.getErrorMsg()); + showErrorView(); + }); + } + + private void initView() { + setText(binding.tvPlanName, mData.getPlanName()); + GlideLoader.loadOriginal(binding.img, AppUtil.getImageUrl(mData.getCropsImg())); + setText(binding.tvPlanArea, mData.getPlanParameters().getPlantingArea() + "亩"); + setText(binding.tvPlanMonth, mData.getPlanParameters().getPlantingMonths() + "月"); + setText(binding.tvPlanCycle, + mData.getPlanParameters().getGrowthCycle() + getCycleUnit( + Integer.parseInt(mData.getPlanParameters().getGrowthCycleUnit()))); + if (mData.getActualParameters() != null) { + setText(binding.tvRealArea, mData.getActualParameters().getPlantingArea() + "亩"); + setText(binding.tvRealMonth, mData.getActualParameters().getPlantingMonths() + "月"); + setText(binding.tvRealCycle, + mData.getActualParameters().getGrowthCycle() + getCycleUnit( + Integer.parseInt(mData.getActualParameters().getGrowthCycleUnit()))); + } + + binding.progress.setProgress(mData.getCurrentProgress()); + setText(binding.tvProgress, mData.getCurrentProgress() + "%"); + + } + + private String getCycleUnit(int unit) { + String unitStr; + switch (unit) { + case 1: + unitStr = "天"; + break; + case 2: + unitStr = "周"; + break; + case 3: + unitStr = "月"; + break; + case 4: + unitStr = "年"; + break; + default: + unitStr = ""; + break; + } + return unitStr; + } + + private void showContentView() { + binding.contentView.setVisibility(View.VISIBLE); + binding.loadingView.setVisibility(View.GONE); + binding.blankView.setVisibility(View.GONE); + } + + private void showErrorView() { + binding.contentView.setVisibility(View.GONE); + binding.loadingView.setVisibility(View.GONE); + binding.blankView.setVisibility(View.VISIBLE); + } + + private void showLoadingView() { + binding.contentView.setVisibility(View.GONE); + binding.loadingView.setVisibility(View.VISIBLE); + binding.blankView.setVisibility(View.GONE); + } +} diff --git a/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/PlantPlanListActivity.java b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/PlantPlanListActivity.java new file mode 100644 index 0000000..b49b512 --- /dev/null +++ b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/PlantPlanListActivity.java @@ -0,0 +1,375 @@ +package com.tairui.gov_affairs_cloud.ui.land; + +import java.util.ArrayList; +import java.util.List; + +import com.bigkoo.pickerview.builder.OptionsPickerBuilder; +import com.bigkoo.pickerview.view.OptionsPickerView; +import com.chad.library.adapter.base.BaseQuickAdapter; +import com.chad.library.adapter.base.BaseViewHolder; +import com.kongzue.dialogx.dialogs.MessageDialog; +import com.kongzue.dialogx.dialogs.PopMenu; +import com.kongzue.dialogx.util.ItemDivider; +import com.orhanobut.hawk.Hawk; +import com.rxjava.rxlife.RxLife; +import com.tairui.gov_affairs_cloud.R; +import com.tairui.gov_affairs_cloud.base.BaseActivity; +import com.tairui.gov_affairs_cloud.databinding.ActivityPlantPlanListBinding; +import com.tairui.gov_affairs_cloud.entity.Api; +import com.tairui.gov_affairs_cloud.entity.Constant; +import com.tairui.gov_affairs_cloud.http.OnError; +import com.tairui.gov_affairs_cloud.ui.land.entity.CropsEntity; +import com.tairui.gov_affairs_cloud.ui.land.entity.LandAreaRegionEntity; +import com.tairui.gov_affairs_cloud.ui.land.entity.LandGridEntity; +import com.tairui.gov_affairs_cloud.ui.land.entity.PlantPlanListEntity; +import com.tairui.gov_affairs_cloud.util.AppUtil; +import com.tairui.gov_affairs_cloud.util.ArrayUtil; +import com.tairui.gov_affairs_cloud.util.DateUtils; +import com.tairui.gov_affairs_cloud.util.DensityUtils; +import com.tairui.gov_affairs_cloud.util.IntentUtil; +import com.tairui.gov_affairs_cloud.util.SingleClickListener; +import com.tairui.gov_affairs_cloud.util.ToastUtil; +import com.tairui.gov_affairs_cloud.util.glide.GlideLoader; +import com.tairui.gov_affairs_cloud.widget.RefreshRecyclerView; + +import android.graphics.Color; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.View; +import android.widget.ImageView; +import android.widget.ProgressBar; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.LinearLayoutManager; +import rxhttp.RxHttp; +import rxhttp.RxHttpNoBodyParam; + +public class PlantPlanListActivity extends BaseActivity { + + private LandAreaRegionEntity selectRegion; + private LandGridEntity.RecordsEntity selectGrid; + + private List landAreaRegionData; + private List cropsData; + private LandGridEntity landGridData; + + private OptionsPickerView landRegionPickerView; + private OptionsPickerView landGridPickerView; + + private PlantPlanListAdapter mAdapter; + private int currentPageIndex = 1; + private String selectCropsId = ""; + + @Override + protected Class getBindingClass() { + return ActivityPlantPlanListBinding.class; + } + + @Override + protected void onQueryArguments() { + selectRegion = (LandAreaRegionEntity) getIntent().getSerializableExtra(Constant.SELECT_REGION); + selectGrid = (LandGridEntity.RecordsEntity) getIntent().getSerializableExtra(Constant.SELECT_GRID); + } + + @Override + protected void onFindView(Bundle savedInstanceState) { + binding.refreshRecycler.setLayoutManager(new LinearLayoutManager(mContext)); + mAdapter = new PlantPlanListAdapter(); + binding.refreshRecycler.setAdapter(mAdapter); + setText(binding.tvRegion, selectRegion.getAreaName()); + setText(binding.tvGrid, selectGrid.getGridName()); + } + + @Override + protected void onBindListener() { + binding.btnBack.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + finish(); + } + }); + binding.btnAdd.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + IntentUtil.startActivity(mContext, AddPlantPlanActivity.class); + } + }); + binding.layoutRegion.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + showRegionDialog(); + } + }); + binding.layoutGrid.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + showLandGridDialog(); + } + }); + binding.refreshRecycler.setRefreshRecyclerListener(new RefreshRecyclerView.RefreshRecyclerListener() { + @Override + public void onRefresh() { + currentPageIndex = 1; + requestData(false); + } + + @Override + public void onLoadmore() { + requestData(true); + } + + @Override + public void onBlankClick() { + binding.refreshRecycler.showLoading(); + currentPageIndex = 1; + requestData(false); + } + }); + mAdapter.setOnItemClickListener((baseQuickAdapter, view, i) -> { + PlantPlanListEntity.RecordsEntity item = mAdapter.getItem(i); + Bundle bundle = new Bundle(); + bundle.putString("plan_id", item.getId()); + IntentUtil.startActivity(mContext, PlantPlanDetailActivity.class, bundle); + }); + binding.layoutType.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + showCropsMenu(); + } + }); + } + + @Override + protected void onApplyData() { + getCropsData(); + getRegionData(); + if (selectRegion != null) { + getGridData(); + } + if (selectRegion != null && selectGrid != null) { + binding.refreshRecycler.showLoading(); + currentPageIndex = 1; + requestData(false); + } + } + + private void showRegionNoticeMsg() { + MessageDialog.show("选择区域", "请先选择区域信息", "确定", "取消") + .setOkButton((baseDialog, v1) -> { + showRegionDialog(); + return false; + }).setCancelButton((dialog, v) -> { + finish(); + return false; + }); + } + + private void getRegionData() { + RxHttp.get(Api.LAND_AREA_REGION) + .add("areaCode", "530926") + .asResponseList(LandAreaRegionEntity.class) + .as(RxLife.asOnMain(this)) + .subscribe(data -> { + landAreaRegionData = data; + if (selectRegion == null) { + showRegionNoticeMsg(); + } + }, (OnError) error -> showToast(error.getErrorMsg())); + } + + private void getCropsData() { + RxHttp.get(Api.PLANT_PLAN_CROPS) + .asResponseList(CropsEntity.class) + .as(RxLife.asOnMain(this)) + .subscribe(data -> { + CropsEntity allItem = new CropsEntity(); + allItem.setId(""); + allItem.setCropsName("全部"); + data.add(0, allItem); + cropsData = data; + }, (OnError) error -> showToast(error.getErrorMsg())); + } + + private void showCropsMenu() { + List menus = new ArrayList<>(); + for (CropsEntity item : cropsData) { + menus.add(item.getCropsName()); + } + PopMenu.show(binding.layoutType, menus) + .setBackgroundColorRes(R.color.white) + .setWidth(DensityUtils.dp2px(mContext, 100)) + .setOnMenuItemClickListener((dialog, text, index) -> { + selectCropsId = cropsData.get(index).getId(); + setText(binding.tvType, cropsData.get(index).getCropsName()); + binding.refreshRecycler.showLoading(); + currentPageIndex = 1; + requestData(false); + return false; + }).setItemDivider(new ItemDivider(15, 15, 1)); + } + + private void getGridData() { + RxHttp.get(Api.LAND_GRID_LIST) + .add("regionCode", selectRegion.getAreaCode()) + .asResponse(LandGridEntity.class) + .as(RxLife.asOnMain(this)) + .subscribe(data -> { + hideLoading(); + if (ArrayUtil.isEmpty(data.getRecords())) { + showToast("当前区域下没有网格,请重新选择"); + binding.layoutGrid.postDelayed(() -> showRegionDialog(), 500); + } else { + landGridData = data; + if (selectGrid == null) { + showLandGridDialog(); + } + } + }, (OnError) error -> showToast(error.getErrorMsg())); + } + + private void showRegionDialog() { + if (landRegionPickerView == null) { + List> subLandData = new ArrayList<>(); + for (LandAreaRegionEntity itemData : landAreaRegionData.get(0).getAreaChildVOS()) { + subLandData.add(itemData.getAreaChildVOS()); + } + + landRegionPickerView = new OptionsPickerBuilder(this, (options1, options2, options3, v) -> { + selectRegion = landAreaRegionData.get(0).getAreaChildVOS().get(options1).getAreaChildVOS().get(options2); + Hawk.put(Constant.SELECT_REGION, selectRegion); + selectGrid = null; + Hawk.delete(Constant.SELECT_GRID); + showLoading(); + getGridData(); + }).setTitleText("土地区域选择").setContentTextSize(20) + .setSelectOptions(0, 0) + .setTitleBgColor(Color.WHITE) + .isRestoreItem(true)//切换时是否还原,设置默认选中第一项。 + .isCenterLabel(false) //是否只显示中间选中项的label文字,false则每项item全部都带有label。 + .setOutSideColor(0x00000000) //设置外部遮罩颜色 + .addOnCancelClickListener(view -> finish()) + .build(); + + landRegionPickerView.setPicker(landAreaRegionData.get(0).getAreaChildVOS(), subLandData);//二级选择器 + } + landRegionPickerView.show(); + } + + private void showGridNoticeMsg() { + MessageDialog.show("选择网格", "不继续选择网格信息了吗?", "确定", "取消") + .setOkButton((baseDialog, v1) -> { + finish(); + return false; + }); + } + + private void showLandGridDialog() { + if (landGridPickerView == null) { + landGridPickerView = new OptionsPickerBuilder(this, (options1, options2, options3, v) -> { + selectGrid = landGridData.getRecords().get(options1); + Hawk.put(Constant.SELECT_GRID, selectGrid); + binding.refreshRecycler.showLoading(); + currentPageIndex = 1; + requestData(false); + }).setTitleText("土地网格选择").setContentTextSize(20) + .setSelectOptions(0) + .setTitleBgColor(Color.WHITE) + .isRestoreItem(true) + .isCenterLabel(false) + .setOutSideColor(0x00000000) + .addOnCancelClickListener(view -> { + if (selectGrid == null) { + showGridNoticeMsg(); + } + }).build(); + + landGridPickerView.setPicker(landGridData.getRecords()); + } + landGridPickerView.show(); + } + + private void requestData(boolean loadmore) { + RxHttpNoBodyParam http = RxHttp.get(Api.PLANT_PLAN_LIST); + if (!TextUtils.isEmpty(selectCropsId)) { + http.add("cropsId", selectCropsId); + } + http.add("year", DateUtils.getCurrentYear()) + .add("gridId", selectGrid.getId()) + .add("current", currentPageIndex) + .add("size", Api.SIZE_PAGE) + .asResponse(PlantPlanListEntity.class) + .as(RxLife.asOnMain(this)) + .subscribe(data -> { + if (loadmore) { + binding.refreshRecycler.finishLoadMore(); + } + if (!ArrayUtil.isEmpty(data.getRecords())) { + if (loadmore) { + mAdapter.addData(data.getRecords()); + } else { + mAdapter.setNewData(data.getRecords()); + } + currentPageIndex += 1; + if (ArrayUtil.size(data.getRecords()) < Api.SIZE_PAGE) { + binding.refreshRecycler.setNoMoreData(true); + } else { + binding.refreshRecycler.setNoMoreData(false); + } + binding.refreshRecycler.showContent(); + } else { + binding.refreshRecycler.showError(); + binding.refreshRecycler.setNoMoreData(true); + } + }, (OnError) error -> { + binding.refreshRecycler.showError(); + binding.refreshRecycler.setNoMoreData(true); + ToastUtil.showLongToast(error.getErrorMsg()); + }); + } + + private class PlantPlanListAdapter extends BaseQuickAdapter { + + public PlantPlanListAdapter() { + super(R.layout.item_plant_plan); + } + + @Override + protected void convert(@NonNull BaseViewHolder holder, PlantPlanListEntity.RecordsEntity entity) { + ImageView img = holder.getView(R.id.img); + GlideLoader.loadOriginal(img, AppUtil.getImageUrl(entity.getCropsImg())); + + holder.setText(R.id.tvName, entity.getPlanName()); + holder.setText(R.id.tvPlantingMonths, entity.getPlantingMonths() + "月种植"); + holder.setText(R.id.tvCrops, entity.getCropsName()); + holder.setText(R.id.tvGrowthCycle, + "生长周期" + entity.getGrowthCycle() + getCycleUnit(Integer.parseInt(entity.getGrowthCycleUnit()))); + holder.setText(R.id.tvArea, entity.getPlantingArea() + "亩"); + ProgressBar progressBar = holder.getView(R.id.progress); + progressBar.setProgress(entity.getCurrentProgress()); + holder.setText(R.id.tvProgress, entity.getCurrentProgress() + "%"); + + } + + private String getCycleUnit(int unit) { + String unitStr; + switch (unit) { + case 1: + unitStr = "天"; + break; + case 2: + unitStr = "周"; + break; + case 3: + unitStr = "月"; + break; + case 4: + unitStr = "年"; + break; + default: + unitStr = ""; + break; + } + return unitStr; + } + } + +} diff --git a/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/entity/CropsEntity.java b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/entity/CropsEntity.java new file mode 100644 index 0000000..4616b76 --- /dev/null +++ b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/entity/CropsEntity.java @@ -0,0 +1,53 @@ +package com.tairui.gov_affairs_cloud.ui.land.entity; + +import com.contrarywind.interfaces.IPickerViewData; +import com.google.gson.annotations.SerializedName; + +public class CropsEntity implements IPickerViewData { + + @SerializedName("cropsName") + private String cropsName; + @SerializedName("note") + private String note; + @SerializedName("id") + private String id; + @SerializedName("status") + private String status; + + public String getCropsName() { + return cropsName; + } + + public void setCropsName(String cropsName) { + this.cropsName = cropsName; + } + + public String getNote() { + return note; + } + + public void setNote(String note) { + this.note = note; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + @Override + public String getPickerViewText() { + return cropsName; + } +} diff --git a/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/entity/LandAreaRegionEntity.java b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/entity/LandAreaRegionEntity.java index 8686840..2164f9a 100644 --- a/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/entity/LandAreaRegionEntity.java +++ b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/entity/LandAreaRegionEntity.java @@ -1,11 +1,12 @@ package com.tairui.gov_affairs_cloud.ui.land.entity; +import java.io.Serializable; import java.util.List; import com.contrarywind.interfaces.IPickerViewData; import com.google.gson.annotations.SerializedName; -public class LandAreaRegionEntity implements IPickerViewData { +public class LandAreaRegionEntity implements IPickerViewData, Serializable { @SerializedName("id") private String id; diff --git a/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/entity/LandGridEntity.java b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/entity/LandGridEntity.java index 3e5e40d..8ed1374 100644 --- a/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/entity/LandGridEntity.java +++ b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/entity/LandGridEntity.java @@ -1,40 +1,31 @@ package com.tairui.gov_affairs_cloud.ui.land.entity; +import java.io.Serializable; import java.util.List; import com.contrarywind.interfaces.IPickerViewData; import com.google.gson.annotations.SerializedName; -public class LandGridEntity implements IPickerViewData { +public class LandGridEntity { - @SerializedName("regionCode") - private String regionCode; - @SerializedName("regionName") - private String regionName; @SerializedName("total") private Integer total; - @SerializedName("cooperativeCount") - private Integer cooperativeCount; - @SerializedName("farmerCount") - private Integer farmerCount; - @SerializedName("children") - private List children; - - public String getRegionCode() { - return regionCode; - } - - public void setRegionCode(String regionCode) { - this.regionCode = regionCode; - } - - public String getRegionName() { - return regionName; - } - - public void setRegionName(String regionName) { - this.regionName = regionName; - } + @SerializedName("size") + private Integer size; + @SerializedName("current") + private Integer current; + @SerializedName("optimizeCountSql") + private Boolean optimizeCountSql; + @SerializedName("searchCount") + private Boolean searchCount; + @SerializedName("maxLimit") + private Object maxLimit; + @SerializedName("countId") + private Object countId; + @SerializedName("pages") + private Integer pages; + @SerializedName("records") + private List records; public Integer getTotal() { return total; @@ -44,32 +35,145 @@ public class LandGridEntity implements IPickerViewData { this.total = total; } - public Integer getCooperativeCount() { - return cooperativeCount; + public Integer getSize() { + return size; } - public void setCooperativeCount(Integer cooperativeCount) { - this.cooperativeCount = cooperativeCount; + public void setSize(Integer size) { + this.size = size; } - public Integer getFarmerCount() { - return farmerCount; + public Integer getCurrent() { + return current; } - public void setFarmerCount(Integer farmerCount) { - this.farmerCount = farmerCount; + public void setCurrent(Integer current) { + this.current = current; } - public List getChildren() { - return children; + public Boolean isOptimizeCountSql() { + return optimizeCountSql; } - public void setChildren(List children) { - this.children = children; + public void setOptimizeCountSql(Boolean optimizeCountSql) { + this.optimizeCountSql = optimizeCountSql; } - @Override - public String getPickerViewText() { - return regionName; + public Boolean isSearchCount() { + return searchCount; + } + + public void setSearchCount(Boolean searchCount) { + this.searchCount = searchCount; + } + + public Object getMaxLimit() { + return maxLimit; + } + + public void setMaxLimit(Object maxLimit) { + this.maxLimit = maxLimit; + } + + public Object getCountId() { + return countId; + } + + public void setCountId(Object countId) { + this.countId = countId; + } + + public Integer getPages() { + return pages; + } + + public void setPages(Integer pages) { + this.pages = pages; + } + + public List getRecords() { + return records; + } + + public void setRecords(List records) { + this.records = records; + } + + public static class RecordsEntity implements IPickerViewData, Serializable { + @SerializedName("gridName") + private String gridName; + @SerializedName("gridAreaCode") + private String gridAreaCode; + @SerializedName("scope") + private Object scope; + @SerializedName("scopeImg") + private Object scopeImg; + @SerializedName("note") + private String note; + @SerializedName("id") + private String id; + @SerializedName("gridAreaName") + private String gridAreaName; + + public String getGridName() { + return gridName; + } + + public void setGridName(String gridName) { + this.gridName = gridName; + } + + public String getGridAreaCode() { + return gridAreaCode; + } + + public void setGridAreaCode(String gridAreaCode) { + this.gridAreaCode = gridAreaCode; + } + + public Object getScope() { + return scope; + } + + public void setScope(Object scope) { + this.scope = scope; + } + + public Object getScopeImg() { + return scopeImg; + } + + public void setScopeImg(Object scopeImg) { + this.scopeImg = scopeImg; + } + + public String getNote() { + return note; + } + + public void setNote(String note) { + this.note = note; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getGridAreaName() { + return gridAreaName; + } + + public void setGridAreaName(String gridAreaName) { + this.gridAreaName = gridAreaName; + } + + @Override + public String getPickerViewText() { + return gridName; + } } } diff --git a/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/entity/LandGridHomeEntity.java b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/entity/LandGridHomeEntity.java new file mode 100644 index 0000000..0885c6d --- /dev/null +++ b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/entity/LandGridHomeEntity.java @@ -0,0 +1,75 @@ +package com.tairui.gov_affairs_cloud.ui.land.entity; + +import java.util.List; + +import com.contrarywind.interfaces.IPickerViewData; +import com.google.gson.annotations.SerializedName; + +public class LandGridHomeEntity implements IPickerViewData { + + @SerializedName("regionCode") + private String regionCode; + @SerializedName("regionName") + private String regionName; + @SerializedName("total") + private Integer total; + @SerializedName("cooperativeCount") + private Integer cooperativeCount; + @SerializedName("farmerCount") + private Integer farmerCount; + @SerializedName("children") + private List children; + + public String getRegionCode() { + return regionCode; + } + + public void setRegionCode(String regionCode) { + this.regionCode = regionCode; + } + + public String getRegionName() { + return regionName; + } + + public void setRegionName(String regionName) { + this.regionName = regionName; + } + + public Integer getTotal() { + return total; + } + + public void setTotal(Integer total) { + this.total = total; + } + + public Integer getCooperativeCount() { + return cooperativeCount; + } + + public void setCooperativeCount(Integer cooperativeCount) { + this.cooperativeCount = cooperativeCount; + } + + public Integer getFarmerCount() { + return farmerCount; + } + + public void setFarmerCount(Integer farmerCount) { + this.farmerCount = farmerCount; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } + + @Override + public String getPickerViewText() { + return regionName; + } +} diff --git a/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/entity/PlantPlanDetailEntity.java b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/entity/PlantPlanDetailEntity.java new file mode 100644 index 0000000..f0b8378 --- /dev/null +++ b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/entity/PlantPlanDetailEntity.java @@ -0,0 +1,193 @@ +package com.tairui.gov_affairs_cloud.ui.land.entity; + +import com.google.gson.annotations.SerializedName; + +public class PlantPlanDetailEntity { + + @SerializedName("regionCode") + private String regionCode; + @SerializedName("regionName") + private String regionName; + @SerializedName("gridName") + private String gridName; + @SerializedName("id") + private String id; + @SerializedName("planName") + private String planName; + @SerializedName("cropsName") + private String cropsName; + @SerializedName("cropsImg") + private String cropsImg; + @SerializedName("planParameters") + private PlanParametersEntity planParameters; + @SerializedName("actualParameters") + private ActualParametersEntity actualParameters; + @SerializedName("currentProgress") + private Integer currentProgress; + + public String getRegionCode() { + return regionCode; + } + + public void setRegionCode(String regionCode) { + this.regionCode = regionCode; + } + + public String getRegionName() { + return regionName; + } + + public void setRegionName(String regionName) { + this.regionName = regionName; + } + + public String getGridName() { + return gridName; + } + + public void setGridName(String gridName) { + this.gridName = gridName; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getPlanName() { + return planName; + } + + public void setPlanName(String planName) { + this.planName = planName; + } + + public String getCropsName() { + return cropsName; + } + + public void setCropsName(String cropsName) { + this.cropsName = cropsName; + } + + public String getCropsImg() { + return cropsImg; + } + + public void setCropsImg(String cropsImg) { + this.cropsImg = cropsImg; + } + + public PlanParametersEntity getPlanParameters() { + return planParameters; + } + + public void setPlanParameters(PlanParametersEntity planParameters) { + this.planParameters = planParameters; + } + + public ActualParametersEntity getActualParameters() { + return actualParameters; + } + + public void setActualParameters(ActualParametersEntity actualParameters) { + this.actualParameters = actualParameters; + } + + public Integer getCurrentProgress() { + return currentProgress; + } + + public void setCurrentProgress(Integer currentProgress) { + this.currentProgress = currentProgress; + } + + public static class PlanParametersEntity { + @SerializedName("plantingArea") + private Double plantingArea; + @SerializedName("plantingMonths") + private String plantingMonths; + @SerializedName("growthCycle") + private String growthCycle; + @SerializedName("growthCycleUnit") + private String growthCycleUnit; + + public Double getPlantingArea() { + return plantingArea; + } + + public void setPlantingArea(Double plantingArea) { + this.plantingArea = plantingArea; + } + + public String getPlantingMonths() { + return plantingMonths; + } + + public void setPlantingMonths(String plantingMonths) { + this.plantingMonths = plantingMonths; + } + + public String getGrowthCycle() { + return growthCycle; + } + + public void setGrowthCycle(String growthCycle) { + this.growthCycle = growthCycle; + } + + public String getGrowthCycleUnit() { + return growthCycleUnit; + } + + public void setGrowthCycleUnit(String growthCycleUnit) { + this.growthCycleUnit = growthCycleUnit; + } + } + + public static class ActualParametersEntity { + @SerializedName("plantingArea") + private Double plantingArea; + @SerializedName("plantingMonths") + private String plantingMonths; + @SerializedName("growthCycle") + private String growthCycle; + @SerializedName("growthCycleUnit") + private String growthCycleUnit; + + public Double getPlantingArea() { + return plantingArea; + } + + public void setPlantingArea(Double plantingArea) { + this.plantingArea = plantingArea; + } + + public String getPlantingMonths() { + return plantingMonths; + } + + public void setPlantingMonths(String plantingMonths) { + this.plantingMonths = plantingMonths; + } + + public String getGrowthCycle() { + return growthCycle; + } + + public void setGrowthCycle(String growthCycle) { + this.growthCycle = growthCycle; + } + + public String getGrowthCycleUnit() { + return growthCycleUnit; + } + + public void setGrowthCycleUnit(String growthCycleUnit) { + this.growthCycleUnit = growthCycleUnit; + } + } +} diff --git a/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/entity/PlantPlanHomeEntity.java b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/entity/PlantPlanHomeEntity.java new file mode 100644 index 0000000..08f23af --- /dev/null +++ b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/entity/PlantPlanHomeEntity.java @@ -0,0 +1,108 @@ +package com.tairui.gov_affairs_cloud.ui.land.entity; + +import java.util.List; + +import com.google.gson.annotations.SerializedName; + +public class PlantPlanHomeEntity { + + @SerializedName("total") + private TotalEntity total; + @SerializedName("crops") + private List crops; + + public TotalEntity getTotal() { + return total; + } + + public void setTotal(TotalEntity total) { + this.total = total; + } + + public List getCrops() { + return crops; + } + + public void setCrops(List crops) { + this.crops = crops; + } + + public static class TotalEntity { + @SerializedName("totalPlanArea") + private Integer totalPlanArea; + @SerializedName("totalActualArea") + private Integer totalActualArea; + + public Integer getTotalPlanArea() { + return totalPlanArea; + } + + public void setTotalPlanArea(Integer totalPlanArea) { + this.totalPlanArea = totalPlanArea; + } + + public Integer getTotalActualArea() { + return totalActualArea; + } + + public void setTotalActualArea(Integer totalActualArea) { + this.totalActualArea = totalActualArea; + } + } + + public static class CropsEntity { + @SerializedName("cropsName") + private String cropsName; + @SerializedName("area") + private AreaEntity area; + @SerializedName("currentProgress") + private Integer currentProgress; + + public String getCropsName() { + return cropsName; + } + + public void setCropsName(String cropsName) { + this.cropsName = cropsName; + } + + public AreaEntity getArea() { + return area; + } + + public void setArea(AreaEntity area) { + this.area = area; + } + + public Integer getCurrentProgress() { + return currentProgress; + } + + public void setCurrentProgress(Integer currentProgress) { + this.currentProgress = currentProgress; + } + + public static class AreaEntity { + @SerializedName("totalPlanArea") + private Integer totalPlanArea; + @SerializedName("totalActualArea") + private Integer totalActualArea; + + public Integer getTotalPlanArea() { + return totalPlanArea; + } + + public void setTotalPlanArea(Integer totalPlanArea) { + this.totalPlanArea = totalPlanArea; + } + + public Integer getTotalActualArea() { + return totalActualArea; + } + + public void setTotalActualArea(Integer totalActualArea) { + this.totalActualArea = totalActualArea; + } + } + } +} diff --git a/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/entity/PlantPlanListEntity.java b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/entity/PlantPlanListEntity.java new file mode 100644 index 0000000..92c6a8d --- /dev/null +++ b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/land/entity/PlantPlanListEntity.java @@ -0,0 +1,342 @@ +package com.tairui.gov_affairs_cloud.ui.land.entity; + +import java.util.List; + +import com.google.gson.annotations.SerializedName; + +public class PlantPlanListEntity { + + @SerializedName("total") + private Integer total; + @SerializedName("size") + private Integer size; + @SerializedName("current") + private Integer current; + @SerializedName("optimizeCountSql") + private Boolean optimizeCountSql; + @SerializedName("searchCount") + private Boolean searchCount; + @SerializedName("maxLimit") + private Object maxLimit; + @SerializedName("countId") + private Object countId; + @SerializedName("pages") + private Integer pages; + @SerializedName("records") + private List records; + + public Integer getTotal() { + return total; + } + + public void setTotal(Integer total) { + this.total = total; + } + + public Integer getSize() { + return size; + } + + public void setSize(Integer size) { + this.size = size; + } + + public Integer getCurrent() { + return current; + } + + public void setCurrent(Integer current) { + this.current = current; + } + + public Boolean isOptimizeCountSql() { + return optimizeCountSql; + } + + public void setOptimizeCountSql(Boolean optimizeCountSql) { + this.optimizeCountSql = optimizeCountSql; + } + + public Boolean isSearchCount() { + return searchCount; + } + + public void setSearchCount(Boolean searchCount) { + this.searchCount = searchCount; + } + + public Object getMaxLimit() { + return maxLimit; + } + + public void setMaxLimit(Object maxLimit) { + this.maxLimit = maxLimit; + } + + public Object getCountId() { + return countId; + } + + public void setCountId(Object countId) { + this.countId = countId; + } + + public Integer getPages() { + return pages; + } + + public void setPages(Integer pages) { + this.pages = pages; + } + + public List getRecords() { + return records; + } + + public void setRecords(List records) { + this.records = records; + } + + public static class RecordsEntity { + @SerializedName("createTime") + private String createTime; + @SerializedName("createUser") + private String createUser; + @SerializedName("updateTime") + private Object updateTime; + @SerializedName("updateUser") + private Object updateUser; + @SerializedName("tenantId") + private Integer tenantId; + @SerializedName("id") + private String id; + @SerializedName("year") + private String year; + @SerializedName("regionCode") + private String regionCode; + @SerializedName("cropsId") + private Long cropsId; + @SerializedName("gridId") + private String gridId; + @SerializedName("planName") + private String planName; + @SerializedName("plantingArea") + private Double plantingArea; + @SerializedName("plantingAreaActual") + private Object plantingAreaActual; + @SerializedName("plantingMonths") + private String plantingMonths; + @SerializedName("growthCycle") + private String growthCycle; + @SerializedName("growthCycleUnit") + private String growthCycleUnit; + @SerializedName("note") + private String note; + @SerializedName("deleteFlag") + private String deleteFlag; + @SerializedName("cropsName") + private String cropsName; + @SerializedName("cropsImg") + private String cropsImg; + @SerializedName("gridName") + private String gridName; + @SerializedName("regionName") + private String regionName; + @SerializedName("currentProgress") + private Integer currentProgress; + @SerializedName("actualFlag") + private String actualFlag; + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public String getCreateUser() { + return createUser; + } + + public void setCreateUser(String createUser) { + this.createUser = createUser; + } + + public Object getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Object updateTime) { + this.updateTime = updateTime; + } + + public Object getUpdateUser() { + return updateUser; + } + + public void setUpdateUser(Object updateUser) { + this.updateUser = updateUser; + } + + public Integer getTenantId() { + return tenantId; + } + + public void setTenantId(Integer tenantId) { + this.tenantId = tenantId; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getYear() { + return year; + } + + public void setYear(String year) { + this.year = year; + } + + public String getRegionCode() { + return regionCode; + } + + public void setRegionCode(String regionCode) { + this.regionCode = regionCode; + } + + public Long getCropsId() { + return cropsId; + } + + public void setCropsId(Long cropsId) { + this.cropsId = cropsId; + } + + public String getGridId() { + return gridId; + } + + public void setGridId(String gridId) { + this.gridId = gridId; + } + + public String getPlanName() { + return planName; + } + + public void setPlanName(String planName) { + this.planName = planName; + } + + public Double getPlantingArea() { + return plantingArea; + } + + public void setPlantingArea(Double plantingArea) { + this.plantingArea = plantingArea; + } + + public Object getPlantingAreaActual() { + return plantingAreaActual; + } + + public void setPlantingAreaActual(Object plantingAreaActual) { + this.plantingAreaActual = plantingAreaActual; + } + + public String getPlantingMonths() { + return plantingMonths; + } + + public void setPlantingMonths(String plantingMonths) { + this.plantingMonths = plantingMonths; + } + + public String getGrowthCycle() { + return growthCycle; + } + + public void setGrowthCycle(String growthCycle) { + this.growthCycle = growthCycle; + } + + public String getGrowthCycleUnit() { + return growthCycleUnit; + } + + public void setGrowthCycleUnit(String growthCycleUnit) { + this.growthCycleUnit = growthCycleUnit; + } + + public String getNote() { + return note; + } + + public void setNote(String note) { + this.note = note; + } + + public String getDeleteFlag() { + return deleteFlag; + } + + public void setDeleteFlag(String deleteFlag) { + this.deleteFlag = deleteFlag; + } + + public String getCropsName() { + return cropsName; + } + + public void setCropsName(String cropsName) { + this.cropsName = cropsName; + } + + public String getCropsImg() { + return cropsImg; + } + + public void setCropsImg(String cropsImg) { + this.cropsImg = cropsImg; + } + + public String getGridName() { + return gridName; + } + + public void setGridName(String gridName) { + this.gridName = gridName; + } + + public String getRegionName() { + return regionName; + } + + public void setRegionName(String regionName) { + this.regionName = regionName; + } + + public Integer getCurrentProgress() { + return currentProgress; + } + + public void setCurrentProgress(Integer currentProgress) { + this.currentProgress = currentProgress; + } + + public String getActualFlag() { + return actualFlag; + } + + public void setActualFlag(String actualFlag) { + this.actualFlag = actualFlag; + } + } +} diff --git a/app/src/main/java/com/tairui/gov_affairs_cloud/ui/login/LoginActivity.java b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/login/LoginActivity.java index 3ea648f..7e1cd61 100644 --- a/app/src/main/java/com/tairui/gov_affairs_cloud/ui/login/LoginActivity.java +++ b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/login/LoginActivity.java @@ -27,9 +27,9 @@ public class LoginActivity extends BaseActivity { @Override protected void onFindView(Bundle savedInstanceState) { // GlideLoader.loadOriginal(binding.ivVerifyCode, Api.CAPTCHA, -1); - if (Hawk.contains(Constant.LOGIN_USER_NAME)) { - setText(binding.inputUserName, Hawk.get(Constant.LOGIN_USER_NAME)); - } + // if (Hawk.contains(Constant.LOGIN_USER_NAME)) { + // setText(binding.inputUserName, Hawk.get(Constant.LOGIN_USER_NAME)); + // } } @Override @@ -50,7 +50,7 @@ public class LoginActivity extends BaseActivity { @Override protected void onApplyData() { -// getCaptcha(); + // getCaptcha(); } private void getCaptcha() { diff --git a/app/src/main/java/com/tairui/gov_affairs_cloud/ui/workspace/WorkSpaceFragment.java b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/workspace/WorkSpaceFragment.java index 1e83143..3f41ea8 100644 --- a/app/src/main/java/com/tairui/gov_affairs_cloud/ui/workspace/WorkSpaceFragment.java +++ b/app/src/main/java/com/tairui/gov_affairs_cloud/ui/workspace/WorkSpaceFragment.java @@ -8,9 +8,10 @@ import com.chad.library.adapter.base.BaseViewHolder; import com.tairui.gov_affairs_cloud.R; import com.tairui.gov_affairs_cloud.base.BaseFragment; import com.tairui.gov_affairs_cloud.databinding.FragmentWorkspaceBinding; -import com.tairui.gov_affairs_cloud.ui.grid.GridInfoActivity; +import com.tairui.gov_affairs_cloud.ui.land.GridInfoActivity; import com.tairui.gov_affairs_cloud.ui.land.LandActivity; import com.tairui.gov_affairs_cloud.ui.land.LandResouceInfoActivity; +import com.tairui.gov_affairs_cloud.ui.land.PlantPlanActivity; import com.tairui.gov_affairs_cloud.ui.workspace.entity.WorkSpaceEntity; import com.tairui.gov_affairs_cloud.util.IntentUtil; @@ -62,9 +63,11 @@ public class WorkSpaceFragment extends BaseFragment { int _id = itemEntity.getId(); if (_id == 1) { IntentUtil.startActivity(mContext, LandActivity.class); - }else if (_id == 2) { + } else if (_id == 2) { IntentUtil.startActivity(mContext, GridInfoActivity.class); - }else if (_id == 6) { + } else if (_id == 3) { + IntentUtil.startActivity(mContext, PlantPlanActivity.class); + } else if (_id == 6) { IntentUtil.startActivity(mContext, LandResouceInfoActivity.class); } }); @@ -107,11 +110,11 @@ public class WorkSpaceFragment extends BaseFragment { itemFunc.setIcon(R.mipmap.ic_workspace_func_3); funcs.add(itemFunc); -// itemFunc = new WorkSpaceEntity.FuncsEntity(); -// itemFunc.setId(4); -// itemFunc.setName("农事作业"); -// itemFunc.setIcon(R.mipmap.ic_workspace_func_4); -// funcs.add(itemFunc); + // itemFunc = new WorkSpaceEntity.FuncsEntity(); + // itemFunc.setId(4); + // itemFunc.setName("农事作业"); + // itemFunc.setIcon(R.mipmap.ic_workspace_func_4); + // funcs.add(itemFunc); itemFunc = new WorkSpaceEntity.FuncsEntity(); itemFunc.setId(5); diff --git a/app/src/main/java/com/tairui/gov_affairs_cloud/util/DateUtils.java b/app/src/main/java/com/tairui/gov_affairs_cloud/util/DateUtils.java new file mode 100644 index 0000000..cf60190 --- /dev/null +++ b/app/src/main/java/com/tairui/gov_affairs_cloud/util/DateUtils.java @@ -0,0 +1,12 @@ +package com.tairui.gov_affairs_cloud.util; + +import java.util.Calendar; + +public class DateUtils { + + public static int getCurrentYear() { + Calendar calendar = Calendar.getInstance(); + return calendar.get(Calendar.YEAR); + } +} + diff --git a/app/src/main/res/drawable/bg_container_blue_raduis_2.xml b/app/src/main/res/drawable/bg_container_blue_raduis_2.xml new file mode 100644 index 0000000..b8ac68d --- /dev/null +++ b/app/src/main/res/drawable/bg_container_blue_raduis_2.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_container_green_raduis_2.xml b/app/src/main/res/drawable/bg_container_green_raduis_2.xml new file mode 100644 index 0000000..5dbfd6f --- /dev/null +++ b/app/src/main/res/drawable/bg_container_green_raduis_2.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_container_white_raduis_2_blue_border.xml b/app/src/main/res/drawable/bg_container_white_raduis_2_blue_border.xml new file mode 100644 index 0000000..38e9a5b --- /dev/null +++ b/app/src/main/res/drawable/bg_container_white_raduis_2_blue_border.xml @@ -0,0 +1,14 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_container_white_raduis_2_green_border.xml b/app/src/main/res/drawable/bg_container_white_raduis_2_green_border.xml new file mode 100644 index 0000000..d841361 --- /dev/null +++ b/app/src/main/res/drawable/bg_container_white_raduis_2_green_border.xml @@ -0,0 +1,14 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_gradient_plant_plan.xml b/app/src/main/res/drawable/bg_gradient_plant_plan.xml new file mode 100644 index 0000000..f5c0984 --- /dev/null +++ b/app/src/main/res/drawable/bg_gradient_plant_plan.xml @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_gradient_plant_real.xml b/app/src/main/res/drawable/bg_gradient_plant_real.xml new file mode 100644 index 0000000..2bf21ad --- /dev/null +++ b/app/src/main/res/drawable/bg_gradient_plant_real.xml @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/custom_progressbar_blue.xml b/app/src/main/res/drawable/custom_progressbar_blue.xml new file mode 100644 index 0000000..767bde4 --- /dev/null +++ b/app/src/main/res/drawable/custom_progressbar_blue.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/custom_progressbar_green.xml b/app/src/main/res/drawable/custom_progressbar_green.xml new file mode 100644 index 0000000..a7458f1 --- /dev/null +++ b/app/src/main/res/drawable/custom_progressbar_green.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_add_plant_plan.xml b/app/src/main/res/layout/activity_add_plant_plan.xml new file mode 100644 index 0000000..312089b --- /dev/null +++ b/app/src/main/res/layout/activity_add_plant_plan.xml @@ -0,0 +1,317 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 63df16b..7389c2b 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -36,6 +36,7 @@ android:id="@+id/inputUserName" android:layout_width="match_parent" android:layout_height="wrap_content" + android:text="admin" android:background="@color/transparent" android:hint="请输入账号" android:textColor="@color/color_txt_black" @@ -68,6 +69,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/transparent" + android:text="admin123" android:hint="请输入密码" android:inputType="textPassword" android:textColor="@color/color_txt_black" diff --git a/app/src/main/res/layout/activity_plant_plan.xml b/app/src/main/res/layout/activity_plant_plan.xml new file mode 100644 index 0000000..946bccd --- /dev/null +++ b/app/src/main/res/layout/activity_plant_plan.xml @@ -0,0 +1,420 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_plant_plan_detail.xml b/app/src/main/res/layout/activity_plant_plan_detail.xml new file mode 100644 index 0000000..21a1b38 --- /dev/null +++ b/app/src/main/res/layout/activity_plant_plan_detail.xml @@ -0,0 +1,408 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_plant_plan_list.xml b/app/src/main/res/layout/activity_plant_plan_list.xml new file mode 100644 index 0000000..35f865b --- /dev/null +++ b/app/src/main/res/layout/activity_plant_plan_list.xml @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_plant_plan.xml b/app/src/main/res/layout/item_plant_plan.xml new file mode 100644 index 0000000..8f92a98 --- /dev/null +++ b/app/src/main/res/layout/item_plant_plan.xml @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/mipmap-xxhdpi/bg_plant_plan_area.png b/app/src/main/res/mipmap-xxhdpi/bg_plant_plan_area.png new file mode 100644 index 0000000..1f8b145 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/bg_plant_plan_area.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/bg_plant_real_area.png b/app/src/main/res/mipmap-xxhdpi/bg_plant_real_area.png new file mode 100644 index 0000000..b6230d0 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/bg_plant_real_area.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_arrow_right_gray.png b/app/src/main/res/mipmap-xxhdpi/ic_arrow_right_gray.png new file mode 100644 index 0000000..2c970ed Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_arrow_right_gray.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_dot_green.png b/app/src/main/res/mipmap-xxhdpi/ic_dot_green.png new file mode 100644 index 0000000..967ffdb Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_dot_green.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_plant_plan_area.png b/app/src/main/res/mipmap-xxhdpi/ic_plant_plan_area.png new file mode 100644 index 0000000..4d0466a Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_plant_plan_area.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_plant_real_area.png b/app/src/main/res/mipmap-xxhdpi/ic_plant_real_area.png new file mode 100644 index 0000000..cff2439 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_plant_real_area.png differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index dd804ed..6e695b0 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -10,6 +10,7 @@ #666666 #444444 #FF4E3D + #2AD848 #999999 #00000000 #F8F8F8 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 89ef2ea..28ef329 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ - 政务监管平台 + 产业政务平台 当前无网络,请检查你的网络设置 服务器无响应,请稍后重试! diff --git a/settings.gradle b/settings.gradle index 3c6f80d..70f1472 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,5 @@ include ':app' include ':AlbumDialog' +include ':DialogX' +include ':DialogXInterface' +include ':DialogXMaterialYou'