Android 个性化 之 百变Dialog

前言

关于Dialog的使用可谓是相当的广泛,可以用来做各种处理,其中也衍生了许多种的处理方式,有好有坏,自己有必要加以拿捏,熟练了以后,便可谓是招之即来,挥之即去。
  Dialog搭配一些个性化风格与简单动画会有一些神奇效果(Pics from Baidu_Pics):

dialog1.gif

dialog2.gif

  实现类似的展示效果并不难,接下来会在页面的基础上加入一些回调设置的说明,在开始之后还是先来看下其继承结构吧:

  java.lang.Object    
    ↳ android.app.Dialog

  -->implements DialogInterface, KeyEvent.Callback, View.OnCreateContextMenuListener, Window.Callback

由此可知,Dialog是一个可以独立的控件,而DialogInterface和KeyEvent.Callback也说明其是一个回调性很强的Object,在诸多的Dialog实例中,其子类AlertDialog是一种很好的实现方式,下面会具体介绍。

Dialog的初始化

Dialog和普通的View不同,它有自己的生命周期。

  • 通过onCreate( )创建
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.my_dialog);
    }
      可以看出和Activity的onCreate( )类似,也可以在其中加入一些init( )方法对layout文件中的子控件进行监听等。下面看一下调用方式:
    MyDialog myDialog = new MyDialog(this);
    myDialog.setCanceledOnTouchOutside(false);
    myDialog.show();
    // 具体逻辑

  • 通过构造方法创建
    public MyDialog(Activity activity, int resLayout) {
    super(activity, R.style.myDialog);
    this.activity = activity;
    this.resLayout = resLayout;
    }
      构造方法中的参数可以根据需求来定义,一般不超过5个,不然会影响性能,下面看一下调用方式:
    MyDialog myDialog = new MyDialog(this, R.style.myDialog, R.layout.my_dialog);
    myDialog.show();

  • 创建与销毁时的细节
      而这两种创建方法却有一点不同:onCreate( )创建的Dialog的逻辑处理需在show( )之后,相当于调用了show( )之后才会真正的创建出来,而通过构造方法创建的Dialog的show( )可以放在最后的位置,相当于一个整体的展示。
      而一般自定义的Dialog由于个性化需求较大,所以其style一般需要简单定制一下:

    <!-- 自定义Dialog的Theme定义 -->
    <style name="myDialog" parent="android:Theme.Dialog">       
          <item name="android:windowFrame">@null</item>
          <item name="android:windowNoTitle">true</item>
          <item name="android:windowIsFloating">true</item>
          <item name="android:windowContentOverlay">@null</item>
          <item name="android:windowBackground">@android:color/transparent</item>
    </style>
    

对于Dialog的销毁,有两种方法:dismiss( )和cancel( ),仔细的童鞋会发现cancel( )其实中调用了dismiss( )的,只是加了一个对mCancelMessage的判断,看下该源码:
public void setOnCancelListener(final OnCancelListener listener) {
if (listener != null) {
mCancelMessage = mListenersHandler.obtainMessage(CANCEL, listener);
} else {
mCancelMessage = null;
}
}

  public void setCancelMessage(final Message msg) {  
        mCancelMessage = msg;  
  }  

也就是说,如果调用了setOnCancelListener,这个mCancelMessage变量有作用,否则dismiss( )和cancel( )等同。
  此外,还可以对Dialog的透明,展出方向(上下左右)等进行设置,如下图便是一个底部弹出的Dialog:


dialog_bottom.png

Dialog的回调监听

