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閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,806評論 25 708
  • AlertDialog是經常使用控件,今天我來分析一下源碼。 android新手經常會用遇到幾個問題? 窗體溢出的...
    一航jason閱讀 8,503評論 2 1
  • ¥開啟¥ 【iAPP實現進入界面執行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程,因...
    小菜c閱讀 6,497評論 0 17
  • footer定位底部起初我的打算是用絕對定位,脫離文本流,參照瀏覽器左上角定位,設置TRBL作為原點此時我的代碼如...
    DecadeHeart閱讀 8,820評論 0 0
  • Vinil內心閱讀 325評論 0 1