'alertdialog'에 해당되는 글 3건

  1. 2010.10.13 Android - Custom Dialog, AlertDialog (4)
  2. 2010.10.13 Android - Dialog, AlertDialog, ProgressDialog, DatePickerDialog, TimePickerDialog (5)
  3. 2010.09.01 [강좌A12] 안드로이드 실전 개발 - Main UI 소스, ListAdapter (1)
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 4
  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

    감사합니다^^

2010.10.13 12:46

Android - Dialog, AlertDialog, ProgressDialog, DatePickerDialog, TimePickerDialog




Android에서 사용하는 Dialog에 대해서 알아보도록 하겠습니다.

[참고자료]

메인글 - 앞부분 설명은 아래 원문을 번역한 글입니다.
http://developer.android.com/guide/topics/ui/dialogs.html

DatePickerDialog Source 참조.
http://developer.android.com/resources/tutorials/views/hello-datepicker.html

TimePickerDialog Source 참조.
http://developer.android.com/resources/tutorials/views/hello-timepicker.html


Dialog는 보통 현 Activity 앞에 보여지는 작은 윈도우입니다. 많은 경우 새로운 Activity를 띄워 서로 데이터를 주고 받기 보다는 Dialog를 사용한다면 좀 더 편리하게 Android 개발을 할 수 있습니다.

먼저 Dialog 및 AlertDialog의 기본 기능을 살펴 본 후 다음 Post에서 Custom Dialog를 만들어 보도록 하겠습니다.

Dialog 하위 클래스에는 다음과 같은 특수한 Dialog가 존재합니다

AlertDialog : 대부분의 Dialog UI를 제공함.
ProgressDialog : Progress Wheel 이나 progress Bar 기능 지원
DatePickerDialog : 날짜 선택
TimePickereDialog : 시간 선택

Showing a Dialog


Dialog는 항상 Activity의 일부로 생성되어 보여집니다. 보통은 Activity 내부의 onCreateDialog(int) Callback Method 안에서 Dialog를 생성하는데, 이 Callback 메소드를 사용해서 Dialog를 생성하면 Android는 각각의 Dialog의 상태를 자동으로 관리하고, 호출된 Dialog의 소유자로 호출한 Activity가 자동으로 지정됩니다.

Dialog의 소유자 Activity가 지정되면, Dialog는 Activity로부터 Properties를 상속 받을 수 있습니다. 예를 들어 Dialog가 Open 되었을때 Menu Key는 Activity에 정의되어 있는 Option menu를 보여주며, Volumn Key는 Activity에 의해 사용되는 Audio Stream을 조절 할 수 있습니다.
(* 이 부분은 저로서도 명확하게 이해하기가 어렵군요. Menu 부분은 테스트를 해 봤는데, 별로 차이점을 찾지 못했구요. 좀 더 다른 테스트를 더 해봐야 할 듯 합니다.)

Note: 만일 onCreateDialog(int) 메소드 외부에서 Dialog를 생성하면 Activity에 붙어지지 않게 됩니다만, setOwnerActivity(Activity)를 통해 Activity에 Dialog를 붙일 수도 있습니다.

Dialog를 보여주고 싶다면 showDialog(int)를 호출하고 보여주고 싶은 Dialog를 구분해주는 번호를 넘겨주면 됩니다.

Dialog가 처음 요청 받게 되면, Android는 onCreateDialog(int)를 호출하는데 그곳에서 Dialog를 인스턴스를 생성해야 합니다. onCreateDialog(int) Callback Method는 showDialog(int)로 받은 것과 동일한 ID를 넘겨받아 Dialog를 생성한 후 Method의 맨 끝에서 해당 Object를 리턴합니다.

Dialog가 보여지기 이전에, Android는 선택적 Callback Method인 onPrepareDialog(int, Dialog)를 호출합니다. 만일 Dialog가 열릴 때 Dialog의 Property를 바꾸고 싶다면 onPrepareDialog Method를 재정의 해야 합니다. onPrepareDialog Method는 Dialog가 열릴 때마다 호출되는 되지만, onCreateDialog(int)는 Dialog가 처음 열릴 때 한번 만 호출됩니다.
만일 onPrepareDialog()를 정의하지 않았다면 Dialog는 이전 상태를 그대로 유지합니다. onPrepareDialog Method는 onCreateDialog()를 통해 만들어진 Dialog Object와 함께 Dialog의 ID를 인자로 넘겨 받습니다.

onCreateDialog(int)와 onPrepareDialog(int, Dialog) Callback Methods구현은 일반적으로 인자로 넘겨받는 ID값을 체크하여 처리하는 swith 구문을 사용하는 것이 일반적입니다.

Dialog 관련 코딩 작성 스타일을 보겠습니다.

먼저, 각각의 Dialog에 대한 ID를 정의한다.

static final int DIALOG_PAUSED_ID = 0;

static final int DIALOG_GAMEOVER_ID = 1;



그 이후, onCreateDialog(int) Callback을 정의합니다.


protected Dialog onCreateDialog(int id) {

        Dialog dialog;

        switch(id) {

        case DIALOG_PAUSED_ID:

            // do the work to define the pause Dialog

            break;

        case DIALOG_GAMEOVER_ID:

            // do the work to define the game over Dialog

            break;

        default:

            dialog = null;

        }

        return dialog;

    }


Dismissing a Dialog

Dialog를 닫고 싶을 때 Dialog Object에서 dismiss()를 호출하여 종료시킬 수 있으며 필요시 dismissDialog(int)를 호출할 수 있습니다.

Dialog의 State 관리를 위해 onCreateDialog(int)를 사용한다면 Dialog가 닫힐때마다 Dialog Object의 State은 Activity에 의해 유지됩니다. 만일 생성한 Object가 더이상 필요없거나 State가 Clear되는 것이 중요하다면 removeDialog(int)를 호출하면 됩니다. removeDialog() 메소드를 호출하면 Object에 연결된 모든 내부 참조를 삭제하며 만일 Dialog가 현재 보여지고 있으면 닫아주게 됩니다.


