1. 程式人生 > >詳解Dialog(三)——自定義對話方塊檢視及引數傳遞

詳解Dialog(三)——自定義對話方塊檢視及引數傳遞

前言:這篇文章是有關對話方塊的最後一篇了,最近工作是很忙,不過閒下來的時候也不想寫部落格,估計是累過頭了,還好以前定了個任務,每個月必須寫四篇,這才強制自己去一直更新,馬總說過,夢想這東西還是要有的,萬一實現了呢,趁在阿里的兩年,努力!

相關文章:


今天給大家講講有關自定義對話方塊的相關內容,前面兩篇都在在利用系統提供的函式來實現對話方塊,但侷限性太大,當我們想自己定義檢視的時候,就不能利用系統函數了,就需要我們這裡的自定義對話方塊了,有關自定義對話方塊的東東,以前有寫過一篇《android之Dialog相關》,寫的不好,今天給大家重新寫一篇

一、雛形構建

先給大家看下這小節的效果圖:

自定義一個對話方塊,內容是四個ImageView橫排;


1、Dialog佈局

根據上圖的對話方塊樣式,我們看一下Dialog的佈局定義(custom_dialog.xml)

[html] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
  3.     android:id="@+id/log_in_layout"
  4.     android:layout_width="fill_parent"
  5.     android:layout_height
    ="wrap_content"
  6.     android:orientation="horizontal">
  7.     <ImageView
  8.         android:layout_width="match_parent"
  9.         android:layout_height="100dip"
  10.         android:src="@drawable/animal1"
  11.         android:clickable="true"
  12.         android:layout_weight="1"/>
  13.     <ImageView
  14.         android:layout_width
    ="match_parent"
  15.         android:layout_height="100dip"
  16.         android:src="@drawable/animal2"
  17.         android:clickable="true"
  18.         android:layout_weight="1"/>
  19.     <ImageView
  20.         android:layout_width="match_parent"
  21.         android:layout_height="100dip"
  22.         android:src="@drawable/animal3"
  23.         android:clickable="true"
  24.         android:layout_weight="1"/>
  25.     <ImageView
  26.         android:layout_width="match_parent"
  27.         android:layout_height="100dip"
  28.         android:src="@drawable/animal4"
  29.         android:clickable="true"
  30.         android:layout_weight="1"/>
  31. </LinearLayout>
<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/log_in_layout"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="100dip"
        android:src="@drawable/animal1"
        android:clickable="true"
        android:layout_weight="1"/>
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="100dip"
        android:src="@drawable/animal2"
        android:clickable="true"
        android:layout_weight="1"/>
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="100dip"
        android:src="@drawable/animal3"
        android:clickable="true"
        android:layout_weight="1"/>
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="100dip"
        android:src="@drawable/animal4"
        android:clickable="true"
        android:layout_weight="1"/>

</LinearLayout>

2、從Dialog派生對話方塊類

有關建構函式:
有三種建構函式,現在我這裡使用重寫了兩個,這裡只需要使用第一個,即傳進去context即可;