Keep your eyes on......重点来了,关于Dialog最重要的除了展示效果外无非就是其回调的设置了,而其回调方式有很多种,有把Dialog当作Activity的方式来处理的,有使用DialogInterface来处理的,有把Dialog当作View的方式来处理,还有使用loop/handler的方式来处理的......总之有很多种方式,具体的逻辑与效率总有优劣之处,请大家自己掌控~下面来一一介绍:

  • Dialog VS Handler
      使用Handler的方式,可以通过构造函数传递参数,然后在消息队列中捕获并处理,这种方式比较简洁,在调用的位置加上:
    myDialog.setHandler(mHandler);
      在Dialog中:
    private void setHandler(mHandler) {
    this.mHandler = mHandler;
    }
      然后便是在Activity中通过handleMessage(msg)方法进行处理,当然可以在此进行优化,使用静态内部类InnerHandler + 弱引用WeakReference的方式(附参考链接),将具体的回调设置在此处处理。

  • Dialog VS DialogFragment
      DialogFragment集Dialog与Fragment于一身,貌似很强大。DialogFragment配合DialogInterface使用比较切合,在调用的位置:
    MyDialog myDialog = new MyDialog(this, R.style.myDialog, R.layout.my_dialog);
    FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
    myDialog.show(fragmentTransaction, null);
      而此时的MyDialog需要继承自DialogFragment,即按Fragment的方式来初始化Dialog,在我们需要回调处理的地方:
    MyActivity instance = (MyActivity) getActivity();
    instance.onDialogBack();
      即相当于回调了Activity中的onDialogBack( )方法:
    public void onDialogBack() {
    // 具体逻辑
    }

  • Dialog VS setListener( )
      看过我以前自定义View的童鞋一定也了解其实现思路,即在Dialog中加入一些setListener( )方法,然后在实例化后直接该用内部的setListener( )方法即可,两种创建方式都可行,只需将myDialog对象调用MyDialog中的方法:
    Dialog中:
    public void setMyVisibility(boolean visibility) {
    mView.setVisibility(visibility ? View.VISIBLE : View.GONE);
    }

    Activity中:
    myDialog.setMyVisibility(true);
    
  • Example
      结合了以上各个思路,下面给出两种可以拿来封装的样式,底部弹出的Dialog与居中弹出的Dialog:

    dialog_style1.png

    dialog_style2.png

AlertDialog初探

AlertDialog是一种极其个性化的Dialog,相当于一个样式封装好的Dialog,便于调用,默认的风格便是一种最简单的处理,当然也可以自定义,在其初始化时使用了强大且神奇的Build-建造者模式
  AlertDialog的构造方法全部是Protected的,所以不能直接通过new一个AlertDialog来创建出一个AlertDialog,需要用到AlertDialog.Builder中的create()方法:

  Dialog alertDialog = new AlertDialog.Builder(this)   
        .setTitle("Title")                                    // 标题
        .setMessage("Content")                                // 内容
        .setPositiveButton("OK", this)                        // Positive Button
        .setNegativeButton("Cancel", this);                   // Negative Button
        .setNeutralButton("Neutral", this);                   // Neutral Button
        .setItems(new String[] {"A", "B", "C"}, this);        // 条目
        .setIcon(R.drawable.ic_launcher)                      // 图标
        .create();
  alertDialog.show(); 

因为是建造者模式,所以上面这些Build的内容都是Optional的,而其中若想添加监听,可以这样实现:

   .setPositiveButton("OK", new DialogInterface.OnClickListener() {                                                    
        @Override                     
        public void onClick(DialogInterface dialog, int which) {                         
              // TODO Auto-generated method stub                      
        }                 
  })

针对于其中的setItem( ),也可以有这样的扩展,效果如下:

   .setSingleChoiceItems(new String[] {"A", "B", "C", "D"}, 0, this);       // 单选条目      
single_item.png
    .setMultiChoiceItems(new String[] {"A", "B", "C", "D"}, 0, this);        // 多选条目
multi_item.png

  此外值得一说的是,若是继承了DialogFragment,则在使用了AlertDialog的setButton( )后,可以重写父类的onClick( )方法直接回调,感觉很强大的样子:

   @Override
   public void onClick(DialogInterface dialog, int which) {    
          switch(which) {
                 case AlertDialog.BUTTON_NEGATIVE:
                        // TODO
                        break;
                 case AlertDialog.BUTTON_NEGATIVE:
                        // TODO
                        break;
                 // TODO
          }    
   }

当然AlertDialog还有很多方法,这里就不一一介绍了,其中pedant大神对其有仔细研究,有兴趣的可以去看看他的sweet-alert-dialog。我以前也参考过一些用例,简单写过一个AlertDialogDemo,大家也可以参考一下~

尾声

关于Dialog这部分自己是一点一点踩过了许多坑,然后总结了许多种实现方式从而最终Get到其强大之处,由此写来给大家分享一下小小的心得~

最后再来看一张很好看的效果~


dialog_sweet.gif
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,401评论 25 707
  • AlertDialog是经常使用控件,今天我来分析一下源码。 android新手经常会用遇到几个问题? 窗体溢出的...
    一航jason阅读 8,431评论 2 1
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,350评论 0 17
  • footer定位底部起初我的打算是用绝对定位,脱离文本流,参照浏览器左上角定位,设置TRBL作为原点此时我的代码如...
    DecadeHeart阅读 8,738评论 0 0
  • Vinil内心阅读 320评论 0 1