2010/10/13 16:41

Android - Custom Dialog, AlertDialog




이번에는 Android 환경에서 Dialog로 좀 더 다양한 활용을 위한 Custom Dialog 제작에 관해 알아보겠습니다.

이전 포스트(http://overoid.tistory.com/28)에서 Android에서 제공하는 기본 Dialog에 대해 설명하였으니, 관심 있으시면 그 부분도 살펴보시기 바랍니다.

Custom Dialog를 만드는 가장 쉬운 방법은 별도의 레이아웃을 xml로 작성하시고, AlertDialog.builder에서 addView로 생성한 레이아웃을 등록하시는게 가장 쉬운 방법입니다.

Custom Dialog Sample1



TextView 2개와 입력 받을수 있는 EditText 2개로 구성되어 있는 Dialog입니다.

먼저 XML Layout을 생성합니다.

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

  xmlns:android="http://schemas.android.com/apk/res/android"

  android:layout_width="wrap_content"

  android:id="@+id/layout_root"

  android:layout_height="wrap_content"

  android:orientation="vertical">

  <LinearLayout android:orientation="horizontal"

                  android:layout_width="fill_parent"

                  android:layout_height="fill_parent"

                  android:layout_marginTop="5dip">

                 

        <TextView android:layout_width="70dip"

                      android:layout_height="wrap_content"

                      android:layout_gravity="center_vertical"

                      android:layout_marginLeft="10dip"

                      android:textColor="#FFFFFF"

                      android:textSize="14sp"

                      android:text="Name"/>

                     

        <EditText  android:id="@+id/dlgDisplayName"

                   android:layout_width="wrap_content"

                   android:layout_height="38dip"

                   android:textSize="12sp"

                   android:layout_weight="1"

                   android:layout_marginLeft="10dip"

                   android:layout_marginRight="10dip"

                   android:hint="Name"

                   />

    </LinearLayout>              

   

 

    <LinearLayout android:orientation="horizontal"

             android:layout_width="fill_parent"

             android:layout_height="fill_parent">        

         <TextView android:layout_width="70dip"

                      android:layout_height="wrap_content"

                      android:layout_gravity="center_vertical"

                      android:layout_marginLeft="10dip"

                      android:textColor="#FFFFFF"

                      android:textSize="14sp"

                      android:text="Phone Number"/>     

                     

         <EditText  android:id="@+id/dlgPhoneNumber"

                   android:layout_height="38dip"

                   android:layout_weight="1"

                   android:layout_width="fill_parent"

                   android:layout_marginLeft="10dip"

                   android:layout_marginRight="10dip"

                   android:inputType="phone"

                   android:hint="Phone Number"

                   android:textSize="12sp"/>

     </LinearLayout>      

                            

 

</LinearLayout>


 


Dialog
Content View로 위에 정의해 둔 Layout을 설정하고 각 UI 항목을 정의하면 됩니다.


AlertDialog.Builder builder;

AlertDialog alertDialog;

 

Context mContext = MainActivity.this;

LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(LAYOUT_INFLATER_SERVICE);

View layout = inflater.inflate(R.layout.custom_dialog1,(ViewGroup) findViewById(R.id.layout_root));

 

final EditText name = (EditText)layout.findViewById(R.id.dlgDisplayName);

final EditText number = (EditText)layout.findViewById(R.id.dlgPhoneNumber);

 

builder = new AlertDialog.Builder(mContext);

builder.setView(layout);

builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {

   

    @Override

    public void onClick(DialogInterface dialog, int which) {

        if(TextUtils.isEmpty(name.getText())) {

            Toast.makeText(getApplicationContext(), "name is empty", Toast.LENGTH_SHORT).show();

        } else {

            dialog.dismiss();

        }

    }

});

alertDialog = builder.create();

XML Layout을 Inflating시키기 위해서는 LayoutInflater를 얻어와 inflate(int, ViewGroup)을 호출해야 합니다. 여기서 첫번째 파라미터는 Layout Resource ID이고 두번째 파라미터는 Root View의 ID입니다. Inflate 호출후에는 EditText에 대한 내용을 정의할 수 있습니다.