有關OnCreate() 在OnCreate()時,利用LayoutInflater獲取我們對話方塊的View,然後利用SetContentView指定為我們CustomDialog類的佈局。 [java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. publicclass CustomDialog extends Dialog {  
  2.     Context mContext;  
  3.     public CustomDialog (Context context){  
  4.         super(context);  
  5.         mContext = context;  
  6.     }  
  7.     public CustomDialog(Context context, int theme) {  
  8.         super(context, theme);  
  9.         mContext = context;  
  10.     }  
  11.     @Override
  12.     protectedvoid onCreate(Bundle savedInstanceState) {  
  13.         super.onCreate(savedInstanceState);  
  14.         LayoutInflater inflater = (LayoutInflater) mContext  
  15.                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  16.         View layout = inflater.inflate(R.layout.custom_dialog, null);  
  17.         this.setContentView(layout);  
  18.     }  
  19. }  
public class CustomDialog extends Dialog {
    Context mContext;
    public CustomDialog (Context context){
        super(context);
        mContext = context;
    }
    public CustomDialog(Context context, int theme) {
        super(context, theme);
        mContext = context;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        LayoutInflater inflater = (LayoutInflater) mContext
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View layout = inflater.inflate(R.layout.custom_dialog, null);
        this.setContentView(layout);
    }
}

3、主函式(MainActivity)

在MainActivity中,我們寫一個Button,當用戶點選的時候彈出我們自定義的對話方塊例項

MainActivity的佈局:(activity_main.xml)

[html] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. <RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
  2.     xmlns:tools="http://schemas.android.com/tools"
  3.     android:layout_width="match_parent"
  4.     android:layout_height="match_parent"
  5.     tools:context=".MainActivity">
  6.     <Button
  7.         android:id="@+id/btn_pop_dialog"
  8.         android:layout_width="fill_parent"
  9.         android:layout_height="wrap_content"
  10.         android:text="彈出對話方塊"/>
  11. </RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btn_pop_dialog"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="彈出對話方塊"/>

</RelativeLayout>
程式碼中的處理:(MainActivity.java)
在點選Btn的時候,建立CustomDialog類的例項,並顯示 [java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. publicclass MainActivity extends Activity {  
  2.     @Override
  3.     protectedvoid onCreate(Bundle savedInstanceState) {  
  4.         super.onCreate(savedInstanceState);  
  5.         setContentView(R.layout.activity_main);  
  6.         Button btn = (Button)findViewById(R.id.btn_pop_dialog);  
  7.         btn.setOnClickListener(new View.OnClickListener() {  
  8.             @Override
  9.             publicvoid onClick(View view) {  
  10.                 CustomDialog dialog = new  CustomDialog(MainActivity.this);  
  11.                 dialog.show();  
  12.             }  
  13.         });  
  14.     }  
  15. }  
public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button btn = (Button)findViewById(R.id.btn_pop_dialog);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                CustomDialog dialog = new  CustomDialog(MainActivity.this);
                dialog.show();
            }
        });
    }

}
這部分原始碼在文章最底部給出;

二、定義對話方塊樣式

這裡再回頭看看上面彈出的對話方塊:


在佈局中,我們只定義了一個水平佈局,裡面放了四個ImageView,即那四個小動物,那上面那一坨是哪來的呢(我用紅筆圈出來的那塊)?能不能去掉?這節,我們就說說有關樣式的問題。

第一個問題,只所有上面那一坨,是因為我們沒有指定對話方塊樣式,系統會使用預設樣式,那一坨就是標題欄。

要去掉的話,我們就需要自定義樣式。

1、自定義樣式

我們的樣式是寫在res/values資料夾下的style.xml檔案中的,如圖,如果沒有style.xml,自已新建一個,位置如圖:


這裡定義的樣式程式碼是這樣的:

[html] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. <stylename="dialog"parent="android:Theme.Dialog">
  2.     <itemname="android:windowFrame">@null</item>
  3.     <itemname="android:windowIsFloating">true</item>
  4.     <itemname="android:windowContentOverlay">@null</item>
  5.     <itemname="android:windowNoTitle">true</item>
  6. </style>
    <style name="dialog" parent="android:Theme.Dialog">
        <item name="android:windowFrame">@null</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:windowContentOverlay">@null</item>
        <item name="android:windowNoTitle">true</item>
    </style>
先對這幾個引數解釋一下:

android:windowFrame:介面對應的前景圖片;
android:windowIsFloating:表示浮在螢幕上的,如果在這裡使用了,整個layout就會在 螢幕中心,相當於浮在螢幕上,所以這個只適用於dialog
android:windowContentOverlay:表示標題欄的陰影部分的樣式,使用圖片或者顏色
android:windowNoTitle:標題欄是否隱藏,這就是我們上面顯示的標題欄

有關樣式的內容很多很雜,這裡有篇文章大家可以參考下《Andriod中Style/Theme原理以及Activity介面檔案選取過程淺析》,有機會給大家總結一下有關樣式和主題的內容,到時再細講,這裡不是本篇的重點,就不再細講了。

2、使用樣式

方法一:

