YCDialog

Introduction: 自定义弹窗,其中包括:自定义 Toast,采用 builder 模式,支持设置吐司多个属性;自定义 dialog 控件,仿 IOS 底部弹窗;自定义 DialogFragment 弹窗,支持自定义布局,也支持填充 recyclerView 布局;自定义 PopupWindow 弹窗,轻量级,还有自定义 Snackbar 等等;还有自定义 loading 加载窗,简单便用。目前已经用于多个正式项目中。同时还有附加 7 篇关于弹窗的源码分析博客
More: Author   ReportBugs   
Tags:

自定义对话框

目录介绍

  • 01.该库介绍
  • 02.效果展示
  • 03.如何使用
  • 04.注意要点
  • 05.优化问题
  • 06.版本更新
  • 07.弹窗 bug
  • 08.参考案例

01.该库介绍

  • 本库融合了弹窗类型有:Dialog,DialogFragment,Toast,PopupWindow,SnackBar。功能十分齐全,适合应用于实际开发中。
  • 一行代码调用检测手机通知权限,建议加上,大部分手机通知权限是开启的。如果关闭了,则吐司是无法显示的,但是仍有部分手机,比如某型号小米手机,锤子手机等就权限需要手动开启。
  • 针对吐司工具类,轻量级,使用 builder 模式,链式调用设置属性,可以设置吐司背景,文字内容,或者自定义布局吐司。解决避免点击多次导致吐司多次问题。
  • 针对 Dialog 弹窗,仿 IOS 底部弹窗 Dialog,可以设置弹窗主题,菜单项单击事件,取消事件监听,可以设置菜单项名称,十分方便。
  • 针对实际开发中,比如请求接口需要加载 loading,可以直接使用该库中的 ViewLoading,一行代码 show 或者一行代码 dismiss。
  • 封装了 SnackBar 工具类,链式调用,可以快捷设置内容和 action,十分方便开发者使用。

02.如何使用

  • 首先在项目 build.gradlew 中添加**

      //这个是依赖弹窗所有的
      implementation 'com.github.yangchong211.YCDialog:DialogLib:3.6.8'
    
      //如果你想使用某个 lib,可以选择下面这种依赖
      implementation 'com.github.yangchong211.YCDialog:PopupWindow:3.6.8'
      implementation 'com.github.yangchong211.YCDialog:ToastUtils:3.6.8'
      implementation 'com.github.yangchong211.YCDialog:SnackBar:3.6.8'
      implementation 'com.github.yangchong211.YCDialog:DialogFragment:3.6.8'
      implementation 'com.github.yangchong211.YCDialog:BaseDialog:3.6.8'
    

2.1 仿 IOS 底部弹窗 Dialog

  • 支持设置弹窗主题,支持设置取消事件 listener,支持设置名称和标题。有多个构造方法可以创建对象……
      private void showCustomDialog() {
          final List<String> names = new ArrayList<>();
          names.add("拍照");
          names.add("相册");
          names.add("其他");
          showDialog(new CustomSelectDialog.SelectDialogListener() {
              @Override
              public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
              }
          }, names);
      }
      private CustomSelectDialog showDialog(CustomSelectDialog.SelectDialogListener listener,
                                              List<String> names) {
          CustomSelectDialog dialog = new CustomSelectDialog(this,
                  R.style.transparentFrameWindowStyle, listener, names);
          dialog.setItemColor(R.color.colorAccent,R.color.colorPrimary);
          //判断 activity 是否 finish
          if (!this.isFinishing()) {
              dialog.show();
          }
          return dialog;
      }
    

2.2 自定义 Toast 工具类

  • 采用 builder 构造者模式,链式编程,一行代码调用即可设置吐司 Toast。注意:为了避免静态 toast 对象内存泄漏,固可以使用应用级别的上下文 context。
  • 支持设置吐司的背景颜色,支持设置自定义吐司,同时支持设置吐司的多种属性等等,具体的用法如下所示:

      //可以自由设置吐司的背景颜色,默认是纯黑色
      ToastUtils.setToastBackColor(this.getResources().getColor(R.color.color_7f000000));
    
      //直接设置最简单吐司,只有吐司内容
      ToastUtils.showRoundRectToast("自定义吐司");
    
      //设置吐司标题和内容
      ToastUtils.showRoundRectToast("吐司一下","潇湘剑雨杨充是个逗比");
    
      //第三种直接设置自定义布局的吐司
      ToastUtils.showRoundRectToast(R.layout.view_layout_toast_delete);
    
      //或者直接采用 bulider 模式创建
      ToastUtils.Builder builder = new ToastUtils.Builder(this.getApplication());
      builder
              .setDuration(Toast.LENGTH_SHORT)
              .setFill(false)
              .setGravity(Gravity.CENTER)
              .setOffset(0)
              .setDesc("内容内容")
              .setTitle("标题")
              .setTextColor(Color.WHITE)
              .setBackgroundColor(this.getResources().getColor(R.color.blackText))
              .build()
              .show();
    