Using Dismiss Listeners

만약 Dialog가 닫히는 그 시점에 Appliction이 특정 기능을 수행하도록 처리하려면 Dialog에 on-dismiss listener를 구현해야 합니다.

먼저 DialogInterface.onDismissListener Interface를 정의합니다. 이 Interface는 onDismiss(DialogInterface) Method 하나를 가지고 있으며 Dialog가 닫힐 때 호출됩니다. 그 다음 setOnDismissListener()에 구현된 OnDismissListener를 넘겨주면 됩니다..

Dialog가 닫히는 동작이 dismiss 메소드가 호출될 때 말고도 Cancel에 의해서도 일어날 수 있는 점을 알아야 합니다. 사용자가 "back" Key를 눌렀을 때나 Dialog내에 있는 Cancel 버튼으로 cancel()이 호출되었을 때 발생하게 합니다. Dialog가 취소되었을 때 OnDismissListener은 알림을 기다리게 되지만 Dialog가 명시적으로 취소 되었음을 알려주고 싶다면 setOnCancelListen()를 DialogInterface.OnCancelListener로 등록시켜야 합니다.

Creating an AlertDialog

AlertDialog는 Dialog Class의 하위 클래스로 가장 많이 사용하는 Dialog 타입입니다.
기본적으로 Title, Text Message, One, Two, or Three Buttons, List of Selectable Itmes (check box or Raido Button) 을 가진 Dialog 이면 AlertDialog를 사용하시면 됩니다.

AlertDialog를 생성하려면 AlertDialog.Builder subclass를 사용합니다. AlertDialog.Builder(Context)를 통해 Builder를 얻어와 AlertDialog Properties를 설정한 후  create()을 통해 AlertDialog Object를 얻어온다.

샘플 코드를 보겠습니다.

AlertDialog Sample1



protected Dialog onCreateDialog(int id) {

    Dialog dialog;

    AlertDialog.Builder builder;

    switch(id) {

       

        case DIALOG_SIMPLE1:

            builder = new AlertDialog.Builder(this);

            builder.setMessage("Are you sure you want to exit?")      

                   .setCancelable(false)      

                   .setPositiveButton("Yes", new DialogInterface.OnClickListener() {

                       public void onClick(DialogInterface dialog, int id) {

                           MainActivity.this.finish();          

                       }      

                    })      

                   .setNegativeButton("No", new DialogInterface.OnClickListener() {          

                       public void onClick(DialogInterface dialog, int id) {               

                           dialog.cancel();          

                        }      

                     });

            dialog = builder.create();

            break;

                default :

            dialog = null;

    }

    return dialog;

}



위 그림처럼 두개의 버튼을 가지는 Dialog를 만들기 위해서 AlertDialog.Builder 인스턴스에 set..Button 메소드를 사용해서 버튼을 지정하고, 버튼 클릭시 처리 내용을 기술하면 됩니다.

setCancelable(false)로 지정하면 Dialog가 실행된 상태에서 취소 버튼을 클릭해도 화면이 취소(사라지지) 않습니다.

AlertDialog Sample3 – List Style




//array xml에서 불러옴.

final String[] colors = getResources().getStringArray(R.array.colors);

builder = new AlertDialog.Builder(this);

builder.setTitle("Pick a color");

builder.setItems(colors, new DialogInterface.OnClickListener() {

   

    @Override

    public void onClick(DialogInterface dialog, int item) {

        Toast.makeText(getApplicationContext(), colors[item], Toast.LENGTH_SHORT).show();

    }

});

dialog = builder.create();


Array 데이터는 res\values\array.xml로 다음과 같이 정의해서 사용했습니다

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

<resources>

    <string-array name="colors">

        <item>Red</item>

        <item>Green</item>

        <item>Yellow</item>

        <item>White</item>

    </string-array>

</resources>


List형의 AlertDialog는 setItmes 메소드로 배열정보와 항목 클릭시 코드를 기술하면 됩니다. 리스트 항목 선택시 별도로 dialog를 dismiss() 하지 않아도 자동으로 Dialog가 닫기는 군요.

AlertDialog Sample4 – CheckBox




final CharSequence[] items = {"Red", "Green","Blue"};

final boolean[] states = {false, false, true};

builder = new AlertDialog.Builder(this);

builder.setTitle("Pick a color");

//setMultiChoiceItems(int itemsId, boolean[] checkedItems, DialogInterface.OnMultiChoiceClickListener listener)

builder.setMultiChoiceItems(items, states, new DialogInterface.OnMultiChoiceClickListener() {

   

    @Override

    public void onClick(DialogInterface dialog, int item, boolean state) {

        Toast.makeText(getApplicationContext(), items[item] + " set to " + state, Toast.LENGTH_SHORT).show();

        //자동으로 닫기지는 않는다.

    }

   

});

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

   

    @Override

    public void onClick(DialogInterface dialog, int which) {

        String msg = "";

        for(boolean state : states) {

            msg += String.valueOf(state) + " ";        

        }

        Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG).show();

    }

});

dialog = builder.create();


CheckBox 리스트를 가진 Dialog를 만들기 위해서는 리스트에 뿌릴 배열(items) 및 각 항목의 체크여부를 가진 Boolean[] 배열 변수가 필요합니다.
Builder.setMultiChoiceItmes 메소드에 두 배열 변수 및 DialogInterface.OnMultiChoiceClickListener 인터페이스를 정의하면 됩니다.
각 항목이 선택되거나 해제될 때 마다 states  boolean 배열 변수의 값이 자동으로 설정되므로, Dialog 창이 닫길 때 해당 정보를 읽어서 다른 처리하고자 한다면 states 배열을 읽어서 처리하면 됩니다.