這裡有兩種方法來使用樣式,主要還是利用建構函式,還記得我們上面說過,對話方塊的兩個建構函式:

[java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. publicclass CustomDialog extends Dialog {  
  2.     Context mContext;  
  3.     public CustomDialog(Context context) {  
  4.         super(context);  
  5.         mContext = context;  
  6.     }  
  7.     public CustomDialog(Context context, int theme) {  
  8.         super(context, theme);  
  9.         mContext = context;  
  10.     }  
  11.     …………  
  12. }  
public class CustomDialog extends Dialog {
    Context mContext;

    public CustomDialog(Context context) {
        super(context);
        mContext = context;
    }

    public CustomDialog(Context context, int theme) {
        super(context, theme);
        mContext = context;
    }

    …………
}
看第二個就是我們的Theme樣式,所以第一種使用樣式的方法是在構造時直接將樣式傳進來,如果這樣使用,那我們在構造對話方塊時應該是這樣的“: [java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. publicclass MainActivity extends Activity {  
  2.     @Override
  3.     protectedvoid onCreate(Bundle savedInstanceState) {  
  4.         super.onCreate(savedInstanceState);  
  5.         setContentView(R.layout.activity_main);  
  6.         Button btn = (Button)findViewById(R.id.btn_pop_dialog);  
  7.         btn.setOnClickListener(new View.OnClickListener() {  
  8.             @Override
  9.             publicvoid onClick(View view) {  
  10.                 CustomDialog dialog = new  CustomDialog(MainActivity.this,R.style.dialog);  
  11.                 dialog.show();  
  12.             }  
  13.         });  
  14.     }  
  15. }  
public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button btn = (Button)findViewById(R.id.btn_pop_dialog);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                CustomDialog dialog = new  CustomDialog(MainActivity.this,R.style.dialog);
                dialog.show();
            }
        });
    }
}
即在新建CustomDialog例項時,第二個引數傳進來我們上面定義的樣式。

方法二:

第二種方法,就是我們在構造時,不讓別人傳樣式,只讓傳進來Context,但在內部,我們利用Super(context,theme)來指定樣式,程式碼如下:

[java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. publicclass CustomDialog extends Dialog {  
  2.     Context mContext;  
  3.     public CustomDialog(Context context) {  
  4.         super(context,R.style.dialog);  
  5.         mContext = context;  
  6.     }  
  7.     …………  
  8. }  
public class CustomDialog extends Dialog {
    Context mContext;

    public CustomDialog(Context context) {
        super(context,R.style.dialog);
        mContext = context;
    }

    …………
}
這種情況一般用在我們封裝程式碼時,不讓其它人在外部改變我們的樣式時使用的。
無論使用哪種方法,我們就已經指定我我們的對話方塊樣式,現在的效果是這樣的:


看到了沒,上面的標題欄沒了,下面再看看有關引數傳遞的問題

三、引數傳遞

這裡涉及到兩個問題:傳進去和傳出來;

傳進去:下面有兩個按鈕,當用戶點第一個按鈕時,在對話方塊中顯示"From btn 1",如果使用者點選第二個按鈕,在對話方塊中顯示“From btn 2”

傳出來:這四個圖片都是可以點選的,當用戶點選任何一個圖片,把它的ID傳出來,並設定到我們的MainActivity中;

這裡為了好理解,更改了對話方塊的佈局,將水平佈局改成了垂直佈局。並且在MainActiviy下面加了一個ImageView.


1、傳進去

往對話方塊裡傳引數,一般就是利用建構函式來完成的,很簡單,即在對話方塊的建構函式上加上我們自己想要傳的引數,這裡我們需要額外傳一個字串,來表示從哪個BTN來的。

所以對話方塊的建構函式就變成了這樣:

[java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. publicclass CustomDialog extends Dialog{  
  2.     private Context mContext;  
  3.     private String mStr;  
  4.     public CustomDialog(Context context, String str, int theme) {  
  5.         super(context, theme);  
  6.         mContext = context;  
  7.         mStr = str;  
  8.     }  
  9.     …………  
  10. }  
public class CustomDialog extends Dialog{
    private Context mContext;
    private String mStr;

    public CustomDialog(Context context, String str, int theme) {
        super(context, theme);
        mContext = context;
        mStr = str;
    }

    …………
}
在使用的時候,也很簡單: [java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. Button btn2 = (Button)findViewById(R.id.btn_pop_dialog_2);  
  2. btn2.setOnClickListener(new View.OnClickListener() {  
  3.     @Override
  4.     publicvoid onClick(View view) {  
  5.         CustomDialog dialog = new  CustomDialog(MainActivity.this,"From btn 2",R.style.dialog);  
  6.         dialog.show();  
  7.     }  
  8. });  
Button btn2 = (Button)findViewById(R.id.btn_pop_dialog_2);
btn2.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        CustomDialog dialog = new  CustomDialog(MainActivity.this,"From btn 2",R.style.dialog);
        dialog.show();
    }
});
直接利用建構函式傳參即可