2.3 自定义简易型 PopupWindow

  • 只需要继承 BasePopDialog 即可,实现其中的两个抽象方法

      //第一步,自定义 pop,继承 BasePopDialog 类,重新抽象方法
      public class CustomPop extends BasePopDialog {
    
          public CustomPop(Context context) {
              super(context);
          }
    
          @Override
          public int getViewResId() {
              return R.layout.view_pop_custom;
          }
    
          @Override
          public void initData(View contentView) {
              TextView tv_pop = (TextView) contentView.findViewById(R.id.tv_pop);
              tv_pop.setOnClickListener(new View.OnClickListener() {
                  @Override
                  public void onClick(View v) {
                      ToastUtils.showRoundRectToast("滚犊子吧");
                  }
              });
          }
      }
    
      //第二步,创建 pop 并且展示
      CustomPop customPop = new CustomPop(this);
      customPop.setDelayedMsDismiss(2500);
      customPop.setBgAlpha(0.5f);
      customPop.showAsDropDown(tv6, 0, -tv6.getMeasuredHeight() - tv6.getHeight());
    

2.4 自定义 PopupWindow,builder 模式

  • 如下所示,更加详细的用法的方法说明可以直接看项目 demo 的代码
      View contentView = LayoutInflater.from(this).inflate(R.layout.pop_layout,null);
      //创建并显示 popWindow
      popWindow = new CustomPopupWindow.PopupWindowBuilder(this)
              //.setView(R.layout.pop_layout)
              .setView(contentView)
              .setFocusable(true)
              //弹出 popWindow 时,背景是否变暗
              .enableBackgroundDark(true)
              //控制亮度
              .setBgDarkAlpha(0.7f)
              .setOutsideTouchable(true)
              .setAnimationStyle(R.style.popWindowStyle)
              .setOnDissmissListener(new PopupWindow.OnDismissListener() {
                  @Override
                  public void onDismiss() {
                      //对话框销毁时
                  }
              })
              .create()
              .showAsDropDown(tv6,0,10);
    

2.5 自定义底部弹窗 Dialog,builder 模式【使用 menu】

  • 代码如下所示

      new CustomBottomDialog(MainActivity.this)
              .title("这个是标题")
              .setCancel(true,"取消选择")
              .orientation(CustomBottomDialog.VERTICAL)
              .inflateMenu(R.menu.menu_share, new OnItemClickListener() {
                  @Override
                  public void click(CustomItem item) {
    
                  }
              })
              .show();
    

2.6 自定义布局弹窗 dialogFragment[填充普通布局]

  • 同 1.2.7 具体可以先安装一下 demo,看一下效果,使用方法如下所示:
      final BottomDialogFragment dialog = new BottomDialogFragment();
      dialog.setFragmentManager(getSupportFragmentManager());
      dialog.setViewListener(new BottomDialogFragment.ViewListener() {
          @Override
          public void bindView(View v) {
              TextView tv_cancel = (TextView) v.findViewById(R.id.tv_cancel);
              tv_cancel.setOnClickListener(new View.OnClickListener() {
                  @Override
                  public void onClick(View v) {
                      dialog.dismissDialogFragment();
                  }
              });
          }
      });
      dialog.setLayoutRes(R.layout.dialog_bottom_layout);
      dialog.setDimAmount(0.5f);
      dialog.setTag("BottomDialog");
      dialog.setCancelOutside(true);
      //这个高度可以自己设置,十分灵活
      dialog.setHeight(getScreenHeight() / 2);
      dialog.show();
    

