当前位置: 移动技术网 > 移动技术>移动开发>Android > Android中用Builder模式自定义Dialog的方法

Android中用Builder模式自定义Dialog的方法

2019年07月24日  | 移动技术网移动技术  | 我要评论

前言

我们开发人员在实际项目过程中遇到的需求是多种多样的,有时我们要匹配app自己的设计风格,有时我们会觉得系统的对话框使用起来不够自由,因此自己定义一个适合自己的dialog是很有必要的。

为什么要用builder模式

builder设计模式是一步步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程。它的优点就在于将对象的构建和表示分离从而解耦。我们都知道android系统自身的对话框如alertdialog就采用了builder模式,因此可见builder模式很适合用来构建dialog对象。

下面话不多说了,上代码。

basedialog.java

package com.acker.android.dialog;

import android.app.dialog;
import android.content.context;
import android.os.bundle;
import android.text.textutils;
import android.view.view;
import android.widget.button;
import android.widget.framelayout;
import android.widget.linearlayout;
import android.widget.progressbar;
import android.widget.textview;

/**
 * 自定义dialog基类
 *
 * @author guojinyu
 */
public class basedialog extends dialog {

  private textview tvtitle;
  private textview tvmsg;
  private progressbar pbloading;
  private button btnpositive;
  private button btnnegative;
  private framelayout flcustom;
  private view.onclicklistener ondefaultclicklistener = new view.onclicklistener() {

    @override
    public void onclick(view view) {
      cancel();
    }

  };
  private view.onclicklistener onpositivelistener = ondefaultclicklistener;
  private view.onclicklistener onnegativelistener = ondefaultclicklistener;
  private string mtitle;
  private string mmessage;
  private string positivetext;
  private string negativetext;
  private boolean isprogressbarshow = false;
  private boolean isnegativebtnshow = true;
  private view mview;

  private basedialog(context context) {
    super(context, r.style.mydialog);
  }

  @override
  public void oncreate(bundle savedinstancestate) {
    super.oncreate(savedinstancestate);
    setcontentview(r.layout.dialog_base);
    flcustom = (framelayout) findviewbyid(r.id.fl_dialog_content);
    tvtitle = (textview) findviewbyid(r.id.tv_title);
    pbloading = (progressbar) findviewbyid(r.id.pb_loading);
    tvmsg = (textview) findviewbyid(r.id.tv_msg);
    btnpositive = (button) findviewbyid(r.id.btn_positive);
    btnnegative = (button) findviewbyid(r.id.btn_negative);
  }

  /**
   * 调用完builder类的create()方法后显示该对话框的方法
   */
  @override
  public void show() {
    super.show();
    show(this);
  }

  private void show(basedialog mdialog) {
    if (!textutils.isempty(mdialog.mtitle)) {
      mdialog.tvtitle.settext(mdialog.mtitle);
    }
    if (mdialog.mview != null) {
      mdialog.flcustom.addview(mdialog.mview);
      mdialog.pbloading.setvisibility(view.gone);
      mdialog.tvmsg.setvisibility(view.gone);
    } else {
      if (!textutils.isempty(mdialog.mmessage)) {
        mdialog.tvmsg.settext(mdialog.mmessage);
        mdialog.tvmsg.setvisibility(view.visible);
      }
      if (isprogressbarshow) {
        mdialog.pbloading.setvisibility(view.visible);
        mdialog.btnpositive.setvisibility(view.gone);
        mdialog.btnnegative.setvisibility(view.gone);
      }
    }
    if (!mdialog.isnegativebtnshow) {
      mdialog.btnnegative.setvisibility(view.gone);
      linearlayout.layoutparams layoutparams = (linearlayout.layoutparams) mdialog.btnpositive
          .getlayoutparams();
      layoutparams.setmargins(150, layoutparams.topmargin, 150, layoutparams.bottommargin);
      mdialog.btnpositive.setlayoutparams(layoutparams);
    } else {
      mdialog.btnnegative.setonclicklistener(mdialog.onnegativelistener);
      if (!textutils.isempty(mdialog.negativetext)) {
        mdialog.btnnegative.settext(mdialog.negativetext);
      }
    }
    mdialog.btnpositive.setonclicklistener(mdialog.onpositivelistener);
    if (!textutils.isempty(mdialog.positivetext)) {
      mdialog.btnpositive.settext(mdialog.positivetext);
    }
  }

  public static class builder {

    private basedialog mdialog;

    public builder(context context) {
      mdialog = new basedialog(context);
    }

    /**
     * 设置对话框标题
     *
     * @param title
     */
    public builder settitle(string title) {
      mdialog.mtitle = title;
      return this;
    }

    /**
     * 设置对话框文本内容,如果调用了setview()方法,该项失效
     *
     * @param msg
     */
    public builder setmessage(string msg) {
      mdialog.mmessage = msg;
      return this;
    }

    /**
     * 设置确认按钮的回调
     *
     * @param onclicklistener
     */
    public builder setpositivebutton(view.onclicklistener onclicklistener) {
      mdialog.onpositivelistener = onclicklistener;
      return this;
    }