2、傳出來

利用建構函式傳引數大家都知道,但要怎麼把使用者的操作資訊傳出來就不是那麼簡單了,這裡就要利用回撥來實現了!

回撥函式的使用主要依照下面這些步驟:

在對話方塊中:

[java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. publicclass CustomDialog extends Dialog {  
  2.     // 利用interface來構造一個回撥函式
  3.     publicinterface ICustomDialogEventListener {  
  4.         publicvoid customDialogEvent(int valueYouWantToSendBackToTheActivity);  
  5.     }  
  6.     private ICustomDialogEventListener onCustomDialogEventListener;  
  7.     // 在建構函式中,設定進去回撥函式
  8.     public CustomDialog(Context context,  
  9.                         ICustomDialogEventListener onCustomDialogEventListener) {  
  10.         super(context);  
  11.         this.onCustomDialogEventListener = onCustomDialogEventListener;  
  12.     }  
  13.     //當你想把值傳回去的時候,呼叫回撥函式將值設定進去
  14.     @Override
  15.     publicvoid onCreate(Bundle savedInstanceState)  
  16.     {  
  17.         Button btnOk = (Button) findViewById(R.id.customDialogButton);  
  18.         btnOk.setOnClickListener( new Button.OnClickListener()  
  19.         {  
  20.             publicvoid onClick(View v) {  
  21.                 onCustomDialogEventListener.customDialogEvent(valueYouWantToSendBackToTheActivity);  
  22.                 dismiss();  
  23.             }  
  24.         });  
  25.     }  
  26. }  
public class CustomDialog extends Dialog {

    // 利用interface來構造一個回撥函式
    public interface ICustomDialogEventListener {
        public void customDialogEvent(int valueYouWantToSendBackToTheActivity);
    }

    private ICustomDialogEventListener onCustomDialogEventListener;

    // 在建構函式中,設定進去回撥函式
    public CustomDialog(Context context,
                        ICustomDialogEventListener onCustomDialogEventListener) {
        super(context);
        this.onCustomDialogEventListener = onCustomDialogEventListener;
    }

    //當你想把值傳回去的時候,呼叫回撥函式將值設定進去
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        Button btnOk = (Button) findViewById(R.id.customDialogButton);
        btnOk.setOnClickListener( new Button.OnClickListener()
        {
            public void onClick(View v) {
                onCustomDialogEventListener.customDialogEvent(valueYouWantToSendBackToTheActivity);
                dismiss();
            }
        });
    }
}
在構造對話方塊時: [java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. final CustomDialog dialog = new CustomDialog(thisnew ICustomDialogEventListener() {  
  2.     publicvoid customDialogEvent(int value) {  
  3.         //在這裡就獲取到了從對話方塊傳回來的值
  4.     }  
  5. });  
final CustomDialog dialog = new CustomDialog(this, new ICustomDialogEventListener() {
    public void customDialogEvent(int value) {
        //在這裡就獲取到了從對話方塊傳回來的值
    }
});
大致使用流程就是這樣的,下面就把我們上面的效果利用回撥函式實現出來;

首先在對話方塊中新建一個回撥函式(介面),並且在對話方塊的構造方法中把回撥函式傳進來:

[java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. publicclass CustomDialog extends Dialog implements View.OnClickListener {  
  2.     //增加一個回撥函式,用以從外部接收返回值
  3.     publicinterface ICustomDialogEventListener {  
  4.         publicvoid customDialogEvent(int id);  
  5.     }  
  6.     private ICustomDialogEventListener mCustomDialogEventListener;  
  7.     private Context mContext;  
  8.     private String mStr;  
  9.     //把回撥函式傳進來
  10.     public CustomDialog(Context context, String str, ICustomDialogEventListener listener, int theme) {  
  11.         super(context, theme);  
  12.         mContext = context;  
  13.         mStr = str;  
  14.         mCustomDialogEventListener = listener;  
  15.     }  
  16.     …………  
  17. }  
public class CustomDialog extends Dialog implements View.OnClickListener {

    //增加一個回撥函式,用以從外部接收返回值
    public interface ICustomDialogEventListener {
        public void customDialogEvent(int id);
    }

    private ICustomDialogEventListener mCustomDialogEventListener;
    private Context mContext;
    private String mStr;
    //把回撥函式傳進來
    public CustomDialog(Context context, String str, ICustomDialogEventListener listener, int theme) {
        super(context, theme);
        mContext = context;
        mStr = str;
        mCustomDialogEventListener = listener;
    }

    …………
}
然後在OnCreate() 函式中設定ImageView的點選事件,我們將CustomDialog類繼承View.OnClickListener介面,對點選事件統一處理: [java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. privatevoid bindImageClickEvent(View layout){  
  2.     ImageView img1 = (ImageView)layout.findViewById(R.id.dialog_image1);  
  3.     ImageView img2 = (ImageView)layout.findViewById(R.id.dialog_image2);  
  4.     ImageView img3 = (ImageView)layout.findViewById(R.id.dialog_image3);  
  5.     ImageView img4 = (ImageView)layout.findViewById(R.id.dialog_image4);  
  6.     img1.setOnClickListener(this);  
  7.     img2.setOnClickListener(this);  
  8.     img3.setOnClickListener(this);  
  9.     img4.setOnClickListener(this);  
  10. }  
  11. @Override
  12. protectedvoid onCreate(Bundle savedInstanceState) {  
  13.     super.onCreate(savedInstanceState);  
  14.     LayoutInflater inflater = (LayoutInflater) mContext  
  15.             .getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  16.     View layout = inflater.inflate(R.layout.custom_dialog, null);  
  17.     TextView tv = (TextView)layout.findViewById(R.id.dialog_text);  
  18.     tv.setText(mStr);  
  19.     bindImageClickEvent(layout);//繫結ImageView點選事件
  20.     this.setContentView(layout);  
  21. }  
private void bindImageClickEvent(View layout){
    ImageView img1 = (ImageView)layout.findViewById(R.id.dialog_image1);
    ImageView img2 = (ImageView)layout.findViewById(R.id.dialog_image2);
    ImageView img3 = (ImageView)layout.findViewById(R.id.dialog_image3);
    ImageView img4 = (ImageView)layout.findViewById(R.id.dialog_image4);
    img1.setOnClickListener(this);
    img2.setOnClickListener(this);
    img3.setOnClickListener(this);
    img4.setOnClickListener(this);
}
    
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    LayoutInflater inflater = (LayoutInflater) mContext
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    View layout = inflater.inflate(R.layout.custom_dialog, null);

    TextView tv = (TextView)layout.findViewById(R.id.dialog_text);
    tv.setText(mStr);

    bindImageClickEvent(layout);//繫結ImageView點選事件
   
    this.setContentView(layout);
}
在OnCreate()中,首先將傳進來的String字串設定到對話方塊的TextView中;然後繫結各個ImageView的點選事件
然後 就是在OnClick中的處理:

主要是根據使用者當前點選的ImageView的ID,把這個ImageView所用的圖片的DrawableID返回給MainActivity,程式碼如下:

[java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. publicvoid onClick(View view) {  
  2.     int id = view.getId();  
  3.     int drawableID = -1;  
  4.     switch (id){  
  5.