2.7 自定义布局弹窗 dialogFragment[填充 list 布局]

  • 这个是之前沙丘大学底部弹窗而定制的,可以设置自定义布局 view 或者 layout,可以设置 tag,设置是否可以 cancel 弹窗,并且最主要可以设置弹窗的高度。
  • 比较常见,比如下载音乐,下载视频等等,弹窗页面可以滚动,ok,就可以使用这个,代码也不是很复杂,具体使用场景可以参考我的另外一个 demo:https://github.com/yangchong211/YCVideoPlayer

      final List<DialogBean> list = new ArrayList<>();
      for(int a=0 ; a<20 ; a++){
          DialogBean dialogBean = new DialogBean("ooo","杨充","title");
          list.add(dialogBean);
      }
    
      BottomDialogFragment.create(getSupportFragmentManager())
          .setViewListener(new BottomDialogFragment.ViewListener() {
              @Override
              public void bindView(View v) {
                  RecyclerView recyclerView = (RecyclerView) v.findViewById(R.id.recyclerView);
                  ImageView ivCancel = (ImageView) v.findViewById(R.id.iv_cancel);
                  ImageView ivDownload = (ImageView) v.findViewById(R.id.iv_download);
    
                  recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this));
                  DialogListAdapter mAdapter = new DialogListAdapter(MainActivity.this, list);
                  recyclerView.setAdapter(mAdapter);
                  final RecycleViewItemLine line = new RecycleViewItemLine(
                          MainActivity.this, LinearLayout.HORIZONTAL, 2,
                          MainActivity.this.getResources().getColor(R.color.grayLine));
                  recyclerView.addItemDecoration(line);
                  mAdapter.setOnItemClickListener(new DialogListAdapter.OnItemClickListener() {
                      @Override
                      public void onItemClick(int position) {
    
                      }
                  });
                  View.OnClickListener listener = new View.OnClickListener() {
                      @Override
                      public void onClick(View v) {
                          switch (v.getId()) {
                              case R.id.iv_cancel:
    
                                  break;
                              case R.id.iv_download:
                                  ToastUtils.showToast("下载");
                                  break;
                              default:
                                  break;
                          }
                      }
                  };
                  ivCancel.setOnClickListener(listener);
                  ivDownload.setOnClickListener(listener);
              }
          })
          .setLayoutRes(R.layout.dialog_bottom_layout_list)
          .setDimAmount(0.5f)
          .setTag("BottomDialog")
          .setCancelOutside(true)
          .setHeight(getScreenHeight() / 2)
          .show();
    

2.8 自定义常见弹窗,builder 模式

  • 非常常见的弹窗,由于原生自带的弹窗不太美观,因此就需要自己定制弹窗呢。可以设置弹窗标题,内容,以及下面取消,确定等按钮内容和颜色
  • 注意,如果某个属性设置为空或者不设置,那么就会隐藏该布局。这个也是根据公司产品具体的业务场景,逐渐演变过来的,具体效果可以直接看项目 demo
      CustomDialogFragment
              .create(getSupportFragmentManager())
              .setTitle("这个是是标题")
              .setContent("这个是弹窗的内容")
              .setOtherContent("其他")
              .setDimAmount(0.2f)
              .setTag("dialog")
              .setCancelOutside(true)
              .setCancelListener(new View.OnClickListener() {
                  @Override
                  public void onClick(View v) {
                      CustomDialogFragment.dismissDialogFragment();
                      ToastUtils.showRoundRectToast("取消了");
                  }
              })
              .setOkListener(new View.OnClickListener() {
                  @Override
                  public void onClick(View v) {
                      CustomDialogFragment.dismissDialogFragment();
                      ToastUtils.showRoundRectToast("确定了");
                  }
              })
              .setOtherListener(new View.OnClickListener() {
                  @Override
                  public void onClick(View v) {
                      CustomDialogFragment.dismissDialogFragment();
                      ToastUtils.showRoundRectToast("其他内容");
                  }
              })
              .show();
    

2.9 自定义 loading 加载窗

  • 一行代码调用即可,个人感觉不需要太复杂的代码,就能实现这个功能,满足具体业务需求,也是不亦乐乎:
      //开始 loading
      ViewLoading.show(this);
      ViewLoading.show(this,"加载中");
      //结束 loading
      ViewLoading.dismiss(this);
    