AlertDialog Sample5 – Radio Button




final CharSequence[] items1 = {"Red", "Green","Blue"};

builder = new AlertDialog.Builder(this);

builder.setTitle("Pick a color");

//setSingleChoiceItems(CharSequence[] items, int checkedItem, DialogInterface.OnClickListener listener)

builder.setSingleChoiceItems(items1, 0, new DialogInterface.OnClickListener() {

   

    @Override

    public void onClick(DialogInterface dialog, int item) {

        Toast.makeText(getApplicationContext(), items1[item] , Toast.LENGTH_SHORT).show();

        //자동으로 닫기지 않는다.

        dialog.dismiss();

    }

});

dialog = builder.create();


Radio Button은 CheckBox과 코드 스타일이 비슷합니다. setMultiChoiceItems() 메소드 대신 setSingleChoiceItems 메소드로 항목을 지정하면 됩니다.
setSingleChoiceItems 메소드의 두번째 파라미터는 checkedItem 항목을 지정하는 변수로 지정된 인덱스를 가진 Radio Button이 선택되어 표시됩니다.
Radio Button 선택시 자동으로 Dialog 창이 닫기지 않아 dismiss() 코드를 onClick 이벤트에 추가했습니다.

ProgressDialog – Circle



ProgressDialog는 위 그림처럼 Spinning Wheel의 형태로 애니메이션 프로그레스를 보여줄 수 도 있으며, 아래 그림처럼 정해진 진행과정을 가지는 Task를 위해 Progress Bar의 형태로 보여줄 수도 있습니다. Progress Dialog를 여는 것은 간단하게 ProgressDialog.show()만 호출하면 됩니다.

저는 onCreateDialog Callback 내에서 dialog를 생성했으며,


case DIALOG_PROGRESS1:

    dialog = ProgressDialog.show(MainActivity.this, "", "Loading, Please Wait..", true, true);


버튼 클릭시 ProgressDialog롤 보여주고, 5초가 지나면 자동으로 닫기도록 Sample 코드를 작성했습니다.


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

progressDialog1.setOnClickListener(new OnClickListener() {

   

    @Override

    public void onClick(View v) {

        showDialog(DIALOG_PROGRESS1);

       

        TimerTask myTask = new TimerTask(){

            public void run(){

                dismissDialog(DIALOG_PROGRESS1);

                                    

            }

        };

       

        Timer timer = new Timer();

        timer.schedule(myTask, 5000);

    }

});


ProgressDialog – Bar



애니메이션 형태의 Progress Bar를 보여주기 위해서는 :

1. ProgressDialog(Context) 생성자를 통해 ProgressDialog를 초기화 한다.
2. setProgressStyle(int)를 통해 "STYLE_HORIZONTAL"로 Style을 설정하고 그런 메시지를 이용하여 다른 Properties도 설정한다.
3. Dialog를 보여줄 준비기 되었다면, show()를 호출하거나 onCreateDialog(int) Callback을 통해 ProgressDialog를 반환시켜 준다.
4. 전체 진행 과정이 완료되기 까지 setProgress(int)를 호출하여 Bar에 보여지는 상태 진행정도를 증가 시킬수 있으며 incrementProgressBy(int)를 통해서도 가능하다.


dialog = new ProgressDialog(MainActivity.this);

((ProgressDialog)dialog).setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);

((ProgressDialog)dialog).setMessage("Loading..");

((ProgressDialog)dialog).setCancelable(true);

progressDialog = (ProgressDialog)dialog;


일반적으로 Bar 형태의 Progress는 별도 Thread를 생성해 다른 처리를 하면서 진행상태를 표시하기 위해 사용합니다. onCreateDialog() 메소드 내에서 dialog 지역변수 값을 progressDialog 멤버변수에 할당하여 Thread내에서 progressDialog 값을 증가시키도록 테스트 코드를 작성했습니다.

//ProgressBar 값을 증가시키는 Handler
Handler
handler = new Handler() {

 

        @Override

        public void handleMessage(Message msg) {

            int total = msg.getData().getInt("total");           

            progressDialog.setProgress(total);

            if(total >= 100) {

                progressDialog.dismiss();

            }

        }

       

    };

 


//OnCreate() 메소드 내에 버튼 클릭시 ProgressBar를 보여주고, 값을 증가시키는 Thread 생성 코드

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

progressDialog2.setOnClickListener(new OnClickListener() {

   

    @Override

    public void onClick(View v) {

        showDialog(DIALOG_PROGRESS2);

       

        new Thread(new Runnable() {

           

            @Override

            public void run() {

                

                try {

                    for(int i = 0; i <= 100; i++) {

                        Thread.sleep(100);

                        Message msg = handler.obtainMessage();

                        Bundle b = new Bundle();  

                        b.putInt("total", i);               

                        msg.setData(b);               

                        handler.sendMessage(msg);

                    }

                } catch(Throwable t) {

                   

                }

               

            }

        }).start();

    }

});


DatePickerDialog



DatePickerDialog와 TimePickerDialog는 onCreateDialog() 메소드내에서 DatePickerDialog 혹은 TimePickerDialog 인스턴스를 생성해 리턴하면 됩니다.

생성자에서 사용자가 날짜 설정시 처리할 Callback Method인 DatePickerDialog.OnDateSetListener 를 먼저 구현한 다음 파라미터로 함게 넘겨주면 쉽게 호출 할 수 있습니다.

private DatePickerDialog.OnDateSetListener mDateSetListener = new DatePickerDialog.OnDateSetListener() {

       

        @Override

        public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {

           mYear = year;

           mMonth = monthOfYear;

           mDay = dayOfMonth;

           updateDateDisplay();

        }

    };