    /**
     * 设置确认按钮的回调
     *
     * @param btntext,onclicklistener
     */
    public builder setpositivebutton(string btntext, view.onclicklistener onclicklistener) {
      mdialog.positivetext = btntext;
      mdialog.onpositivelistener = onclicklistener;
      return this;
    }

    /**
     * 设置取消按钮的回掉
     *
     * @param onclicklistener
     */
    public builder setnegativebutton(view.onclicklistener onclicklistener) {
      mdialog.onnegativelistener = onclicklistener;
      return this;
    }

    /**
     * 设置取消按钮的回调
     *
     * @param btntext,onclicklistener
     */
    public builder setnegativebutton(string btntext, view.onclicklistener onclicklistener) {
      mdialog.negativetext = btntext;
      mdialog.onnegativelistener = onclicklistener;
      return this;
    }

    /**
     * 设置手否显示progressbar,默认不显示
     *
     * @param isprogressbarshow
     */
    public builder setprogressbarshow(boolean isprogressbarshow) {
      mdialog.isprogressbarshow = isprogressbarshow;
      return this;
    }

    /**
     * 设置是否显示取消按钮,默认显示
     *
     * @param isnegativebtnshow
     */
    public builder setnegativebtnshow(boolean isnegativebtnshow) {
      mdialog.isnegativebtnshow = isnegativebtnshow;
      return this;
    }

    /**
     * 设置自定义内容view
     *
     * @param view
     */
    public builder setview(view view) {
      mdialog.mview = view;
      return this;
    }

    /**
     * 设置该对话框能否被cancel掉,默认可以
     *
     * @param cancelable
     */
    public builder setcancelable(boolean cancelable) {
      mdialog.setcancelable(cancelable);
      return this;
    }

    /**
     * 设置对话框被cancel对应的回调接口,cancel()方法被调用时才会回调该接口
     *
     * @param oncancellistener
     */
    public builder setoncancellistener(oncancellistener oncancellistener) {
      mdialog.setoncancellistener(oncancellistener);
      return this;
    }

    /**
     * 设置对话框消失对应的回调接口,一切对话框消失都会回调该接口
     *
     * @param ondismisslistener
     */
    public builder setondismisslistener(ondismisslistener ondismisslistener) {
      mdialog.setondismisslistener(ondismisslistener);
      return this;
    }

    /**
     * 通过builder类设置完属性后构造对话框的方法
     */
    public basedialog create() {
      return mdialog;
    }
  }
}

代码很简单,basedialog类内定义一些对话框要显示的控件和这些控件对应的一些属性,以及最终将所有属性填入到控件的方法show() 。builder类是basedialog的一个内部类,其中定义了basedialog类的所有属性的set方法以及装配完毕后的create()方法。

对应的自定义dialog的布局文件 dialog_base.xml如下:

<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_marginleft="20dp"
  android:layout_marginright="20dp"
  android:background="@drawable/bg_base_dialog"
  android:orientation="vertical">

  <textview
    android:id="@+id/tv_title"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginbottom="10dp"
    android:layout_margintop="10dp"
    android:gravity="center"
    android:textcolor="@android:color/black"
    android:textsize="18sp" />

  <view
    android:layout_width="match_parent"
    android:layout_height="1dp"
    android:background="@android:color/black" />

  <linearlayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginleft="20dp"
    android:layout_marginright="20dp"
    android:orientation="horizontal">

    <progressbar
      android:id="@+id/pb_loading"
      android:layout_width="wrap_content"
      android:layout_height="match_parent"
      android:layout_gravity="center"
      android:layout_marginbottom="20dp"
      android:layout_marginright="20dp"
      android:layout_margintop="20dp"
      android:visibility="gone" />

    <textview
      android:id="@+id/tv_msg"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_gravity="center"
      android:layout_marginbottom="20dp"
      android:layout_margintop="20dp"
      android:textcolor="@android:color/black"
      android:textsize="18sp"
      android:visibility="gone" />
  </linearlayout>

  <framelayout
    android:id="@+id/fl_dialog_content"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"></framelayout>

  <linearlayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <button
      android:id="@+id/btn_negative"
      android:layout_width="0dp"
      android:layout_height="40dp"
      android:layout_marginbottom="20dp"
      android:layout_marginleft="20dp"
      android:layout_marginright="20dp"
      android:layout_weight="1"
      android:background="@drawable/bg_dialog_btn_negative"
      android:gravity="center"
      android:text="取消"
      android:textcolor="@android:color/white"
      android:textsize="18sp" />

    <button
      android:id="@+id/btn_positive"
      android:layout_width="0dp"
      android:layout_height="40dp"
      android:layout_marginbottom="20dp"
      android:layout_marginleft="20dp"
      android:layout_marginright="20dp"
      android:layout_weight="1"
      android:background="@drawable/bg_dialog_btn_positive"
      android:gravity="center"
      android:text="确定"
      android:textcolor="@android:color/white"
      android:textsize="18sp" />
  </linearlayout>