2.10 自定义 SnackBar 工具类

  • 可以一行代码调用,也可以自己使用链式编程调用。支持设置显示时长属性;可以设置背景色;可以设置文字大小,颜色;可以设置 action 内容,文字大小,颜色,还有点击事件;可以设置 icon;代码如下所示,更多内容可以直接运行 demo 哦!

      //1.只设置 text
      SnackBarUtils.showSnackBar(this,"滚犊子");
    
      //2.设置 text,action,和点击事件
      SnackBarUtils.showSnackBar(this, "滚犊子", "ACTION", new View.OnClickListener() {
          @Override
          public void onClick(View v) {
              ToastUtils.showRoundRectToast("滚犊子啦?");
          }
      });
    
      //3.设置 text,action,和点击事件,和 icon
      SnackBarUtils.showSnackBar(this, "滚犊子", "ACTION",R.drawable.icon_cancel, new View.OnClickListener() {
          @Override
          public void onClick(View v) {
              ToastUtils.showRoundRectToast("滚犊子啦?");
          }
      });
    
      //4.链式调用
      SnackBarUtils.builder()
          .setBackgroundColor(this.getResources().getColor(R.color.color_7f000000))
          .setTextSize(14)
          .setTextColor(this.getResources().getColor(R.color.white))
          .setTextTypefaceStyle(Typeface.BOLD)
          .setText("滚犊子")
          .setMaxLines(4)
          .centerText()
          .setActionText("收到")
          .setActionTextColor(this.getResources().getColor(R.color.color_f25057))
          .setActionTextSize(16)
          .setActionTextTypefaceStyle(Typeface.BOLD)
          .setActionClickListener(new View.OnClickListener() {
              @Override
              public void onClick(View v) {
                  ToastUtils.showRoundRectToast("滚犊子啦?");
              }
          })
          .setIcon(R.drawable.icon_cancel)
          .setActivity(MainActivity.this)
          .setDuration(SnackBarUtils.DurationType.LENGTH_INDEFINITE)
          .build()
          .show();
    

04.注意要点

05.效果展示

image image image image image image image image image image image image

05.优化问题

关于弹窗系列博客
  • 02.Toast 源码深度分析
    • 最简单的创建,简单改造避免重复创建,show()方法源码分析,scheduleTimeoutLocked 吐司如何自动销毁的,TN 类中的消息机制是如何执行的,普通应用的 Toast 显示数量是有限制的,用代码解释为何 Activity 销毁后 Toast 仍会显示,Toast 偶尔报错 Unable to add window 是如何产生的,Toast 运行在子线程问题,Toast 如何添加系统窗口的权限等等
  • 03.DialogFragment 源码分析
    • 最简单的使用方法,onCreate(@Nullable Bundle savedInstanceState)源码分析,重点分析弹窗展示和销毁源码,使用中 show()方法遇到的 IllegalStateException 分析
  • 04.Dialog 源码分析
    • AlertDialog 源码分析,通过 AlertDialog.Builder 对象设置属性,Dialog 生命周期,Dialog 中 show 方法展示弹窗分析,Dialog 的 dismiss 销毁弹窗,Dialog 弹窗问题分析等等
  • 05.PopupWindow 源码分析
    • 显示 PopupWindow,注意问题宽和高属性,showAsDropDown()源码,dismiss()源码分析,PopupWindow 和 Dialog 有什么区别?为何弹窗点击一下就 dismiss 呢?
  • 06.Snackbar 源码分析
    • 最简单的创建,Snackbar 的 make 方法源码分析,Snackbar 的 show 显示与点击消失源码分析,显示和隐藏中动画源码分析,Snackbar 的设计思路,为什么 Snackbar 总是显示在最下面
  • 07.弹窗常见问题
    • DialogFragment 使用中 show()方法遇到的 IllegalStateException,什么常见产生的?Toast 偶尔报错 Unable to add window,Toast 运行在子线程导致崩溃如何解决?
  • 08.Builder 模式
    • 你会发现,在这个弹窗封装库中,很多地方用到了 builder 模式,那么可以先了解下 Builder 模式使用场景,简单案例,Builder 模式实际案例 Demo 展示,看看 AlertDialog.Builder 源代码如何实现,为什么 AlertDialog 要使用 builder 模式呢?builder 模式优缺点分析。

06.版本更新

  • v1.0 更新 2016 年 6 月 2 日
  • v1.4 更新 2017 年 8 月 9 日
  • v3.3 更新 2018 年 1 月 12 日
  • v3.4 更新 2018 年 1 月 18 日
  • v3.5 更新 2018 年 1 月 31 日
  • v3.5.1 更新与 2018 年 6 月 2 日
  • v3.5.3 更新于 2018 年 9 月 10 日
  • v3.6.5 更新于 2018 年 9 月 15 日
  • v3.6.6 更新于 2019 年 5 月 7 日

07.弹窗 bug

  • 1.DialogFragment 使用中 show()方法遇到的 IllegalStateException
  • 2.Toast 偶尔报错 Unable to add window,is your activity running
  • 3.Toast 运行在子线程导致崩溃,子线程如何吐司
  • 4.Dialog Unable to add window --token null is not for an application
  • 5.为什么 Dialog 不能用 Application 的 Context,一旦用了则会导致崩溃

其他介绍

01.关于博客汇总链接

02.关于我的博客

Apps
About Me
GitHub: Trinea
Facebook: Dev Tools