//OnCreateDialog() 메소드 내에서
case
DIALOG_DATEPICKER:

      dialog = new DatePickerDialog(MainActivity.this, mDateSetListener , mYear, mMonth, mDay);


TimePickerDialog



TimePickerDialog는 DatePickerDialog과 거의 코드가 유사합니다. 생성자로 인스턴스 생성시에 24시간제인지? 12시간제인지를 지정할 수 있습니다. 소스는 아래 전체 코드를 보시기 바랍니다.

public class MainActivity extends Activity {

   

    //Dialog 호출을 위한 상수 정의

    static final int DIALOG_SIMPLE1 = 0;

    static final int DIALOG_SIMPLE2 = 1;

    static final int DIALOG_LIST = 3;

    static final int DIALOG_CHECKBOX = 4;

    static final int DIALOG_RADIO = 5;

    static final int DIALOG_PROGRESS1 = 6;

    static final int DIALOG_PROGRESS2 = 7;

    static final int DIALOG_DATEPICKER = 8;

    static final int DIALOG_TIMEPICKER = 9;

   

   

    public static final int MENU_INFO = Menu.FIRST;

   

    Button simpleAlertDialog1;

    Button simpleAlertDialog2;

    Button listAlertDialog;

    Button checkboxAlertDialog;

    Button radioAlertDialog;

    Button progressDialog1;

    Button progressDialog2;

 

    ProgressDialog progressDialog;

    //DatePickerDialog

    private TextView mDateDisplay;   

    private Button mPickDate;   

    private int mYear;   

    private int mMonth;   

    private int mDay;

   

    //TimePickerDialog

    private TextView mTimeDisplay;   

    private Button mPickTime;   

    private int mHour;   

    private int mMinute;

   

   

    Handler handler = new Handler() {

 

        @Override

        public void handleMessage(Message msg) {

            int total = msg.getData().getInt("total");           

            progressDialog.setProgress(total);

            if(total >= 100) {

                progressDialog.dismiss();

            }

        }

       

    };

   

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

       

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

        simpleAlertDialog1.setOnClickListener(new OnClickListener() {

           

            @Override

            public void onClick(View v) {

                showDialog(DIALOG_SIMPLE1);

            }

        });

       

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

        simpleAlertDialog2.setOnClickListener(new OnClickListener() {

           

            @Override

            public void onClick(View v) {

                showSimpleDialog();

            }

        });

       

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

        listAlertDialog.setOnClickListener(new OnClickListener() {

           

            @Override

            public void onClick(View v) {

                showDialog(DIALOG_LIST);

            }

        });

       

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

        checkboxAlertDialog.setOnClickListener(new OnClickListener() {

           

            @Override

            public void onClick(View v) {

                showDialog(DIALOG_CHECKBOX);

            }

        });

       

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

        radioAlertDialog.setOnClickListener(new OnClickListener() {

           

            @Override

            public void onClick(View v) {

                showDialog(DIALOG_RADIO);

            }

        });

       

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

        progressDialog1.setOnClickListener(new OnClickListener() {

           

            @Override

            public void onClick(View v) {

                showDialog(DIALOG_PROGRESS1);

               

                TimerTask myTask = new TimerTask(){

                    public void run(){

                        dismissDialog(DIALOG_PROGRESS1);

                                            

                    }

                };

               

                Timer timer = new Timer();

                timer.schedule(myTask, 5000);

            }

        });

       

       

       

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

        progressDialog2.setOnClickListener(new OnClickListener() {

           

            @Override

            public void onClick(View v) {

                showDialog(DIALOG_PROGRESS2);

               

                new Thread(new Runnable() {

                   

                    @Override

                    public void run() {

                        

                        try {

                            for(int i = 0; i <= 100; i++) {

                                Thread.sleep(100);

                                Message msg = handler.obtainMessage();

                                Bundle b = new Bundle();  

                                b.putInt("total", i);               

                                msg.setData(b);               

                                handler.sendMessage(msg);

                            }

                        } catch(Throwable t) {

                           

                        }

                       

                    }

                }).start();

            }

        });

       

        //DatePicker Dialog Test

        mDateDisplay = (TextView) findViewById(R.id.dateDisplay);       

        mPickDate = (Button) findViewById(R.id.pickDate);

       

        mPickDate.setOnClickListener(new OnClickListener() {

           

            @Override

            public void onClick(View v) {

                showDialog(DIALOG_DATEPICKER);

            }

        });

       

       // 현재 날짜를 가져온다.     

        final Calendar c = Calendar.getInstance();       

        mYear = c.get(Calendar.YEAR);       

        mMonth = c.get(Calendar.MONTH);       

        mDay = c.get(Calendar.DAY_OF_MONTH);       

       

        // display the current date (this method is below)       

        updateDateDisplay();

       

       

        //TimePicker Dialog Test

        mTimeDisplay = (TextView) findViewById(R.id.timeDisplay);       

        mPickTime = (Button) findViewById(R.id.pickTime);

       

        mPickTime.setOnClickListener(new OnClickListener() {

           

            @Override

            public void onClick(View v) {

                showDialog(DIALOG_TIMEPICKER);

            }

        });

       

        final Calendar c1 = Calendar.getInstance();       

        mHour = c1.get(Calendar.HOUR_OF_DAY);       

        mMinute = c1.get(Calendar.MINUTE);

       