그 다음 AlertDialog.Builder를 인스턴스화하고 setView(View)를 통해 해당 Dialog를 위한 Inflated Layout을 설정한다.

쉽습니다. 하지만, 위 Dialog에서 사용자의 입력값을 받고 확인 버튼 클릭시 값의 설정 유무를 체크한 후 값이 있는 경우에만 창을 닫아주고 싶은데.. 기본적인 AlertDialog Builder에서 제공하는 버튼을 사용하는 경우에는 무조건 창이 닫깁니다. 즉, 원하는 바를 할 수 없습니다.

이 문제를 해결하기 위해서 첫번째는 트릭으로, 두번째는 정식 방법으로 해결을 해 보도록 하겠습니다.

Custom Dialog Sample2

위 이슈에 대한 첫번째 해결 방법은 AlertDialog에서 제공하는 set..Button 기능을 사용하지 않고 Layout xml.에서 직접 .button을 넣은 후 그걸로 처리하는 방법입니다.

먼저, 버튼 부분이 추가된 layout xml 입니다.

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

  xmlns:android="http://schemas.android.com/apk/res/android"

  android:layout_width="wrap_content"

  android:id="@+id/layout_root"

  android:orientation="vertical"

  android:layout_height="wrap_content"

  >

  <LinearLayout android:orientation="horizontal"

                  android:layout_width="fill_parent"

                  android:layout_height="fill_parent"

                  android:layout_marginTop="5dip">

                 

        <TextView android:layout_width="80dip"

                      android:layout_height="wrap_content"

                      android:layout_gravity="center_vertical"

                      android:layout_marginLeft="10dip"

                      android:textColor="#FFFFFF"

                      android:textSize="14sp"

                      android:text="Name"/>

                     

        <EditText  android:id="@+id/dlgDisplayName"

                   android:layout_width="wrap_content"

                   android:layout_height="38dip"

                   android:textSize="12sp"

                   android:layout_weight="1"

                   android:layout_marginLeft="10dip"

                   android:layout_marginRight="10dip"

                   android:hint="Name"

                   />

    </LinearLayout>              

   

 

    <LinearLayout android:orientation="horizontal"

             android:layout_width="fill_parent"

             android:layout_height="fill_parent">        

         <TextView android:layout_width="80dip"

                      android:layout_height="wrap_content"

                      android:layout_gravity="center_vertical"

                      android:layout_marginLeft="10dip"

                      android:textColor="#FFFFFF"

                      android:textSize="14sp"

                      android:text="Phone Number"/>     

                     

         <EditText  android:id="@+id/dlgPhoneNumber"

                   android:layout_height="38dip"

                   android:layout_weight="1"

                   android:layout_width="fill_parent"

                   android:layout_marginLeft="10dip"

                   android:layout_marginRight="10dip"

                   android:inputType="phone"

                   android:hint="Phone Number"

                   android:textSize="12sp"/>

     </LinearLayout>      

                            

    

 

    <LinearLayout android:id="@+id/buttonPanel"

        android:layout_width="fill_parent"

        android:layout_height="wrap_content"

        android:minHeight="54dip"

        android:orientation="vertical"

        android:background="#aaa" >    

        <LinearLayout

            android:layout_width="fill_parent"

            android:layout_height="wrap_content"

            android:orientation="horizontal"

            android:paddingTop="4dip"

            android:paddingLeft="2dip"

            android:paddingRight="2dip" >

            <LinearLayout android:id="@+id/leftSpacer"

                android:layout_weight="0.25"

                android:layout_width="0dip"

                android:layout_height="wrap_content"

                android:orientation="horizontal"

                android:visibility="gone" />

            <Button android:id="@+id/button1"

                android:layout_width="0dip"

                android:layout_gravity="left"

                android:layout_weight="1"

                android:maxLines="2"

                android:text="OK"

                android:layout_height="wrap_content" />

            <Button android:id="@+id/button2"

                android:layout_width="0dip"

                android:layout_gravity="right"

                android:layout_weight="1"

                android:maxLines="2"

                android:text="Cancel"

                android:layout_height="wrap_content" />

            <LinearLayout android:id="@+id/rightSpacer"

                android:layout_width="0dip"

                android:layout_weight="0.25"

                android:layout_height="wrap_content"

                android:orientation="horizontal"

                android:visibility="gone" />

        </LinearLayout>

     </LinearLayout>                  

          

 

 

