├── .gitattributes ├── .gitignore ├── EasyDialog.iml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── kale │ │ └── easydialog │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── kale │ │ └── easydialog │ │ ├── App.java │ │ ├── BottomDialog.java │ │ ├── DemoDialog.java │ │ ├── ImageDialog.java │ │ ├── InputDialog.java │ │ ├── MainActivity.java │ │ ├── MyBuilderDialog.java │ │ ├── MyStyleActivity.java │ │ └── TopDialog.java │ └── res │ ├── anim │ ├── dialog_enter.xml │ └── dialog_out.xml │ ├── drawable │ ├── bg_mlls.jpg │ ├── button_checked_selector.xml │ ├── dialog_bg_custom.xml │ ├── dialog_bg_main.xml │ ├── dialog_button_divider.xml │ ├── divider.xml │ ├── icon_checked.png │ ├── icon_unchecked.png │ ├── kale.png │ ├── nintendoswitch.png │ └── saber.png │ ├── font │ ├── aasudaqishui.ttf │ └── sudaqishui.xml │ ├── layout │ ├── activity_main.xml │ ├── content_bottom_sheet.xml │ ├── custom_dialog_image_layout.xml │ ├── custom_dialog_input_layout.xml │ ├── custom_dialog_layout.xml │ ├── custom_dialog_title_layout.xml │ ├── dialog_bottom_button_bar.xml │ ├── dialog_list_item.xml │ ├── dialog_list_layout.xml │ ├── dialog_main_frame.xml │ ├── dialog_multi_choice_item.xml │ ├── dialog_single_choice_item.xml │ └── dialog_top_bar_layout.xml │ ├── menu │ └── menu_main.xml │ ├── mipmap-xhdpi │ └── ic_launcher.png │ └── values │ ├── colors.xml │ ├── dialog_style.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── demo ├── circular.png ├── custom.png ├── multiChoice.png ├── progress.png ├── simple.png └── singleChoice.png ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── lib ├── .gitignore ├── build.gradle └── src │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ ├── android │ │ └── support │ │ │ └── v7 │ │ │ └── app │ │ │ └── _Kale_EasyDialog_AlertDialog.java │ │ └── kale │ │ └── ui │ │ └── view │ │ └── dialog │ │ ├── BaseCustomDialog.java │ │ ├── BaseEasyDialog.java │ │ ├── DialogParams.java │ │ ├── EasyDialog.java │ │ └── EasyDialogListeners.java │ └── test │ └── java │ ├── android │ └── app │ │ └── Activity.java │ └── kale │ └── ui │ └── view │ └── dialog │ ├── ReplaceActivityByValueTest.java │ ├── ReplaceValueByActivityTest.java │ └── TestModel.java ├── provided ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── android │ └── support │ └── v7 │ └── app │ └── AlertController.java └── settings.gradle /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | .DS_Store 6 | /build 7 | /captures 8 | *.iml 9 | .idea/ 10 | -------------------------------------------------------------------------------- /EasyDialog.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EasyDialog 2 | [![](https://jitpack.io/v/tianzhijiexian/EasyDialog.svg)](https://jitpack.io/#tianzhijiexian/EasyDialog) 3 | 4 | 一个DialogFragment的封装库,提供了builder的方式进行调用,因为采用了alertDialog.Builder,所以代码中没有任何自定义,轻量稳定。 5 | 6 | ### 简介 7 | 8 | 原生的Dialog提供了很多Style来让开发者进行自定义,可以满足我们百分之九十的业务需求了。这其实是Android的设计思想,官方“一般”都会把属性值暴露出来,让显示和逻辑分开。因此,本项目并没有重新实现Dialog,而是通过封装了DialogFragment来让大家使用和定制Dialog更加的方便。 9 | 10 | 这里顺便贴一下dialogFragment的方法调用流程: 11 | 12 | ![](http://static.zybuluo.com/shark0017/tm558z1tp99o3tfet1ij9jjd/image_1bk8b5uh6ikp3the4k1iv8ote13.png) 13 | 14 | ### 添加依赖 15 | 16 | 1.在项目外层的build.gradle中添加JitPack仓库 17 | 18 | ``` 19 | repositories { 20 | maven { 21 | url "https://jitpack.io" 22 | } 23 | } 24 | ``` 25 | 26 | 2.在用到的项目中添加依赖 27 | 28 | > compile 'com.github.tianzhijiexian:EasyDialog:[Latest release](https://github.com/tianzhijiexian/EasyDialog/releases)(<-click it)' 29 | 30 | 31 | **举例:** 32 | > compile 'com.github.tianzhijiexian:EasyDialog:1.1.2' 33 | 34 | ### 使用方式 35 | 36 | EasyDialog充分利用了原生alertDialog.Builder的api,所以使用方式和alertDialog无异,它提供了如下五种基本的dialog。 37 | 38 | **1. 基础对话框** 39 | 40 | ![](./demo/simple.png) 41 | 42 | ```JAVA 43 | EasyDialog.Builder builder = EasyDialog.builder(this); // 建立builder对象 44 | 45 | builder.setTitle("Title") 46 | .setIcon(R.drawable.saber) 47 | .setMessage(R.string.hello_world) 48 | 49 | .setOnCancelListener(dialog -> Log.d(TAG, "onCancel")) 50 | .setOnDismissListener(dialog -> Log.d(TAG, "onDismiss")) 51 | 52 | // 设置下方的三个按钮 53 | .setPositiveButton("ok", (dialog, which) -> {}) 54 | .setNegativeButton("cancel", (dialog, which) -> dialog.dismiss()) 55 | .setNeutralButton("ignore", null) 56 | 57 | .setCancelable(true); // 点击空白处可以关闭 58 | 59 | DialogFragment easyDialog = builder.build(); 60 | 61 | // 用showAllowingStateLoss()弹出 62 | easyDialog.showAllowingStateLoss(getSupportFragmentManager()); 63 | ``` 64 | 65 | **2. 单选对话框** 66 | 67 | ![](./demo/singleChoice.png) 68 | 69 | ```JAVA 70 | EasyDialog dialog = EasyDialog.builder(this) 71 | .setTitle("Single Choice Dialog") 72 | 73 | // 这里传入的“1”表示默认选择第二个选项 74 | .setSingleChoiceItems(new String[]{"Android", "ios", "wp"}, 1, 75 | (d, position) -> {d.dismiss();}) 76 | .setPositiveButton("ok", null) 77 | .build(); 78 | 79 | dialog.show(getSupportFragmentManager(), TAG); 80 | ``` 81 | 82 | **3. 多选对话框** 83 | 84 | ![](./demo/multiChoice.png) 85 | 86 | ```JAVA 87 | EasyDialog.builder(this) 88 | // 设置数据和默认选中的选项 89 | .setMultiChoiceItems( 90 | new String[]{"Android", "ios", "wp"}, new boolean[]{true, false, true}, 91 | (dialog, which, isChecked) -> showToast("onClick pos = " + which)) 92 | .build() 93 | .show(getSupportFragmentManager()); 94 | ``` 95 | 96 | **4. 简单列表对话框** 97 | 98 | ![](./demo/array.png) 99 | 100 | ```xml 101 | 102 | 阿尔及利亚 103 | 安哥拉 104 | 贝宁 105 | 缅甸 106 | 107 | ``` 108 | 109 | ```java 110 | EasyDialog.builder(this) 111 | // R.array.country为xml中定义的string数组 112 | .setItems(R.array.country, (dialog, which) -> showToast("click " + which)) 113 | .setPositiveButton("yes", null) 114 | .setNegativeButton("no", null) 115 | .build() 116 | .show(getSupportFragmentManager()); 117 | ``` 118 | 119 | ### 自定义对话框 120 | 121 | 自定义对话框需要继承自`BaseCustomDialog`。如果需要传入更多的参数,还需要继承自`EasyDialog.Builder`来建立自己的builder。 122 | 123 | ```JAVA 124 | public class MyBuilderDialog extends BaseCustomDialog { 125 | 126 | public static final String KEY_AGE = "KEY_AGE", KEY_NAME = "KEY_NAME"; 127 | 128 | /** 129 | * 继承自{@link EasyDialog.Builder}以扩展builder 130 | */ 131 | public static class Builder extends BaseEasyDialog.Builder { 132 | 133 | private Bundle bundle = new Bundle(); 134 | 135 | public Builder(@NonNull Context context) { 136 | super(context); 137 | } 138 | 139 | public Builder setAge(int age) { 140 | bundle.putInt(KEY_AGE, age); 141 | return this; 142 | } 143 | 144 | public Builder setName(String name) { 145 | bundle.putString(KEY_NAME, name); 146 | return this; 147 | } 148 | 149 | @NonNull 150 | @Override 151 | protected EasyDialog createDialog() { 152 | MyBuilderDialog dialog = new MyBuilderDialog(); 153 | dialog.setArguments(bundle); // 增加自己的bundle 154 | return dialog; 155 | } 156 | } 157 | 158 | @Override 159 | protected int getLayoutResId() { 160 | return 0; 161 | } 162 | 163 | @Override 164 | protected void bindViews(View root) { 165 | 166 | } 167 | 168 | @Override 169 | protected void modifyAlertDialogBuilder(AlertDialog.Builder builder) { 170 | super.modifyAlertDialogBuilder(builder); 171 | Bundle arguments = getArguments(); 172 | 173 | String name = arguments.getString(KEY_NAME); 174 | int age = arguments.getInt(KEY_AGE); 175 | 176 | String str = "name: " + name + ", age: " + age; 177 | 178 | // 修改builder对象 179 | builder.setMessage("修改后的message是:\n\n" + str); 180 | } 181 | 182 | @Override 183 | protected void setViews() { 184 | int age = getArguments().getInt(KEY_AGE); 185 | Toast.makeText(getContext(), "age: " + age, Toast.LENGTH_SHORT).show(); 186 | } 187 | 188 | } 189 | ``` 190 | 191 | ### 自定义样式 192 | 193 | **在主题中设置默认样式(如果你想用原生的样式,可以跳过这个步骤)** 194 | 195 | ```XML 196 | 197 | 198 | 203 | 204 | 205 | ``` 206 | 207 | ```XML 208 | 209 | 210 | 211 | 279 | 280 | 284 | 285 | 286 | 301 | 302 | 303 | 307 | 308 | 311 | 312 | 313 | ``` 314 | 315 | ### 开发者 316 | ![](https://avatars3.githubusercontent.com/u/9552155?v=3&s=460) 317 | 318 | Jack Tony: 319 | 320 | ### License 321 | 322 | Copyright 2016-2019 Jack Tony 323 | 324 | Licensed under the Apache License, Version 2.0 (the "License"); 325 | you may not use this file except in compliance with the License. 326 | You may obtain a copy of the License at 327 | 328 | http://www.apache.org/licenses/LICENSE-2.0 329 | 330 | Unless required by applicable law or agreed to in writing, software 331 | distributed under the License is distributed on an "AS IS" BASIS, 332 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 333 | See the License for the specific language governing permissions and 334 | limitations under the License. 335 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 27 5 | buildToolsVersion '27.0.3' 6 | 7 | defaultConfig { 8 | applicationId "kale.easydialog" 9 | minSdkVersion 17 10 | targetSdkVersion 27 11 | versionCode 2 12 | versionName "1.0.1" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | compileOptions { 21 | sourceCompatibility JavaVersion.VERSION_1_8 22 | targetCompatibility JavaVersion.VERSION_1_8 23 | } 24 | } 25 | 26 | dependencies { 27 | implementation fileTree(include: ['*.jar'], dir: 'libs') 28 | implementation 'com.android.support:appcompat-v7:27.1.1' 29 | implementation 'com.android.support:design:27.1.1' 30 | implementation 'com.android.support:cardview-v7:27.1.1' 31 | implementation project(':lib') 32 | } 33 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in H:\Android\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/kale/easydialog/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package kale.easydialog; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | 11 | public ApplicationTest() { 12 | super(Application.class); 13 | } 14 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 13 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/java/kale/easydialog/App.java: -------------------------------------------------------------------------------- 1 | package kale.easydialog; 2 | 3 | import android.app.Activity; 4 | import android.app.Application; 5 | import android.os.Bundle; 6 | import android.support.v7.app.AppCompatActivity; 7 | 8 | import kale.ui.view.dialog.EasyDialog; 9 | 10 | /** 11 | * @author Kale 12 | * @date 2018/8/17 13 | */ 14 | public class App extends Application { 15 | 16 | private AppCompatActivity curActivity; 17 | 18 | @Override 19 | public void onCreate() { 20 | super.onCreate(); 21 | registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { 22 | @Override 23 | public void onActivityCreated(Activity activity, Bundle savedInstanceState) { 24 | 25 | } 26 | 27 | @Override 28 | public void onActivityStarted(Activity activity) { 29 | curActivity = (AppCompatActivity) activity; 30 | } 31 | 32 | @Override 33 | public void onActivityResumed(Activity activity) { 34 | 35 | } 36 | 37 | @Override 38 | public void onActivityPaused(Activity activity) { 39 | 40 | } 41 | 42 | @Override 43 | public void onActivityStopped(Activity activity) { 44 | curActivity = null; 45 | } 46 | 47 | @Override 48 | public void onActivitySaveInstanceState(Activity activity, Bundle outState) { 49 | 50 | } 51 | 52 | @Override 53 | public void onActivityDestroyed(Activity activity) { 54 | 55 | } 56 | }); 57 | } 58 | 59 | public AppCompatActivity getCurActivity() { 60 | return curActivity; 61 | } 62 | 63 | public void showDialog(String title, String message) { 64 | if (curActivity == null) { 65 | return; 66 | } 67 | 68 | EasyDialog.builder(curActivity) 69 | .setTitle(title) 70 | .setMessage(message) 71 | .setPositiveButton("ok", null) 72 | .build() 73 | .show(curActivity.getSupportFragmentManager()); 74 | } 75 | } -------------------------------------------------------------------------------- /app/src/main/java/kale/easydialog/BottomDialog.java: -------------------------------------------------------------------------------- 1 | package kale.easydialog; 2 | 3 | import android.support.design.widget.BottomSheetBehavior; 4 | import android.view.View; 5 | import android.widget.FrameLayout; 6 | import android.widget.TextView; 7 | 8 | import kale.ui.view.dialog.BaseCustomDialog; 9 | 10 | /** 11 | * @author Kale 12 | * @date 2017/6/26 13 | */ 14 | public class BottomDialog extends BaseCustomDialog { 15 | 16 | private BottomSheetBehavior mBehavior; 17 | 18 | @Override 19 | protected int getLayoutResId() { 20 | return R.layout.custom_dialog_layout; 21 | } 22 | 23 | @Override 24 | protected void bindViews(View root) { 25 | FrameLayout bottomSheet = root.findViewById(android.support.design.R.id.design_bottom_sheet); 26 | mBehavior = BottomSheetBehavior.from(bottomSheet); 27 | } 28 | 29 | @Override 30 | protected void setViews() { 31 | TextView textView = findView(R.id.message_tv); 32 | 33 | textView.setOnClickListener(view -> { 34 | // getDialog().cancel(); // 如果要触发cancel,必须手动触发 35 | dismiss(); 36 | }); 37 | textView.setText(getDialogParams().message); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/kale/easydialog/DemoDialog.java: -------------------------------------------------------------------------------- 1 | package kale.easydialog; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.NonNull; 5 | import android.view.View; 6 | 7 | import kale.ui.view.dialog.BaseCustomDialog; 8 | 9 | /** 10 | * @author Kale 11 | * @date 2018/8/15 12 | */ 13 | public class DemoDialog extends BaseCustomDialog { 14 | 15 | @Override 16 | protected int getLayoutResId() { 17 | return 0; 18 | } 19 | 20 | @Override 21 | protected void bindViews(View root) { 22 | 23 | } 24 | 25 | @Override 26 | protected void setViews() { 27 | 28 | } 29 | 30 | @Override 31 | public void onSaveInstanceState(Bundle outState) { 32 | super.onSaveInstanceState(outState); 33 | } 34 | 35 | @Override 36 | protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { 37 | super.onRestoreInstanceState(savedInstanceState); 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /app/src/main/java/kale/easydialog/ImageDialog.java: -------------------------------------------------------------------------------- 1 | package kale.easydialog; 2 | 3 | import android.content.DialogInterface; 4 | import android.support.v7.app.AlertDialog; 5 | import android.view.View; 6 | import android.widget.Button; 7 | 8 | import kale.ui.view.dialog.BaseCustomDialog; 9 | import kale.ui.view.dialog.EasyDialog; 10 | 11 | /** 12 | * @author Kale 13 | * @date 2018/8/13 14 | */ 15 | public class ImageDialog extends BaseCustomDialog { 16 | 17 | @Override 18 | protected int getLayoutResId() { 19 | return R.layout.custom_dialog_image_layout; 20 | } 21 | 22 | @Override 23 | protected void modifyAlertDialogBuilder(AlertDialog.Builder builder) { 24 | super.modifyAlertDialogBuilder(builder); 25 | builder.setPositiveButton(null, null); 26 | } 27 | 28 | @Override 29 | protected void bindViews(View root) { 30 | Button button = root.findViewById(R.id.button); 31 | button.setText(getDialogParams().positiveText); 32 | button.setOnClickListener(v -> { 33 | // 手动调用外层回调 34 | getPositiveListener().onClick(getDialog(), DialogInterface.BUTTON_POSITIVE); 35 | // 关闭对话框 36 | dismiss(); 37 | // 展示新的一个对话框 38 | showDialog(); 39 | }); 40 | } 41 | 42 | private void showDialog(){ 43 | EasyDialog.Builder builder = EasyDialog.builder(getActivity(), R.style.Theme_Dialog_Alert_Kale); 44 | 45 | builder.setTitle("Dynamic Style Dialog") 46 | .setIcon(R.drawable.kale) 47 | .setMessage("上半部分是透明背景的样式") 48 | .build() 49 | .show(getFragmentManager()); 50 | } 51 | 52 | @Override 53 | protected void setViews() { 54 | 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/kale/easydialog/InputDialog.java: -------------------------------------------------------------------------------- 1 | package kale.easydialog; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | 5 | import android.content.Context; 6 | import android.graphics.Bitmap; 7 | import android.graphics.BitmapFactory; 8 | import android.os.Bundle; 9 | import android.support.annotation.NonNull; 10 | import android.support.annotation.Nullable; 11 | import android.support.v7.app.AlertDialog; 12 | import android.support.v7.app.AppCompatDelegate; 13 | import android.text.TextUtils; 14 | import android.view.View; 15 | import android.view.inputmethod.InputMethodManager; 16 | import android.widget.Button; 17 | import android.widget.EditText; 18 | import android.widget.ImageView; 19 | import android.widget.LinearLayout; 20 | import android.widget.TextView; 21 | import android.widget.Toast; 22 | 23 | import kale.ui.view.dialog.BaseCustomDialog; 24 | import kale.ui.view.dialog.BaseEasyDialog; 25 | 26 | /** 27 | * @author Jack Tony 28 | * @date 2015/8/27 29 | */ 30 | public class InputDialog extends BaseCustomDialog { 31 | 32 | private static final String KEY_INPUT_TEXT = "key_input_text"; 33 | 34 | private static final String KEY_INPUT_HINT = "key_input_hint"; 35 | 36 | private static final String KEY_IMAGE_BITMAP = "key_image_bitmap"; 37 | 38 | private Bitmap mBitmap; 39 | 40 | private CharSequence mInputText; 41 | 42 | private CharSequence mInputHint; 43 | 44 | private TextView mTitleTv; 45 | 46 | private EditText mInputTextEt; 47 | 48 | /** 49 | * 自定义builder来增加一些参数,记得要继承自BaseEasyDialog.Builder 50 | */ 51 | public static class Builder extends BaseEasyDialog.Builder { 52 | 53 | private Bundle bundle = new Bundle(); 54 | 55 | public Builder(@NonNull Context context) { 56 | super(context); 57 | } 58 | 59 | public Builder setImageBitmap(Bitmap bitmap) { 60 | bundle.putByteArray(KEY_IMAGE_BITMAP, bitmap2ByteArr(bitmap)); 61 | return this; 62 | } 63 | 64 | public Builder setInputText(CharSequence text, CharSequence hint) { 65 | bundle.putCharSequence(KEY_INPUT_TEXT, text); 66 | bundle.putCharSequence(KEY_INPUT_HINT, hint); 67 | return this; 68 | } 69 | 70 | @NonNull 71 | @Override 72 | protected InputDialog createDialog() { 73 | InputDialog dialog = new InputDialog(); 74 | dialog.setArguments(bundle); 75 | return dialog; 76 | } 77 | 78 | } 79 | 80 | @Override 81 | public void onCreate(@Nullable Bundle savedInstanceState) { 82 | super.onCreate(savedInstanceState); 83 | Bundle arguments = getArguments(); 84 | 85 | byte[] mBitmapByteArr = null; 86 | if (arguments != null) { 87 | mBitmapByteArr = arguments.getByteArray(KEY_IMAGE_BITMAP); 88 | mInputText = arguments.getCharSequence(KEY_INPUT_TEXT); 89 | mInputHint = arguments.getCharSequence(KEY_INPUT_HINT); 90 | } 91 | if (mBitmapByteArr != null) { 92 | mBitmap = BitmapFactory.decodeByteArray(mBitmapByteArr, 0, mBitmapByteArr.length); 93 | } 94 | } 95 | 96 | @Override 97 | protected int getLayoutResId() { 98 | return R.layout.custom_dialog_input_layout; 99 | } 100 | 101 | @Override 102 | protected void bindViews(View root) { 103 | mTitleTv = findView(R.id.msg_tv); 104 | mInputTextEt = findView(R.id.input_et); 105 | } 106 | 107 | @Override 108 | public void setViews() { 109 | if (mBitmap != null) { 110 | LinearLayout imageTextLl = findView(R.id.root_ll); 111 | imageTextLl.setVisibility(View.VISIBLE); 112 | 113 | ((ImageView) findView(R.id.image_iv)).setImageBitmap(mBitmap); 114 | ((TextView) findView(R.id.msg_tv)).setText(R.string.app_name); 115 | } 116 | 117 | if (mInputText != null) { 118 | mInputTextEt.setVisibility(View.VISIBLE); 119 | if (!isRestored()) { 120 | // 如果是从旋转屏幕或其他状态恢复的fragment 121 | mInputTextEt.setText(mInputText); 122 | } 123 | mInputTextEt.setHint(mInputHint); 124 | } 125 | 126 | Button button = ((AlertDialog) getDialog()).getButton(AlertDialog.BUTTON_POSITIVE); 127 | 128 | button.setOnClickListener(v -> { 129 | if (TextUtils.isEmpty(mInputTextEt.getText())) { 130 | Toast.makeText(getActivity(), "请输入内容,否则不能关闭!", Toast.LENGTH_SHORT).show(); 131 | } else { 132 | getPositiveListener().onClick(null, AlertDialog.BUTTON_POSITIVE); 133 | dismiss(); 134 | } 135 | }); 136 | 137 | showInputMethod(mInputTextEt); 138 | } 139 | 140 | public void showInputMethod(final EditText editText) { 141 | editText.post(() -> { 142 | editText.setFocusable(true); 143 | editText.setFocusableInTouchMode(true); 144 | editText.requestFocus(); 145 | InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); 146 | if (imm != null) { 147 | imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT); 148 | } 149 | }); 150 | } 151 | 152 | public 153 | @Nullable 154 | EditText getInputTextEt() { 155 | return mInputTextEt; 156 | } 157 | 158 | @Override 159 | public void onDestroyView() { 160 | super.onDestroyView(); 161 | mInputTextEt = null; 162 | } 163 | 164 | /** 165 | * Bitmap转Byte[] 166 | */ 167 | public static byte[] bitmap2ByteArr(Bitmap bitmap) { 168 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 169 | bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos); 170 | return baos.toByteArray(); 171 | } 172 | 173 | } 174 | -------------------------------------------------------------------------------- /app/src/main/java/kale/easydialog/MainActivity.java: -------------------------------------------------------------------------------- 1 | package kale.easydialog; 2 | 3 | import android.content.DialogInterface; 4 | import android.content.Intent; 5 | import android.graphics.BitmapFactory; 6 | import android.os.Bundle; 7 | import android.support.annotation.NonNull; 8 | import android.support.design.widget.BottomSheetBehavior; 9 | import android.support.v7.app.AppCompatActivity; 10 | import android.text.TextUtils; 11 | import android.util.Log; 12 | import android.view.View; 13 | import android.widget.Toast; 14 | 15 | import kale.ui.view.dialog.EasyDialog; 16 | 17 | /** 18 | * 关于更多对话框的设置请参考:http://www.cnblogs.com/tianzhijiexian/p/3867731.html 19 | */ 20 | public class MainActivity extends AppCompatActivity implements DialogInterface.OnClickListener { 21 | 22 | public final String TAG = getClass().getSimpleName(); 23 | 24 | private BottomSheetBehavior behavior; 25 | 26 | @Override 27 | protected void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | setContentView(R.layout.activity_main); 30 | setViews(); 31 | 32 | 33 | // new Handler().postDelayed(() -> ((App) getApplication()).showDialog("全局弹窗", "可在任意时机弹出一个dialog"), 5000); 34 | 35 | } 36 | 37 | private void setViews() { 38 | findViewById(R.id.dayNight_btn).setOnClickListener( 39 | v -> startActivity(new Intent(this, MyStyleActivity.class))); 40 | 41 | // 得到 Bottom Sheet 的视图对象所对应的 BottomSheetBehavior 对象 42 | behavior = BottomSheetBehavior.from(findViewById(R.id.ll_sheet_root)); 43 | behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { 44 | @Override 45 | public void onStateChanged(@NonNull View bottomSheet, int newState) { 46 | 47 | } 48 | 49 | @Override 50 | public void onSlide(@NonNull View bottomSheet, float slideOffset) { 51 | 52 | } 53 | }); 54 | } 55 | 56 | public EasyDialog easyDialog; 57 | 58 | public void simpleDialog(View v) { 59 | final android.support.v4.app.FragmentTransaction ft = 60 | getSupportFragmentManager().beginTransaction(); 61 | 62 | EasyDialog.Builder builder = EasyDialog.builder(this); 63 | builder.setTitle("Title") 64 | .setIcon(R.drawable.saber) 65 | .setMessage(R.string.hello_world) 66 | .setOnCancelListener(dialog -> Log.d(TAG, "onCancel")) 67 | .setOnDismissListener(dialog -> Log.d(TAG, "onDismiss")) 68 | // .setNeutralButton("know", null) 69 | // 设置对话框上的按钮 ok->dismiss 70 | .setPositiveButton("ok", (dialog, which) -> { 71 | Log.d(TAG, "onClick ok"); 72 | 73 | ft.remove(easyDialog); 74 | ft.addToBackStack(null); 75 | 76 | EasyDialog.builder(MainActivity.this) 77 | .setTitle("Stack Dialog") 78 | .setMessage("Please press back button") 79 | .build() 80 | .show(ft, "stackDialog"); 81 | }) 82 | // cancel -> dismiss 83 | .setNegativeButton("cancel", (dialog, which) -> dialog.dismiss()) 84 | .setNeutralButton("ignore", this) 85 | .setCancelable(true); 86 | 87 | easyDialog = builder.build(); 88 | easyDialog.showAllowingStateLoss(getSupportFragmentManager()); 89 | 90 | // easyDialog.show(getSupportFragmentManager()); 91 | 92 | // findViewById(R.id.coordinatorlayout).setVisibility(View.INVISIBLE); 93 | } 94 | 95 | public void listDialog(View v) { 96 | EasyDialog.builder(this) 97 | .setItems(R.array.country, (dialog, which) -> showToast("click " + which)) 98 | .setRetainInstance(true) 99 | .setPositiveButton("yes", (dialog, which) -> showToast("yes")) 100 | .setNegativeButton("no", this) 101 | .build() 102 | .show(getSupportFragmentManager()); 103 | } 104 | 105 | /** 106 | * 支持单选列表的对话框 107 | */ 108 | public void singleChoiceDialog(View v) { 109 | EasyDialog dialog = EasyDialog.builder(this) 110 | .setTitle("Single Choice Dialog") 111 | .setSingleChoiceItems(new String[]{"Android", "ios", "wp"}, 1, 112 | (dialog1, position) -> { 113 | Log.d(TAG, "onItemClick pos = " + position); 114 | dialog1.dismiss(); 115 | })// 设置单选列表的数据和监听 116 | .setPositiveButton("ok", null) 117 | .build(); 118 | dialog.setCancelable(false); 119 | dialog.show(getSupportFragmentManager(), TAG); 120 | } 121 | 122 | /** 123 | * 支持多选列表的对话框 124 | */ 125 | public void multiChoiceDialog(View v) { 126 | EasyDialog.builder(this) 127 | // 设置数据和默认选中的选项 128 | .setMultiChoiceItems( 129 | new String[]{"Android", "ios", "wp"}, new boolean[]{true, false, true}, 130 | (dialog, which, isChecked) -> showToast("onClick pos = " + which + " , isChecked = " + isChecked)) // 设置监听器 131 | .build() 132 | .show(getSupportFragmentManager(), TAG); 133 | } 134 | 135 | private InputDialog dialog; 136 | 137 | /** 138 | * 可以输入文字的dialog,拥有自定义布局 139 | */ 140 | public void inputDialog(View v) { 141 | dialog = new InputDialog.Builder(this) 142 | .setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.kale)) 143 | .setInputText("", "hint") 144 | .setPositiveButton("ok", new DialogInterface.OnClickListener() { 145 | @Override 146 | public void onClick(DialogInterface ignore, int which) { 147 | String text = dialog.getInputTextEt().getText().toString(); 148 | if (!TextUtils.isEmpty(text)) { 149 | showToast(text); 150 | } 151 | } 152 | }) 153 | .build(); 154 | dialog.show(getSupportFragmentManager()); // 一个参数的show() 155 | } 156 | 157 | public void imageDialog(View v) { 158 | EasyDialog.builder(this, ImageDialog.class) 159 | .setPositiveButton("弹出动态设置样式的Dialog", (dialog, which) -> { 160 | 161 | }) 162 | .build() 163 | .show(getSupportFragmentManager()); 164 | } 165 | 166 | /** 167 | * 显示在顶部的dialog,背景透明 168 | */ 169 | public void topDialog(View v) { 170 | TopDialog.Builder builder = EasyDialog.builder(this, TopDialog.class); 171 | builder.setTitle("标题"); 172 | builder.setPositiveButton("设置了宽高", null); 173 | builder.setNegativeButton("位置在顶部", null); 174 | builder.build().show(getSupportFragmentManager()); 175 | } 176 | 177 | /** 178 | * 自定一个dialog的builder 179 | */ 180 | public void myBuilderDialog(View v) { 181 | new MyBuilderDialog.Builder(this) 182 | .setTitle("Custom Builder Dialog") 183 | .setMessage("message") 184 | .setName("kale") 185 | .setAge(31) 186 | .build() 187 | .show(getSupportFragmentManager()); 188 | } 189 | 190 | /** 191 | * 从底部弹出的对话框 192 | */ 193 | public void bottomDialog(View v) { 194 | BottomDialog.Builder builder = EasyDialog.builder(this, BottomDialog.class); 195 | builder.setMessage("click me"); 196 | 197 | builder.setIsBottomDialog(true); // 设置后则会变成从底部弹出,否则为正常模式 198 | 199 | // 监听点空白处cancel的事件 200 | builder.setOnCancelListener(d -> showToast("cancel")); 201 | builder.setOnDismissListener(d -> showToast("dismiss")); 202 | 203 | EasyDialog dialog = builder.build(); 204 | dialog.show(getSupportFragmentManager(), "dialog"); 205 | 206 | // 如果设置了,那么底部dialog就不支持手势关闭和空白处关闭 207 | dialog.setCancelable(false); 208 | } 209 | 210 | /** 211 | * Activity中的可以从底部拉出的dialog 212 | */ 213 | public void bottomDialogInActivity(View v) { 214 | if (behavior.getState() == BottomSheetBehavior.STATE_EXPANDED) { 215 | behavior.setState(BottomSheetBehavior.STATE_COLLAPSED); 216 | } else { 217 | behavior.setState(BottomSheetBehavior.STATE_EXPANDED); 218 | } 219 | } 220 | 221 | public void showToast(String msg) { 222 | Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show(); 223 | } 224 | 225 | @Override 226 | public void onClick(DialogInterface dialog, int which) { 227 | if (this != ((App) getApplication()).getCurActivity()) { 228 | throw new RuntimeException("leak"); 229 | } 230 | showToast("handle event in activity, is finish ? " + isDestroyed()); 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /app/src/main/java/kale/easydialog/MyBuilderDialog.java: -------------------------------------------------------------------------------- 1 | package kale.easydialog; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.support.annotation.NonNull; 6 | import android.support.v7.app.AlertDialog; 7 | import android.view.View; 8 | import android.widget.Toast; 9 | 10 | import kale.ui.view.dialog.BaseCustomDialog; 11 | import kale.ui.view.dialog.BaseEasyDialog; 12 | import kale.ui.view.dialog.EasyDialog; 13 | 14 | /** 15 | * @author Kale 16 | * @date 2016/5/3 17 | * 18 | * 自定义builder的dialog 19 | */ 20 | public class MyBuilderDialog extends BaseCustomDialog { 21 | 22 | public static final String KEY_AGE = "KEY_AGE", KEY_NAME = "KEY_NAME"; 23 | 24 | /** 25 | * 继承自{@link EasyDialog.Builder}以扩展builder 26 | */ 27 | public static class Builder extends BaseEasyDialog.Builder { 28 | 29 | private Bundle bundle = new Bundle(); 30 | 31 | public Builder(@NonNull Context context) { 32 | super(context); 33 | } 34 | 35 | public Builder setAge(int age) { 36 | bundle.putInt(KEY_AGE, age); 37 | return this; 38 | } 39 | 40 | public Builder setName(String name) { 41 | bundle.putString(KEY_NAME, name); 42 | return this; 43 | } 44 | 45 | @NonNull 46 | @Override 47 | protected EasyDialog createDialog() { 48 | MyBuilderDialog dialog = new MyBuilderDialog(); 49 | dialog.setArguments(bundle); // 增加自己的bundle 50 | return dialog; 51 | } 52 | } 53 | 54 | @Override 55 | protected int getLayoutResId() { 56 | return 0; 57 | } 58 | 59 | @Override 60 | protected void bindViews(View root) { 61 | 62 | } 63 | 64 | @Override 65 | protected void modifyAlertDialogBuilder(AlertDialog.Builder builder) { 66 | super.modifyAlertDialogBuilder(builder); 67 | Bundle arguments = getArguments(); 68 | 69 | String name = arguments.getString(KEY_NAME); 70 | int age = arguments.getInt(KEY_AGE); 71 | 72 | String str = "name: " + name + ", age: " + age; 73 | 74 | // 修改builder对象 75 | builder.setMessage("修改后的message是:\n\n" + str); 76 | } 77 | 78 | @Override 79 | protected void setViews() { 80 | int age = getArguments().getInt(KEY_AGE); 81 | Toast.makeText(getContext(), "age: " + age, Toast.LENGTH_SHORT).show(); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/java/kale/easydialog/MyStyleActivity.java: -------------------------------------------------------------------------------- 1 | package kale.easydialog; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatDelegate; 6 | 7 | /** 8 | * @author Kale 9 | * @date 2016/11/21 10 | */ 11 | 12 | public class MyStyleActivity extends MainActivity { 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES); 17 | 18 | super.onCreate(savedInstanceState); 19 | 20 | findViewById(R.id.dayNight_btn).setOnClickListener( 21 | v -> startActivity(new Intent(this, MainActivity.class))); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/kale/easydialog/TopDialog.java: -------------------------------------------------------------------------------- 1 | package kale.easydialog; 2 | 3 | import android.app.Activity; 4 | import android.graphics.drawable.ColorDrawable; 5 | import android.os.Bundle; 6 | import android.support.v4.app.FragmentTransaction; 7 | import android.support.v7.app.AlertDialog; 8 | import android.util.DisplayMetrics; 9 | import android.view.Gravity; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.Window; 13 | import android.view.WindowManager; 14 | import android.widget.TextView; 15 | 16 | import kale.ui.view.dialog.BaseCustomDialog; 17 | import kale.ui.view.dialog.EasyDialog; 18 | 19 | /** 20 | * @author Kale 21 | * @date 2016/11/15 22 | * 23 | * 自定义布局的dialog 24 | */ 25 | 26 | public class TopDialog extends BaseCustomDialog { 27 | 28 | private TextView titleTv; 29 | 30 | /** 31 | * 自定义布局 32 | */ 33 | @Override 34 | protected int getLayoutResId() { 35 | return R.layout.custom_dialog_layout; 36 | } 37 | 38 | /** 39 | * title的布局,title的布局会显示在自定义布局的上方 40 | */ 41 | @Override 42 | protected void modifyAlertDialogBuilder(AlertDialog.Builder builder) { 43 | super.modifyAlertDialogBuilder(builder); 44 | 45 | View titleView = LayoutInflater.from(getContext()).inflate(R.layout.custom_dialog_title_layout, null, false); 46 | builder.setCustomTitle(titleView); // 修改builder中的titleView 47 | } 48 | 49 | @Override 50 | protected void bindViews(View root) { 51 | titleTv = findView(R.id.title_tv); 52 | } 53 | 54 | @Override 55 | protected void setViews() { 56 | setLayout(); 57 | // setBackground(); 58 | 59 | // 可从getDialogParams()得到builder中的所有参数 60 | titleTv.setText(getDialogParams().title); 61 | 62 | titleTv.setOnClickListener(v -> { 63 | FragmentTransaction ft = getFragmentManager().beginTransaction(); 64 | 65 | ft.remove(TopDialog.this); 66 | ft.addToBackStack(null); 67 | 68 | EasyDialog.builder(getContext()) 69 | .setTitle("第二个对话框") 70 | .setMessage("点击“返回”后会退回到之前的dialog") 71 | .build() 72 | .show(ft, "dialog"); 73 | }); 74 | } 75 | 76 | /** 77 | * 这时dialog已经初始化完毕 78 | */ 79 | @Override 80 | public void onActivityCreated(Bundle savedInstanceState) { 81 | super.onActivityCreated(savedInstanceState); 82 | // getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE); 83 | } 84 | 85 | /** 86 | * 也可通过setLayout来设置: 87 | * getDialog().getWindow().setLayout(dm.widthPixels, getDialog().getWindow().getAttributes().height); 88 | */ 89 | private void setLayout() { 90 | Window window = getDialog().getWindow(); 91 | final WindowManager.LayoutParams lp = window.getAttributes(); 92 | 93 | // 强制宽高 94 | int padding = getResources().getDimensionPixelOffset(R.dimen.dialog_padding); 95 | lp.width = getScreenWidth(getActivity()) - (padding * 2); 96 | 97 | lp.height = getResources().getDimensionPixelOffset(R.dimen.dialog_height); 98 | 99 | lp.gravity = Gravity.TOP; // 设置展示的位置 100 | 101 | window.setAttributes(lp); 102 | } 103 | 104 | /** 105 | * 强制取消背景,保持有透明 106 | */ 107 | private void setBackground() { 108 | getDialog().getWindow().setBackgroundDrawable(new ColorDrawable()); // 去除dialog的背景,即透明 109 | // getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(0xffffffff)); // 设置白色背景 110 | // getDialog().getWindow().setBackgroundDrawableResource(R.drawable.dialog_bg_custom); // 设置背景 111 | } 112 | 113 | public static int getScreenWidth(Activity activity) { 114 | final DisplayMetrics dm = new DisplayMetrics(); 115 | activity.getWindowManager().getDefaultDisplay().getMetrics(dm); 116 | return dm.widthPixels; 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /app/src/main/res/anim/dialog_enter.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/anim/dialog_out.xml: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_mlls.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaleai/EasyDialog/3400a138c77df3af81728855442f426bf5b67059/app/src/main/res/drawable/bg_mlls.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/button_checked_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 10 | 11 | 14 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/dialog_bg_custom.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/dialog_bg_main.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/dialog_button_divider.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/divider.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaleai/EasyDialog/3400a138c77df3af81728855442f426bf5b67059/app/src/main/res/drawable/icon_checked.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_unchecked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaleai/EasyDialog/3400a138c77df3af81728855442f426bf5b67059/app/src/main/res/drawable/icon_unchecked.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/kale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaleai/EasyDialog/3400a138c77df3af81728855442f426bf5b67059/app/src/main/res/drawable/kale.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/nintendoswitch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaleai/EasyDialog/3400a138c77df3af81728855442f426bf5b67059/app/src/main/res/drawable/nintendoswitch.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/saber.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaleai/EasyDialog/3400a138c77df3af81728855442f426bf5b67059/app/src/main/res/drawable/saber.png -------------------------------------------------------------------------------- /app/src/main/res/font/aasudaqishui.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaleai/EasyDialog/3400a138c77df3af81728855442f426bf5b67059/app/src/main/res/font/aasudaqishui.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/sudaqishui.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 18 | 19 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 19 | 20 |