        updateTimeDisplay();

    }

 

    @Override

    protected Dialog onCreateDialog(int id) {

        Dialog dialog;

        AlertDialog.Builder builder;

        switch(id) {

           

            case DIALOG_SIMPLE1:

                builder = new AlertDialog.Builder(this);

                builder.setMessage("Are you sure you want to exit?")      

                       .setCancelable(false)      

                       .setPositiveButton("Yes", new DialogInterface.OnClickListener() {

                           public void onClick(DialogInterface dialog, int id) {

                               MainActivity.this.finish();          

                           }      

                        })      

                       .setNegativeButton("No", new DialogInterface.OnClickListener() {          

                           public void onClick(DialogInterface dialog, int id) {               

                               dialog.cancel();          

                            }      

                         });

                dialog = builder.create();

                break;

            case DIALOG_LIST:

                //array xml에서 불러옴.

                final String[] colors = getResources().getStringArray(R.array.colors);

                builder = new AlertDialog.Builder(this);

                builder.setTitle("Pick a color");

                builder.setItems(colors, new DialogInterface.OnClickListener() {

                    

                    @Override

                    public void onClick(DialogInterface dialog, int item) {

                        Toast.makeText(getApplicationContext(), colors[item], Toast.LENGTH_SHORT).show();

                    }

                });

                dialog = builder.create();

                break;

            case DIALOG_CHECKBOX:

                final CharSequence[] items = {"Red", "Green","Blue"};

                final boolean[] states = {false, false, true};

                builder = new AlertDialog.Builder(this);

                builder.setTitle("Pick a color");

                //setMultiChoiceItems(int itemsId, boolean[] checkedItems, DialogInterface.OnMultiChoiceClickListener listener)

                builder.setMultiChoiceItems(items, states, new DialogInterface.OnMultiChoiceClickListener() {

                   

                    @Override

                    public void onClick(DialogInterface dialog, int item, boolean state) {

                        Toast.makeText(getApplicationContext(), items[item] + " set to " + state, Toast.LENGTH_SHORT).show();

                        //자동으로 닫기지는 않는다.

                    }

                   

                });

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

                   

                    @Override

                    public void onClick(DialogInterface dialog, int which) {

                        String msg = "";

                        for(boolean state : states) {

                            msg += String.valueOf(state) + " ";        

                        }

                        Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG).show();

                    }

                });

                dialog = builder.create();

                break;

            case DIALOG_RADIO:

                final CharSequence[] items1 = {"Red", "Green","Blue"};

                builder = new AlertDialog.Builder(this);

                builder.setTitle("Pick a color");

                //setSingleChoiceItems(CharSequence[] items, int checkedItem, DialogInterface.OnClickListener listener)

                builder.setSingleChoiceItems(items1, 0, new DialogInterface.OnClickListener() {

                   

                    @Override

                    public void onClick(DialogInterface dialog, int item) {

                        Toast.makeText(getApplicationContext(), items1[item] , Toast.LENGTH_SHORT).show();

                        //자동으로 닫기지 않는다.

                        dialog.dismiss();

                    }

                });

                dialog = builder.create();

                break;

            case DIALOG_PROGRESS1:

                dialog = ProgressDialog.show(MainActivity.this, "", "Loading, Please Wait..", true, true);

                break;

            case DIALOG_PROGRESS2:

                dialog = new ProgressDialog(MainActivity.this);

                ((ProgressDialog)dialog).setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);

                ((ProgressDialog)dialog).setMessage("Loading..");

                ((ProgressDialog)dialog).setCancelable(true);

                progressDialog = (ProgressDialog)dialog;

                break;

            case DIALOG_DATEPICKER:

                dialog = new DatePickerDialog(MainActivity.this, mDateSetListener , mYear, mMonth, mDay);

                break;

            case DIALOG_TIMEPICKER:

                dialog = new TimePickerDialog(MainActivity.this, mTimeSetListener, mHour, mMinute, true);

                break;

            default :

                dialog = null;

        }

        return dialog;

    }

   

    private DatePickerDialog.OnDateSetListener mDateSetListener = new DatePickerDialog.OnDateSetListener() {

       

        @Override

        public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {

           mYear = year;

           mMonth = monthOfYear;

           mDay = dayOfMonth;

           updateDateDisplay();

        }

    };

   

    private TimePickerDialog.OnTimeSetListener mTimeSetListener = new TimePickerDialog.OnTimeSetListener() {

       

        @Override

        public void onTimeSet(TimePicker view, int hourOfDay, int minute) {

            mHour = hourOfDay;           

            mMinute = minute;           

            updateTimeDisplay();

        }

    };

 

    private void showSimpleDialog() {

        AlertDialog.Builder builder = new AlertDialog.Builder(this);

        builder.setMessage("Are you sure you want to exit?")      

               .setCancelable(false)      

               .setPositiveButton("Yes", new DialogInterface.OnClickListener() {

                   public void onClick(DialogInterface dialog, int id) {

                       MainActivity.this.finish();          

                   }      

               })      

               .setNegativeButton("No", new DialogInterface.OnClickListener() {          

                   public void onClick(DialogInterface dialog, int id) {               

                       dialog.cancel();          

                    }      

                });

        builder.show();

    }

   

    private void updateDateDisplay() {

        mDateDisplay.setText(           

                new StringBuilder()    

                    .append(mYear).append("-")

                    .append(mMonth + 1).append("-")

                    .append(mDay));

    }

   

    private void updateTimeDisplay() {

        mTimeDisplay.setText(       

                new StringBuilder()               

                    .append(pad(mHour)).append(":")               

                    .append(pad(mMinute)));

    }

   

    private static String pad(int c) {   

        if (c >= 10)       

            return String.valueOf(c);   

        else       

            return "0" + String.valueOf(c);

    }