</linearlayout>

涉及到的其他资源文件如下:

对话框样式 styles.xml

<resources>
  <!-- 全局dialog样式 -->
  <style name="mydialog" parent="@android:style/theme.dialog">
    <item name="android:windowbackground">@android:color/transparent</item>
    <item name="android:windowframe">@null</item>
    <item name="android:windownotitle">true</item>
    <item name="android:windowisfloating">true</item>
    <item name="android:windowistranslucent">true</item>
    <item name="android:background">@android:color/transparent</item>
    <item name="android:backgrounddimenabled">true</item>
  </style>
</resources>

通过设置这些属性可以保证对话框背景透明无黑边。

确定取消按钮的颜色值 colors.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <color name="btn_dialog_negative_normal">#ff0000</color>
  <color name="btn_dialog_negative_pressed">#bf0000</color>
  <color name="btn_dialog_positive_normal">#368bff</color>
  <color name="btn_dialog_positive_pressed">#0067f3</color>
</resources>

对话框背景 bg_base_dialog.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
 android:shape="rectangle">
 <corners android:radius="4dp" />
 <solid android:color="@android:color/white" />
 <stroke
   android:width="1dp"
   android:color="#e5e7ea" />
</shape>

包含背景、圆角、阴影效果。

确定按钮背景 bg_dialog_btn_positive.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:drawable="@color/btn_dialog_positive_normal" android:state_pressed="false"></item>
  <item android:drawable="@color/btn_dialog_positive_pressed" android:state_pressed="true"></item>
</selector>

包含正常和按下的效果。

取消按钮背景 bg_dialog_btn_negative.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:drawable="@color/btn_dialog_negative_normal" android:state_pressed="false"></item>
  <item android:drawable="@color/btn_dialog_negative_pressed" android:state_pressed="true"></item>
</selector>

包含正常和按下的效果。

以上就是整个自定义dialog的所有内容,接下来我们通过一个简单的demo来演示如何使用它。

mainactivity.java

package com.acker.android.dialog;

import android.content.dialoginterface;
import android.os.bundle;
import android.support.v7.app.appcompatactivity;
import android.view.view;
import android.widget.toast;

public class mainactivity extends appcompatactivity {

  basedialog dialog;

  @override
  protected void oncreate(bundle savedinstancestate) {
    super.oncreate(savedinstancestate);
    setcontentview(r.layout.activity_main);
    dialog = new basedialog.builder(this).settitle("标题").setmessage("内容")
        .setpositivebutton("哈哈", new view.onclicklistener() {
          @override
          public void onclick(view view) {
            dialog.dismiss();
          }
        }).setoncancellistener(new dialoginterface.oncancellistener() {
          @override
          public void oncancel(dialoginterface dialoginterface) {
            toast.maketext(mainactivity.this, "cancel", toast.length_short).show();
          }
        }).setondismisslistener(new dialoginterface.ondismisslistener() {
          @override
          public void ondismiss(dialoginterface dialoginterface) {
            toast.maketext(mainactivity.this, "dismiss", toast.length_short).show();
          }
        }).create();
    dialog.show();
  }

}

来看下效果图:


很丑有木有,不过没关系,这里我们只是展示它的用法,如何把对话框做的好看一点就看各位的发挥了。可以看出自定义的basedialog的使用方法与andorid自身的alertdialog基本一致,都是通过其builder类进行对象的构建。

该自定义对话框还支持显示progressbar以及自定义内容填充的功能。

显示progressbar且触摸屏幕不可取消:

.setprogressbarshow(true)
.setcancelable(false)

效果图如下:


自定义内容区域且不显示取消按钮:

view view = getlayoutinflater().inflate(r.layout.dialog_input_amount, null);
final edittext amountedit = (edittext) view.findviewbyid(r.id.dialog_et_amount);
amountedit.settext("123456789");
.setview(view)
.setnegativebtnshow(false)

其对应的布局文件为 dialog_input_amount.xml:

<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:orientation="vertical" >

  <edittext
    android:id="@+id/dialog_et_amount"
    android:layout_width="match_parent"
    android:layout_height="40dp"
    android:layout_marginbottom="20dp"
    android:layout_marginleft="20dp"
    android:layout_marginright="20dp"
    android:layout_margintop="20dp"
    android:gravity="center_vertical"
    android:paddingleft="10dp"
    android:paddingright="10dp"
    android:inputtype="numberdecimal"
    android:singleline="true"
    android:textsize="18sp" >
  </edittext>

</linearlayout>

效果图如下:

总结

通过本文可以看出通过builder模式自定义dialog既可以维持原有android对话框的使用方法,同时使用方便,自由度更高,大家完全可以按照各自的需求来对代码作出相应的修改。需要说明的是本文并没有严格按照传统的builder设计模式来实现对话框,而是做了一些简化以更适合于我们的场景。以上就是本文的全部内容了,希望这篇文章的内容对各位android开发者们能有所帮助。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网