</LinearLayout>



기존 Sample1에 리소스 xml에 버튼 부분을 더 추가했습니다.


AlertDialog.Builder builder;

AlertDialog alertDialog;

 

Context mContext = MainActivity.this;

LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(LAYOUT_INFLATER_SERVICE);

View layout = inflater.inflate(R.layout.custom_dialog2,(ViewGroup) findViewById(R.id.layout_root));

 

final EditText name = (EditText)layout.findViewById(R.id.dlgDisplayName);

final EditText number = (EditText)layout.findViewById(R.id.dlgPhoneNumber);

final Button okButton = (Button)layout.findViewById(R.id.button1);

okButton.setOnClickListener(new OnClickListener() {

   

    @Override

    public void onClick(View v) {

        if(TextUtils.isEmpty(name.getText())) {

            Toast.makeText(getApplicationContext(), "name is empty", Toast.LENGTH_SHORT).show();

        } else {

            Toast.makeText(getApplicationContext(), "name is ok", Toast.LENGTH_SHORT).show();

            customDialogInstance.dismiss();

        }

    }

});

 

final Button cancelButton = (Button)layout.findViewById(R.id.button2);

cancelButton.setOnClickListener(new OnClickListener() {

   

    @Override

    public void onClick(View v) {

        customDialogInstance.dismiss();               

    }

});

 

builder = new AlertDialog.Builder(mContext);

builder.setView(layout);

alertDialog = builder.create();

기존 코드와 비슷하지만, 처음 Sample1은 setPositiveButton 메소드를 사용해서 버튼을 추가했지만, 이번 샘플은 직접 버튼을 XML에서 불러들여 okButton에 직접 클릭이벤트 처리를 추가했습니다. 편의상 EditText 값 체크는 하나만 처리했습니다.



결과 화면입니다.

Name 부분에 값을 설정하지 않고 OK 버튼을 누르면 창이 닫히질 않은 상태로 Toast 메시지가 나타납니다. 원하는 바는 얻었으나, 왠일인지 버튼 아래로 약간의 공간이 생성됩니다. 버튼이 Dialog 아래쪽에 완전히 붙지를 않는UI적인 문제가 있군요.

Custom Dialog Sample3

끝으로 위 이슈를 해결하기 위해서 AlertDialog.builder를 사용하지 않고, Dialog Class에서 직접 상속을 받아서 Custom Dialog를 만들어 보도록 하겠습니다.

XML layout 코드는 Sample2와 동일한 xml을 사용하도록 하겠습니다.

먼저 Dialog 화면의 대한 클래스 코드입니다.

class CustomDialog3 extends Dialog implements OnClickListener {

    EditText name;

    EditText number;

    Button okButton;

    Button cancelButton;

    Context mContext;

   

    public CustomDialog3(Context context) {

        super(context);

        mContext = context;

        /** 'Window.FEATURE_NO_TITLE' - Used to hide the title */

        requestWindowFeature(Window.FEATURE_NO_TITLE); 

        setContentView(R.layout.custom_dialog2);

       

        name = (EditText)findViewById(R.id.dlgDisplayName);

        number = (EditText)findViewById(R.id.dlgPhoneNumber);

        okButton = (Button)findViewById(R.id.button1);

        cancelButton = (Button)findViewById(R.id.button2);

       

        okButton.setOnClickListener(this);

        cancelButton.setOnClickListener(this);

    }

 

    @Override

    public void onClick(View v) {

       if(v == okButton) {

           if(TextUtils.isEmpty(name.getText())) {

               Toast.makeText(mContext, "name is empty", Toast.LENGTH_SHORT).show();

           } else {

               Toast.makeText(mContext, "name is ok", Toast.LENGTH_SHORT).show();

                   dismiss();

               }

              

           } else if(v == cancelButton) {

               dismiss();

           }

        }

 

    }