* Dialog 유형별 테스트를 위한 Sample Code가 필요하신 분은 아래 첨부 파일을 다운해서 사용하시기 바랍니다.
Trackback 0 Comment 5
  1. 2011.05.30 17:43 address edit & del reply

    비밀댓글입니다

  2. 유명인사 2011.07.27 14:15 신고 address edit & del reply

    잘보고 갑니다. 다이얼로그에 대한 기본적인 것들이 다있어서 유익한 자료가 되겟네요.

  3. Stereo 2011.08.24 09:33 신고 address edit & del reply

    질문좀 해도 될까요?
    해당 액티비티에서 AlertDialog.Builder를 사용하여 show를 했을때 사이즈는 조절 가능한가요?

  4. Isabel Marant Pumps Shoes 2012.08.07 11:56 신고 address edit & del reply

    볼륨버튼 + 전원버튼 : -> iCaRuS SpeedMod 매인화면 진입

  5. 초코아이스티 2012.10.12 10:36 신고 address edit & del reply

    글 잘 보고 갑니다
    dismiss를 이해를 못해서 흘러 흘러 여기까지 왓는데
    다이얼로그까지 덤으로 보고 가게 되네요
    감사합니다
    즐거운 하루 되세요 ^^

2010.09.01 15:21

[강좌A12] 안드로이드 실전 개발 - Main UI 소스, ListAdapter




이제 메인 UI – MainActivity 소스 부분입니다. 강좌의 소스 부분은 거의 막바지에 다다른 것 같습니다. 너무 쉬운 부분을 별다른 설명을 하지는 않겠습니다. 소스를 보시면 잘 아시리라 믿습니다.

전체 강좌 목차

[강좌A01] Moteodev Studio를 이용한 안드로이드 개발 환경 구축 가이드
[강좌A02] 안드로이드 개발 참고 서적 소개
[강좌A03] Android 실전 개발 - 아이디어 / 기획 / Wireframe
[강좌A04] 안드로이드 실전 개발 - 아이콘 제작
[강좌A05] 안드로이드 실전 개발 - 레이아웃 및 리소스 : Part1
[강좌A06] 안드로이드 실전 개발 - 레이아웃 및 리소스 : Part2
[강좌A07] 안드로이드 실전 개발 - 리소스 해킹
[강좌A08] 안드로이드 실전 개발 - SQLite

[강좌A09] 안드로이드  실전 개발 - 데이터베이스 : Part1
[강좌A10] 안드로이드  실전 개발 - 데이터베이스 : Part2
[강좌A11] 안드로이드  실전 개발 - 데이터베이서 : Part3 (Motodev database)
[강좌A12] 안드로이드  실전 개발 - Main UI 소스, ListAdapter

곧바로 코드를 보도록 하겠습니다. 메인 Activity Class 전체 코드입니다.

//MainActivity.java

package com.overoid.hangul2english;

 

 

 

import java.util.ArrayList;

import java.util.List;

 

 

 

import com.overoid.hangul2english.data.DataDao;

import com.overoid.hangul2english.data.DataDao.DataTo;

 

 

import android.app.AlertDialog;

import android.app.ListActivity;

import android.content.Context;

import android.content.DialogInterface;

import android.os.Bundle;

import android.text.ClipboardManager;

import android.text.Editable;

import android.text.TextUtils;

import android.text.TextWatcher;

import android.util.Log;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;

import android.view.View.OnClickListener;

import android.widget.ArrayAdapter;

import android.widget.Button;

import android.widget.EditText;

import android.widget.ImageButton;

import android.widget.ImageView;

import android.widget.ListView;

import android.widget.TextView;

import android.widget.Toast;

 

public class MainActivity extends ListActivity {

    public static String CLASSNAME = MainActivity.class.getSimpleName();

   

    private DataDao dao;

    private DataListAdapter mListAdapter;

    private ArrayList<DataTo> mDatas;

   

    //UI관련 변수

    private EditText mKorText;

    private EditText mEngText;

    private Button mSaveButton;

    private ImageButton mRemoveButton;

    private ImageButton mCopyButton;

    private ImageView mInfoButton;

    private ListView mListView;

    private TextView mEmpty;

   

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

       

        // Dao Instance

        dao = new DataDao(getApplicationContext());

       

        // UI Init

        mKorText = (EditText)findViewById(R.id.text_password);

        mEngText = (EditText)findViewById(R.id.text_english);

        mSaveButton = (Button)findViewById(R.id.button_save);

        mRemoveButton= (ImageButton)findViewById(R.id.button_remove);

        mCopyButton = (ImageButton)findViewById(R.id.button_copy);

        mInfoButton = (ImageView)findViewById(R.id.button_info);

       

        //ListActivity 사용하면 Resource ListView id "@+id/android:list" 사용해야 .

        mListView = getListView(); 

        mListView.setItemsCanFocus(true);

        mListView.setEmptyView(mEmpty);

        mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);

        mEmpty = (TextView)findViewById(R.id.list_empty);

       

        //Event Handler

       

        // Info Button Click

        mInfoButton.setOnClickListener(new OnClickListener() {

           

            @Override

            public void onClick(View v) {

                infoAlertDialogShow(MainActivity.this);

            }

 

        });

       

        // Hangul Text Change

        mKorText.addTextChangedListener(new TextWatcher() {

           

            @Override

            public void onTextChanged(CharSequence s, int start, int before, int count)  {

                Log.v("**", "TextChanged");

                if(!TextUtils.isEmpty(mKorText.getText())) {

                    //mEngText.setText(Hangul.convertToEnglishforSingleChar(Hangul.convertToEnglish(mKorText.getText().toString())));

                } else {

                    mEngText.setText("");

                }

               

            }

           

            @Override

            public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {

                // TODO Auto-generated method stub

               

            }

           

            @Override

            public void afterTextChanged(Editable arg0) {

                // TODO Auto-generated method stub

               

            }

        });

       

        // Remove Button Click

        mRemoveButton.setOnClickListener(new OnClickListener() {

           

            @Override

            public void onClick(View arg0) {

                mKorText.setText("");

                mEngText.setText("");

            }

        });

       

        // Copy Button Click

        mCopyButton.setOnClickListener(new OnClickListener() {

           

            @Override

            public void onClick(View v) {

                if(mEngText.getText().length() > 0) {

                    copyClipboard(mEngText.getText().toString());

                }

            }

        });

       

        // Save Button Click

        mSaveButton.setOnClickListener(new OnClickListener() {

           

            @Override

            public void onClick(View v) {

                if(mKorText.getText().length() > 0 && mEngText.getText().length() > 0) {

                   

                    DataTo to = new DataTo(0, mKorText.getText().toString(), mEngText.getText().toString());

                    Log.i(Constants.LOG_TAG,MainActivity.CLASSNAME + "insert:" + to.toString());

                    dao.insert(to);

                    Toast.makeText(MainActivity.this, R.string.msg_insert_success, Toast.LENGTH_SHORT).show();

   

                    //새로 바인딩해야 .   

                    populateList();

                } else {

                    Toast.makeText(MainActivity.this, R.string.msg_insert_no_data, Toast.LENGTH_SHORT).show();

                }

 

            }

        });

       

        //로딩시 데이터를 가져와 리스트에 뿌린다.

        populateList();

    }

   

    @Override

    protected void onPause() {

        Log.i(Constants.LOG_TAG,MainActivity.CLASSNAME + "- onPause()");

        super.onPause();

    }

 

    @Override

    protected void onResume() {

        Log.i(Constants.LOG_TAG,MainActivity.CLASSNAME + "- onResume()");

        super.onResume();

 

    }

   

   

   

    @Override

    protected void onDestroy() {

        dao.close();

        super.onDestroy();

    }

   

    /****

     * Clipboad 선택한 텍스트 복사.

     */

    private void copyClipboard(String s) {

        ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); 

        clipboard.setText(s);

        Toast.makeText(MainActivity.this, R.string.msg_clipboard_copy_success, Toast.LENGTH_SHORT).show();

    }

   

    /****

     * 정보창을 띄운다.

     * @param context

     */

    private void infoAlertDialogShow(Context context) {

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

        View infoView = inflater.inflate(R.layout.info, null);

 

        String alertTitle = getResources().getString(R.string.app_name);

        String buttonMessage = getResources().getString(R.string.msg_info_close_button);

       

        new AlertDialog.Builder(context)

        .setTitle(alertTitle)

        .setView(infoView)

        .setNeutralButton(buttonMessage, new DialogInterface.OnClickListener() {

           

            public void onClick(DialogInterface dlg, int sumthin) {

                //기본적으로 창이 닫히며, 추가작업은 없다.

            }

        }).show();

    }

   

    /****

     * populateList     : table data 새로 가져와 화면에 뿌린다.

     */

    private void populateList() {

       

        Log.v(Constants.LOG_TAG, MainActivity.CLASSNAME + "- populateList");

       

        mDatas = (ArrayList<DataTo>)dao.get();

       

        if(mDatas == null) {

            mEmpty.setText("");

            setListAdapter(null);

        } else {

            mListAdapter = new DataListAdapter(MainActivity.this,R.layout.list_item, mDatas);

            setListAdapter(mListAdapter);

            Log.v(Constants.LOG_TAG, MainActivity.CLASSNAME + "- populateList, mListView Binding End");

           

        }

    }

   

    class DataListAdapter extends ArrayAdapter<DataTo> {

        private static final String CLASS = "DataListAdapter";

        private Context mContext;

        private List<DataTo> mDataList;

       

        public DataListAdapter(Context context, int textViewResourceId, List<DataTo> items) {

            super(context, textViewResourceId, items);

            mContext = context;

            mDataList = items;

        }

 

        @Override

        public View getView(int position, View convertView, ViewGroup parent) {

            View row = convertView;

           

            if(row == null) {

                LayoutInflater inflator = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

                row = inflator.inflate(R.layout.list_item, null);

            }

           

            final DataTo to = mDataList.get(position);

            Log.i(Constants.LOG_TAG, CLASS + " getView:pos" + position + "data:" + to.toString());

            Log.i(Constants.LOG_TAG, CLASS + " korText:" + to.getKorText().toString() + ",engText:" + to.getEngText().toString());

           

            if(to != null) {

                TextView korText = (TextView)row.findViewById(R.id.korText);

                korText.setText(to.getKorText());

               

                TextView engText = (TextView)row.findViewById(R.id.engText);

                engText.setText(to.getEngText());

               

                ImageView copyClipboadButton = (ImageView)row.findViewById(R.id.copyClipboard);

                copyClipboadButton.setOnClickListener(new OnClickListener() {

                   

                    @Override

                    public void onClick(View v) {

                        copyClipboard(to.getEngText().toString());

                    }

                });

               

                ImageView deleteButton = (ImageView)row.findViewById(R.id.deleteImage);

                deleteButton.setOnClickListener(new OnClickListener() {

                   

                    @Override

                    public void onClick(View v) {

                       

                        Log.i(Constants.LOG_TAG, CLASS + " delete:" + to.toString());

                        dao.delete(to.getId());

                        Toast.makeText(mContext, R.string.msg_delete_success, Toast.LENGTH_SHORT).show();

                       

                        //새로 바인딩해야 .   

                        populateList();

                       

                    }

                });

            }

            return (row);

        }

 

    }

 


먼저 MainActivity는 일반적인 Activity 클래스에서 상속받지 않고 Activity의 하위 클래스인 ListActivity에서 상속을 받았습니다. UI에 List가 있을 경우 ListActivity에서 상속받으면 좀 더 편리합니다.

onCreate() 메소드는 액티비티가 처음 실행될 때 호출되는 메소드로 안드로이드 내부의 액티비티 초기화 작업을 진행할 수 있게 반드시 가장 먼저 해당 이벤트를 상위 클래스에 전달해야 합니다.
super.onCreate(savedInstanceState);

그 이후에 각 UI Component들을 findViewById() 메소드를 통해 이미 만들어진 인스턴스에 대한 레퍼런스를 가져옵니다.