Dialog 클래스에서 직접 상속을 받았으며, 생성자에서 requestWindowFeature(Window.FEATURE_NO_TITLE); 메소드를 사용하여 Dialog에 타이틀이 나오지 않도록 처리했습니다.

코드를 보시면 아시겠지만, 일반 Activity 코드 작성과 별반 다를게 없습니다. 생성자에서 setContentView()를 호출해 xml을 로드한 후 각 UI에 대해서 Event Handler를 작성하시면 됩니다.



버튼 부분의 디자인도 창의 하단에 딱 붙는게 UI적인 문제도 없고, Text값 Validation 체크를 통해 창을 제어할 수 있어서 원하는 바를 다 할 수 있습니다.

다만, AlertDialog.builder를 통해 Dialog를 생성하시면 화면에 보이는 창의 가로크기는 모두 동일합니다. Layout xml에 작게 설정되어 있더라도 가로 부분을 늘려서 동일하게 맞춥니다. 그러나, Dialog Class를 상속해서 만든 경우 사용자가 작성한 xml에 따라 Dialog의 가로창 사이즈가 변하게 됩니다. 그렇지만, 가로 최대값은 메인 화면의 90%를 넘지는 않는 것 같습니다.

아래는 Custom Dialog Sample을 테스트 할 수 있는 전체 코드입니다.

public class MainActivity extends Activity {

   

    static final int DIALOG_CUSTOM1 = 0;

    static final int DIALOG_CUSTOM2 = 1;

    static final int DIALOG_CUSTOM3 = 2;

   

    Button customDialog1;

    Button customDialog2;

    Dialog customDialogInstance;

   

    Button customDialog3;

   

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

       

        customDialog1 = (Button)findViewById(R.id.customDialog1);

        customDialog1.setOnClickListener(new OnClickListener() {

           

            @Override

            public void onClick(View v) {

                showDialog(DIALOG_CUSTOM1);

            }

        });

       

        customDialog2 = (Button)findViewById(R.id.customDialog2);

        customDialog2.setOnClickListener(new OnClickListener() {

           

            @Override

            public void onClick(View v) {

                showDialog(DIALOG_CUSTOM2);

            }

        });

       

        customDialog3 = (Button)findViewById(R.id.customDialog3);

        customDialog3.setOnClickListener(new OnClickListener() {

           

            @Override

            public void onClick(View v) {

                CustomDialog3 customDialog3 = new CustomDialog3(MainActivity.this);

                customDialog3.show();

            }

        });

    }

 

    @Override

    protected Dialog onCreateDialog(int id) {

        Dialog dialog;

        switch(id) {

            case DIALOG_CUSTOM1:

                dialog = getCustomDialog1();

                break;

            case DIALOG_CUSTOM2:

                dialog = getCustomDialog2();

                customDialogInstance = dialog;

                break;

            default:

                dialog = null;

        }

        return dialog;

    }

   

    private Dialog getCustomDialog1() {

        AlertDialog.Builder builder;

        AlertDialog alertDialog;

       

        Context mContext = MainActivity.this;

        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(LAYOUT_INFLATER_SERVICE);

        View layout = inflater.inflate(R.layout.custom_dialog1,(ViewGroup) findViewById(R.id.layout_root));

       

        final EditText name = (EditText)layout.findViewById(R.id.dlgDisplayName);

        final EditText number = (EditText)layout.findViewById(R.id.dlgPhoneNumber);

       

        builder = new AlertDialog.Builder(mContext);

        builder.setView(layout);

        builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {

           

            @Override

            public void onClick(DialogInterface dialog, int which) {

                if(TextUtils.isEmpty(name.getText())) {

                    Toast.makeText(getApplicationContext(), "name is empty", Toast.LENGTH_SHORT).show();

                } else {

                    dialog.dismiss();

                }

            }

        });

        alertDialog = builder.create();

       

        return alertDialog;

    }

   

    private Dialog getCustomDialog2() {

        AlertDialog.Builder builder;

        AlertDialog alertDialog;

       

        Context mContext = MainActivity.this;

        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(LAYOUT_INFLATER_SERVICE);

        View layout = inflater.inflate(R.layout.custom_dialog2,(ViewGroup) findViewById(R.id.layout_root));

       

        final EditText name = (EditText)layout.findViewById(R.id.dlgDisplayName);

        final EditText number = (EditText)layout.findViewById(R.id.dlgPhoneNumber);

        final Button okButton = (Button)layout.findViewById(R.id.button1);

        okButton.setOnClickListener(new OnClickListener() {

           

            @Override

            public void onClick(View v) {

                if(TextUtils.isEmpty(name.getText())) {

                    Toast.makeText(getApplicationContext(), "name is empty", Toast.LENGTH_SHORT).show();

                } else {

                    Toast.makeText(getApplicationContext(), "name is ok", Toast.LENGTH_SHORT).show();

                    customDialogInstance.dismiss();

                }

            }

        });

       

        final Button cancelButton = (Button)layout.findViewById(R.id.button2);

        cancelButton.setOnClickListener(new OnClickListener() {

           

            @Override

            public void onClick(View v) {

                customDialogInstance.dismiss();               

            }

        });

       

        builder = new AlertDialog.Builder(mContext);

        builder.setView(layout);

        alertDialog = builder.create();

                

        return alertDialog;

    }

   

   

    class CustomDialog3 extends Dialog implements OnClickListener {

        EditText name;

        EditText number;

        Button okButton;

        Button cancelButton;

        Context mContext;

       

        public CustomDialog3(Context context) {

            super(context);

            mContext = context;

            /** 'Window.FEATURE_NO_TITLE' - Used to hide the title */

            requestWindowFeature(Window.FEATURE_NO_TITLE); 

            setContentView(R.layout.custom_dialog2);

           

            name = (EditText)findViewById(R.id.dlgDisplayName);

            number = (EditText)findViewById(R.id.dlgPhoneNumber);

            okButton = (Button)findViewById(R.id.button1);

            cancelButton = (Button)findViewById(R.id.button2);

           

            okButton.setOnClickListener(this);

            cancelButton.setOnClickListener(this);

        }

   

        @Override

        public void onClick(View v) {

           if(v == okButton) {

               if(TextUtils.isEmpty(name.getText())) {

                   Toast.makeText(mContext, "name is empty", Toast.LENGTH_SHORT).show();

               } else {

                   Toast.makeText(mContext, "name is ok", Toast.LENGTH_SHORT).show();

                       dismiss();

                   }

                  

               } else if(v == cancelButton) {

                   dismiss();

               }

            }

    

        }

}



리소스 및 소스가 전부 포함된 소스코드 첨부합니다.


 


 

저작자 표시 비영리 변경 금지
Trackback 0 Comment 5
  1. ^^ 2011/01/21 18:16 address edit & del reply

    우와. 훌륭한 포스트 고맙습니다!^^ 덕분에 많은 도움이 되었어요~

  2. 늘근초보 2011/02/13 22:38 address edit & del reply

    찾고있던 자료입니다. 고맙습니다.

  3. 소영 2011/09/21 09:13 address edit & del reply

    정말 많은 도움이 되었습니다 ^^ 감사합니다

  4. 도용훈 2011/12/12 13:18 address edit & del reply

    감사합니다^^

  5. moncler 2013/01/04 15:21 address edit & del reply

    Le bilan des inondations qui touchent le centre et l'est des Philippines est monté à cinq morts après un glissement de terrain, http://www.moncleroutletespain.com/ moncler online, selon les autorités, http://www.moncleroutletespain.com/ moncler chaquetas. Un bébé d'un an et une fillette de cinq ans son morts après avoir été ensevelis à la suite d'un glissement de terrain dimanche dans l'?le de Leyte, http://www.moncleroutletespain.com/ moncler españa, dans la région des Visayas, http://www.moncleroutletespain.com/ http://www.moncleroutletespain.com/. Plus de 1000 familles ont d, http://www.moncleroutletespain.com/ moncler outlet? être évacuées durant le week-end, http://www.moncleroutletespain.com/ moncler.Related articles:


    http://cineform.tistory.com/71 http://cineform.tistory.com/71

    http://iomania.kr/18 http://iomania.kr/18



티스토리 툴바