리스트뷰의 인스턴스의 경우 ListActivity 클래스에서 제공하는 getListView() 메소드를 통해서 가져옵니다. mListView = getListView();

각 UI Component 인스턴스 생성 후에 각각의 View에 Event Handler를 할당합니다.

Info button click시에는 프로그램 정보를 볼 수 있는 창을 띄우는데…


private void infoAlertDialogShow(Context context) {

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

        View infoView = inflater.inflate(R.layout.info, null);

 

        String alertTitle = getResources().getString(R.string.app_name);

        String buttonMessage = getResources().getString(R.string.msg_info_close_button);

       

        new AlertDialog.Builder(context)

        .setTitle(alertTitle)

        .setView(infoView)

        .setNeutralButton(buttonMessage, new DialogInterface.OnClickListener() {

           

            public void onClick(DialogInterface dlg, int sumthin) {

                //기본적으로 창이 닫히며, 추가작업은 없다.

            }

        }).show();

    }

AlertDialog에서 저희가 만든 View를 Add 하기 위해 inflater.inflate() 메서드로 별도로 만든 레이아웃 파일을 동적으로 로드 한 후 AlertDialog Buider에 add 합니다.

한글 입력창에 한글이 입력되면 자동으로 영타로 변환해서 영문창에 나타내어야 합니다. 구현방법은 여러가지 있겠지만, 저는 TextWatcher 를 사용해서 onTextChanged 이벤트에서 한글을 자소 분리하여 영문으로 변환하도록 구현했습니다. 한글 자소분리에 대해서는 다음번 포스트에서 상세히 알려드리도록 하겠습니다.

copyClipboard() 메소드는 입력된 문자열을 클립보드에 복사하는 메소드입니다.
ClipboardManager 인스턴스를 getSystemService() 메서드를 통해 얻은 후 문자열을 setText로 지정하면 클립보드에 복사가 됩니다.

저장버튼 클릭시에는 데이터를 담을 DataTo 객체를 생성 후 한글/영문을 저장하고 DataDao 인스턴스의 insert의 인자로 넘겨주면 DB에 저장이 됩니다.

그리고 각각의 중요한 처리마다 Toast 클래스를 이용하여 메시지를 표현했습니다.

Toast 사용법은 단순합니다. makeText() 메소드에 context와 문자열 및 표시될 시간 상수를 넘겨주면 됩니다. 물론 Toast도 좀 근사하게 만들 수 있습니다. Toast 클래스를 new 키워드로 직접 생성한 후 setView 메서드를 사용해서 원하는 뷰를 지정하시면 됩니다. 그리고  setDuration() 메소드로 화면에 표시될 시간을 지정한 후 show() 메서드를 호출하면 화면에 나타납니다.

다음은 ListAdapter 부분입니다. 위 코드는 Custom List를 만드는 전형적인 코드 패턴입니다.
즉, Adapter 클래스를 상속받아 우리 입맞에 맞게 클래스를 만든 후 getView() 메소드를 오버라이드 해서 처리하는 방식입니다. 저는 ArrayAdapter에서 상속을 받았습니다. 리스트에 뿌려야 될 내용이 DB에서 받아온 ArrayList이므로 ArrayAdpater와는 아주 궁합이 잘 맞습니다.

DataListAdapter 클래스를 MainActivity의 중첩클래스로 만들었습니다. 내부 클래스로 만들면 MainActivity의 자원을 맘껏 가져다 쓸 수 있으므로 편리합니다. DataListAdapter 클래스 생성자에 Context와 DB에서 조회한 ArrayList 를 인자로 넘겨줍니다. getView() 메소드에서는 리스트의 한 항목 UI 레이아웃을 우리가 별도로 제작한 R.layout.list_item 을 inflate 헤서 메모리에 로드한 후 ArrayList에 담긴 내용으로 값을 설정합니다. Inflation이란 XML 레이아웃에 정의된 내용을 분석해 view 객체의 트리 구조를 만들어 내는 작업을 말합니다. 실제 동적으로 특정 XML 레이아웃을 로딩할 때 사용하시면 됩니다.

getView() 내에서 각종 UI View에 이벤트를 할당 할 수도 있습니다. 위 코드는 삭제 버튼과 클립보드 복사 이미지에 클릭 이벤트를 할당하여 삭제하거나 클립보드에 복사하도록 한 소스입니다.

getView()내에서 convertView의 값을 체크하여 null 인 경우에만 inflate 하도록 코드가 작성되어 있습니다. 일반적으로 성능개선을 위해서 위와 같이 사용하기도 하며, 더 나은 성능을 위해서 findViewById() 메서드의 호출을 줄이기 위한 Holder 패턴을 사용하기도 합니다. 홀더 패턴을 간단히 말하면 각 View마다 setTag()와 getTag() 메서드를 사용하며 나중에 사용할 내부 위젯을 캐시해 두는 기법으로 각 행의 View에 필요한 정보를 담은 객체를 태그로 저장해 두고, 사용할 때 getTag()로 꺼내어 findViewById() 메서드의 호출을 최소화 하는 기법입니다. 보다 상세한 사항을 알고 싶으시면 구글 문서나 책들을 보시면 될 것 같습니다.

대략적으로 MainActivity 소스에 대해서 살펴보았습니다.

이제 코드에서 남은 부분은 한타를 영타로 변환하는 한글유틸 클래스만 작성되면 Hangul2English 앱은 개발이 완료 될 것 같습니다.

이번 강좌까지 개발된 소스를 첨부합니다. 필요하신 분은 다운받으셔서 사용하시기 바랍니다.


그럼, 다음 강좌는 일주일쯤 후에 ..

Trackback 4 Comment 1
  1. 뉴이 2011.04.01 19:08 신고 address edit & del reply

    정말 감사합니다^^