반응형
블로그 이미지
개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
솔웅

최근에 받은 트랙백

글 보관함

[CookBook] Fragments 이해하기 -10-

2013. 10. 1. 20:08 | Posted by 솔웅


Setting user's Preferences with PreferenceFragment


PreferenceFragment는 유저가 어플리케이션을 configure하고 personalize할 수 있도록 하는 fragment 입니다. 이 PreferenceFragment는 여러개의 preference view들을 가질 수 있습니다. 애플리케이션의 preference를 쉽게 세팅할 수 있도록 도와주기 위해서이죠. PreferenceFragments에는 아래와 같은 preference view 들이 있습니다.



PreferenceScreen : preference screen을 정의하기 위해 사용되는 XML의 root element.

CheckBoxPreference : check되면 true를 안되면 false를 return 하는 간단한 check box를 표시한다.

ListPreference : 사용자가 선택할 수 있는 radio button들의 리스트를 표시한다.

EditTextPreference : 사용자가 텍스트를 입력할 수 있는 EditText dialog를 표시한다.

RingtonePreference : ringtone을 가리키는 radio button을 표시한다.

PreferenceCategory : 카테고리 내의 관계된 preference들을 grouping 한다.

Preference : Button 처럼 사용하는 custom preference



어떻게 어플리케이션 preference들이 세팅되는지 이해하기 위해 PrefFragmentApp이라는 안드로이드 프로젝트를 일단 만들어 보죠. pPreferenceFragment에서 preference view들을 표시하는 방법에는 두가지가 있습니다. XML 파일을 이용하는 방법과 code를 이용하는 방법이 있습니다. 우리는 XML을 이용하는 방법을 시도해 보겠습니다. 그러니까 먼저 res 폴더 밑에 xml 이라는 폴더를 먼저 만드셔야 합니다. 이 res/xml 이라는 폴더 밑에 preference.xml 이라는 XML 파일을 추가하시면 됩니다. 이 xml 에는 어플리케이션의 configure를 위해 유저에게 표시될 preference view들이 선언돼 있습니다. 이 preference view들 중 유저가 선택한 옵션들은 어플리케이션 내에 계속 유지될 겁니다.






preference.xml



<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
    <PreferenceCategory android:title="Category 1">
        <CheckBoxPreference
            android:title="Newsletter"
            android:defaultValue="false"
            android:key="Newskey"  />
        <EditTextPreference android:key="Namekey"
            android:title="Enter your name: "
            android:dialogTitle="Enter your name" >
        </EditTextPreference>
    </PreferenceCategory>
    <PreferenceCategory android:title="Category 2">
        <RingtonePreference android:showDefault="true"
            android:key="Audio" android:title="Select sound"
            android:ringtoneType="notification" >
        </RingtonePreference>
        <ListPreference android:title="Products List "
            android:key="products_list"
            android:entries="@array/products"
            android:entryValues="@array/prodselected"
            android:dialogTitle="Choose a product" >
        </ListPreference>
    </PreferenceCategory>
    <Preference
        android:title="Submit"
        android:key="submitPref" />
</PreferenceScreen>



이 PreferenceScreen 안에는 두개의 PreferenceCategory가 있는게 보이실 겁니다. Category 1에는 CheckboxPreference, EditTextPreference 이렇게 두개의 preference view가 있습니다. Category 2에는 RingtonePreference, ListPreference 이렇게 있구요. 모든 preference view는 android:key 값을 가져야 합니다. 일정의 id이죠. android:title 속성은 preference view의 초기값을 할당하기 위해 사용됩니다. 그리고 android:defaultValue 속성은 preference view에 디폴트 값을 할당할 때 사용되구요.



CheckBoxPreference는 UI element로서 check box를 표시합니다. 여기에는 Boolean 형식으로 값이 저장되구요. 체크박스가 선택되면 true값이 선택되지 않으면 false 값이 저장됩니다.  android:default 속성을 사용해서 디폴트 값을 false로 설정했습니다.



EditTextPreference 는 android:key 속성에 Namekey 라고 정해져 있습니다. 그리고 android:title에는 Enter your name : 이 지정돼 있구요. 이 EditTextPreference 가 선택되면 title이 Enter your name 이라는 dialog가 뜰 겁니다. 사용자가 정보를 입력하고 OK 버튼을 클릭하면 입력된 정보가 preference store에 저장됩니다.



RingtonePreference는 ringtone 리스트가 있는 dialog box를 보여줄 겁니다. 그리고 사용자가 default ringtone이나 silent mode를 선택할 수 있도록 합니다. 여기에 android:key 속성에는 Audio라고 설정이 돼 있습니다. 그리고 title은 Select sound라고 돼 있죠. android:ringtoneType은 display 될 ringtone 리스트를 정합니다. 여기에 들어갈 수 있는 값은 ringtone, notification, alarm 그리고 all 이 들어갈 수 있습니다.



ListPreference 에는 radio 버튼 형식으로 preference들의 세트를 dialog box 에 보여 줍니다. 사용자가 그 중 하나를 선택할 수 있는 거죠. 이 dialog box의 title은 Choose a product 가 될 겁니다. android:key 속성에는 product_list가 대입 됐구요. android:endtries 속성은 preference들의 리스트를 보여주기 위해 ListPreference에 product라는 이름의 배열이 할당됐습니다. 이 배열에 있는 값들이 ListPreference를 통해서 표시되는 radio button들의 아이템이 될 겁니다. android:entryValues 속성은 prodselected라고 하는 다른 배열을 정의하는데요. product 배열에 정의된 요소들의 값을 잡아두고 있기 위해서 세팅됐습니다. android:entryValues 속성은 사용자가 선택한 radio button에 해당되는 값들을 저장하는 배열입니다.



<Preference> element는 PreferenceFragment에 Submit Button을 표시합니다. 이 Submit button은 여기서 android:key 속성에 해당되는 값이 submitPref로 돼 있습니다. 자바코드에서 이 값을 갖고 콘트롤 할 겁니다.



이제 strings.xml에 두개의 배열을 정의할 겁니다. 하나는 ListPreference의 radio button에 표시될 텍스트들이구요 다른 하나는 첫번째 배열과 대응하는 값들 입니다. 이 xml은 res/values 폴더 안에 있게 됩니다.



strings.xml



<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">PrefFragmentApp</string>
    <string name="menu_settings">Settings</string>
    <string-array name="products">
        <item>Camera</item>
        <item>Laptop</item>
        <item>Watch</item>
        <item>Smartphone</item>
        <item>Television</item>
    </string-array>
    <string-array name="prodselected">
        <item>You have selected Camera</item>
        <item>You have selected Laptop</item>
        <item>You have selected Watch</item>
        <item>You have selected Smartphone</item>
        <item>You have selected Television</item>  
    </string-array>
</resources>


products 배열에 있는 element들은 ListPreference에 보이는 radio buttons 옆에 표시될 텍스트 들입니다. prodselected 배열은 위 products 배열중에서 선택된 것들에 대한 return 값들이 들어 있습니다.



preference.xml에 있는 이 preference view들을 load 하기 위한 자바클래스가 필요 합니다. 아래 그 샘플 코드가 있습니다.



PrefFragActivity.java



public class PrefFragActivity extends Activity {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getFragmentManager().beginTransaction().replace(android.R.id.content, new PrefsFragment()).commit();
    }

    public static class PrefsFragment extends PreferenceFragment  {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.preferences);
            Preference submitPref = (Preference) findPreference("submitPref");
            submitPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
                public boolean onPreferenceClick(Preference preference) {
                    getActivity().finish();
                    return true;
                }
            });
        }
    }
}



PreferenceFragment를 사용하기 위해 자바 클래스에서 PreferenceFragment를 extends 합니다. addPreferencesFromResource() 메소드는 XML 파일(preferences.xml)에 있는 PreferenceFragment에 있는 preference view를 load하기 위해 호출됩니다. preferences.xml에서 <Preference) element를 사용해 정의된 Submit 버튼은 submitPref라는 Preference 객체를 통해서 접근되고 매핑이 됩니다. 이것의 콜백 메소드인 onPreferenceClick()은 submitPref 객체가 클릭됐을 떄 실행됩니다. 이 onPreferenceClick() 메소드는 이 activity를 finish() 하고 선택된 preferences에 필요한 action을 하기 위해 PreferenceFragActivity.java로 돌아 갑니다. 이 PrefFragmentAppActivity자바 activity 파일을 통해 텍스트뷰에 유저가 선택한 preference를 표시할 겁니다.



이 PreferenceFragment에 보여지는 preference view들로부터 선택된 옵션을 표시하기 위해 4개의 텍스트뷰가 정의되어야 합니다. 아래 그 layout xml 파일이 있습니다.



activity_pref_fragment_app.xml



<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/newsletter"
        android:textSize="@dimen/text_size"        
        android:textStyle="bold" />
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/name"
        android:textSize="@dimen/text_size"
        android:textStyle="bold" />
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/ringtone"
        android:textSize="@dimen/text_size"
        android:textStyle="bold" />
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/product"
        android:textSize="@dimen/text_size"
        android:textStyle="bold" />
</LinearLayout>



위 layout xml 파일을 보면 4개의 텍스트뷰가 있고 각각 id 는 각각 newsletter, name, ringtone, product 입니다. 이 텍스트뷰는 LinearLayout 안에서 아래 위로 배치됩니다. newsletter 텍스트뷰에는 CheckBoxPreference의 체크박스에 유저가 체크했는지 여부가 표시될 겁니다. name 텍스트뷰는 EditTextPreference에 유저가 입력한 이름이 표시될것이고 ringtone 텍스트뷰는 유저가 선택한 RingtonePreference의 ringtone이 표시될 겁니다. product 텍스트뷰에는 ListPreference에서 유저가 선택한 product가 표시됩니다.



PreferenceFragment를 표시하고 사용자가 선택한 preference들을 보여주기위해 아래와 같은 자바파일을 만듭니다.



PrefFragmentAppActivity.java



public class PrefFragmentAppActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pref_fragment_app);
        startActivity(new Intent(this, PrefFragActivity.class));
    }

    @Override
    public void onResume() {
        super.onResume();
        SharedPreferences prefs=PreferenceManager.getDefaultSharedPreferences(this);
        TextView newsletter=(TextView)findViewById(R.id.newsletter);
        TextView name=(TextView)findViewById(R.id.name);
        TextView ringtone=(TextView)findViewById(R.id.ringtone);
        TextView product=(TextView)findViewById(R.id.product);
        if(Boolean.valueOf(prefs.getBoolean("Newskey", false)))
            newsletter.setText("You have selected Newsletter");
        else
            newsletter.setText("");
        ringtone.setText("The ringtone selected is "+prefs.getString("Audio", "Silent"));
        name.setText("The name entered is "+prefs.getString("Namekey",""));
        String selectedProduct = prefs.getString("products_list", "Camera");
        product.setText(selectedProduct);
    }
}



이 PreferenceFragment를 표시하기 위해 PrefFragActivity 클래스가 시작됩니다. PreferenceFragment에서 사용자가 선택한 preferences를 보여주기 위해 activity_pref_fragment_app.xml 인 layout 파일에 텍스트 뷰들이 정의돼 있습니다. 이 preference 뷰들에서 선택된 옵션을 찾기 위해  prefs로 명명된 SharedPreferences 객체를 생성했습니다. getInt(), getString(), getBoolean() 메소드를 통해 이 SharedPreferences 객체로부터 그 값들을 수집합니다.



CheckBoxPreference의 값을 읽기 위해서는 getBoolean() 메소드를 사용하고 CheckBoxPreference의 key를 전달해서 값을 받습니다. 이 Newskey라는 key가 SharedPreference 인스턴스의 getBoolean()메소드를 통해 전달 됐을 때 이 CheckBoxPreference는 true나 false 를 return 할 겁니다.


그리고 EditTextPreference는 SharedPreference 인스턴스의 getString()메소드로 Namekey라는 key를 전달합니다. 디폴트는 empty string 입니다. RingtonePreference와 ListPreference도 Audio와 product_list라는 key 값을 SharedPreference인스턴스의 getString() 메소드를 통해서 전달합니다. preference view들에서 사용자에 의해 선택된 preference들은 택스트뷰들을 통해 표시됩니다. ringtone의 디폴트값은 Silent 이고 product는 Camera 입니다.



이 PrefFragActivity.java 를 안드로이드에 보이도록 하려면 아래 코드를 AndroidManifest.xml에 추가하셔야 합니다.



<activity android:name=".PrefFragActivity" android:label="@string/app_name" />



Summary
 
 이 챕터에서는 fragments의 사용법과 다른 화면 크기에 맞게 어플리케이션을 적용하는 방법에 대해 다뤘습니다. fragment life cycle과 runtime시 동적으로 이 fragment를 추가하는 방법도 다뤘습니다. 그리고 fragment들 사이에 데이터를 전달하고 받는 방법도 배웠습니다. 그리고 ListFragment, DialogFragment 그리고 PreferenceFragment에 대해서도 배웠습니다.


반응형

Comment

[CookBook] Fragments 이해하기 -9-

2013. 10. 1. 05:11 | Posted by 솔웅


Displaying Dialog Through DialogFragment



안드로이드에서는 dialogs가 비동기적입니다. 동기적인 dialogs들은 이 dialog가 실행되는 동안 activity가 suspends 됩니다. 유저가 이 dialog를 사용하고 있는 동안에는 다른 것들이 올 수 없습니다. 비동기적인 dialogs들은 유저가 dialog를 사용하고 있는 동안에도 activity의 작업은 계속 됩니다. 이 activity는 콜백 메소드를 implement 함으로서 dialog와 함께 유저의 접근을 허용할 수 있습니다. 안드로이드의 dialogs는 자연스럽습니다. dialog가 open 돼 있는 동안 유저는 어플리케이션의 다른 부분에 접근할 수 없습니다. dialog를 비동기적으로 호출하는 방법의 잇점은 코딩을 줄일 뿐만 아니라 code를 통해서 이 dialog를 dismiss 할 수 있는 기능이 제공 된다는 것입니다.



DialogFragment base class를 extends 함으로서 DialogFragment를 사용하실 수 있습니다. 이 DialogFragment base 클래스는 Fragment 클래스에서 파생된 것입니다. 이 DialogFragment를 구현하기 위해 DialogFragmentApp이라는 프로젝트를 만들겠습니다. 여기에는 두개의 fragments들이 있을 건데요. 하나는 DialogFragment를 보여줄거고 다른 하나는 텍스트뷰를 보여줄 겁니다. DialogFragment와 유저의 상호작용은 두번째 fragment에 있는 텍스트뷰를 통해서 이뤄질 겁니다. DialogFragment에 있는 selected button은 두번째 fragment의 텍스트뷰를 통해서 display 됩니다.




이 DialogFragment를 만들기 전에 우선 텍스트뷰를 가지고 있는 fragment의 UI를 정의하겠습니다.


fragment2.xml



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <TextView 
        android:id="@+id/selectedopt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Select Open Dialog Button"
        android:textSize="@dimen/text_size"
        android:textStyle="bold" />
</LinearLayout>



LinearLayout 안에 id 가 selectedopt인 한개의 TextView 가 있습니다. 여기에 할당된 텍스트는 Select Open Dialog Button이네요. 볼드체이고 사이즈는 dimension resource의 text_size를 갖다가 사용합니다.
이 UI를 load 할 자바 클래스를 만들어 보죠.



Fragment2Activity.java



public class Fragment2Activity extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment2, container, false);
    }
}



아주 간단합니다. Fragment base 클래스를 extends 했고 onCreateView() 메소드 안에는 방금 만들었던 fragment2.xml 의 UI를 로드하기 위해 LayoutInflater를 사용했습니다.



이 fragment를 사용할 layout xml파일을 만들겠습니다.



activity_dialog_fragment_app.xml



<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >
    <fragment
        android:name="com.androidtablet.dialogfragmentapp.Fragment2Activity"
        android:id="@+id/fragment2"
        android:layout_weight="0"
        android:layout_width="wrap_content"
        android:layout_height="match_parent" />
    <Button
        android:id="@+id/dialog_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Open Dialog"
        android:textSize="@dimen/text_size" />
</LinearLayout>


Button 이 있습니다. 이 버튼을 클릭하면 DialogFragment가 나타나도록 할 겁니다. fragment와 button 이 LinearLayout 안에 배치해 있습니다. Fragment는 id 가 fragment2 이고 Button 은 id 가 dialog_button 입니다. Caption은 Open Dialog로 돼 있네요. 이 Caption은 dimention resource에서 text_size로 정의된 텍스트 크기를 가질 거구요.



이제 DialogFragment를 보여줄 코드를 작성할 차례입니다. DialogFragment를 보이기 위해서 자바 클래스는 DialogFragment 클래스를 extend 해야 합니다.



Fragment1Avtivity.java



public class Fragment1Activity extends DialogFragment{
    static Fragment1Activity newInstance(String title) {
        Fragment1Activity fragment = new Fragment1Activity();
        Bundle args = new Bundle();
        args.putString("title", title);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {   
        String title = getArguments().getString("title");
        Dialog diag = new AlertDialog.Builder(getActivity())
        .setIcon(R.drawable.ic_launcher)
        .setTitle(title)
        .setPositiveButton("OK", new DialogInterface.
            OnClickListener() {
            public void onClick(DialogInterface dialog, int
                whichButton) {
                ((DialogFragmentAppActivity) getActivity()).PositiveButton();
            }
        })
        .setNegativeButton("Cancel", new DialogInterface.
            OnClickListener() {
            public void onClick(DialogInterface dialog, int
                whichButton) {
                ((DialogFragmentAppActivity) getActivity()).NegativeButton();
            }
        }).create();
 
        return diag;
    }
}



보시다시피 DialogFragment를 생성하기 위해 이 자바 클래스는 DialogFragment 클래스를 extends 했습니다.
newInstance() 메소드는 이 fragment의 new instance를 생성합니다. 이 DialogFragment의 제목이 파라미터로 전달 됩니다. 이 파라미터는 Bundle 에 저장되고 fragment에 세팅 됩니다. (setArguments().



DialogFragment의 view hierarchy를 생성하기 위해 onCreateDialog() 메소드를 오버라이드 합니다. 그리고 title을 만들기 위한 Bundle 객체가 파라미터로 전달 됩니다. 이 onCreateDialog() 메소드 안에서 이 Dialog를 생성하기 위해 AlertDialog.Builder()를 사용하시는 것을 보실 수 있을 겁니다. 여기에 아이콘을 세팅하고 타이틀을 세팅하고 OK 버튼과 Cancel 버튼을 생성합니다.
이 OK와 Cancel 버튼에는 각각 onClickListener()를 달구요. OK를 클릭하면 PositiviButton() 메소드가 호출 디고 Cancel 버튼을 클릭하면 NegativeButton()이 호출 됩니다.



마지막에 이 메소드에서는 이 AlertDialog를 return 하구요.



이제 이 DialogFragment를 invoke 시킬 자바 액티비티 파일을 만들어야 합니다. 이 java activity file에는 OK와 Cancel을 눌렀을 때 실행되어야 할 PositiveButton()과 NegativeButton()메소드들이 구현되어야 하구요.



DialogFragmentAppActivity.java



public class DialogFragmentAppActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dialog_fragment_app);
        Button dialogButton = (Button)findViewById(R.id.dialog_button);
        dialogButton.setOnClickListener(new Button.OnClickListener(){
            @Override
            public void onClick(View arg0) {
                Fragment1Activity dialogFragment =
                    Fragment1Activity.newInstance( "Continue Processing?");
                dialogFragment.show(getFragmentManager(),  "Dialog Fragment Example");
            }
        });
    }

    public void PositiveButton() {
        TextView selectedOpt = (TextView)findViewById(R.id.selectedopt);
        selectedOpt.setText("You have selected OK button");
    }

    public void NegativeButton() {
        TextView selectedOpt = (TextView) findViewById(R.id.selectedopt);
        selectedOpt.setText("You have selected Cancel button");   
    }
}


onCreate() 메소드를 보시면 아까 만들었던 activity_dialog_fragment_app.xml 의 UI를 view로 set 하고 그 안의 Button에 대한 객체를 만듭니다. 그리고 이 버튼에 리스너를 달아서 클릭하면 Fragment1Activity.java 의 newInstance() 메소드를 호출합니다. 이때 타이틀이 될 Continue Processing?이라는 텍스트가 파라미터로 전달 되구요.
이 DialogFragment는 show() 메소드를 호출함으로서 visible 하게 됩니다. 이 show() 메소드는 이 fragment를 주어진 FragmentManager에 추가하게 되죠.
이 코드에는 아까 말했듯이 PositiveButton()과 NegativeButton() 메소드가 구현되었습니다. OK와 Cancel 버튼이 눌렸을 때 호출될 메소드 들입니다. 이 두 메소드에서는 모두 selectedopt라는 id를 가진 텍스트뷰의 객체를 만들고 여기에 text를 세팅하고 있습니다.



이 앱을 실행하고 Open Dialog 라는 버튼을 누르면 AlertDialog가 뜹니다. 여기서 OK를 누르면 이 Dialog 가 사라지고 왼쪽의 텍스트뷰에 You have selected OK Button이 쓰여지고 Cancel을 누르면 You have selected Cancel button 이 출력됩니다.



이 Dialog box는 사용자에게 중요한 메세지를 alert 하거나 사용자로부터 어떤 feedback을 받을 때 사용합니다.

반응형

Comment

[CookBook] Fragments 이해하기 -8-

2013. 9. 30. 06:43 | Posted by 솔웅


Displaying Options Using ListFragment



ListFragment는 이미 만들어진 리스트뷰를 가지고 있는 fragment 입니다. 리스트 뷰에 들어갈 내용들(data source)는 array나 cursor가 될 수 있습니다. 이 ListFragments를 이해하기 위해서 리스트뷰와 텍스트뷰를 포함하고 있는 어플리케이션을 만들어 보겠습니다. 리스트 뷰에는 아이템들이 들어갈 것이고 이 아이템을 선택하면 텍스트 뷰에 표시되도록 만들 겁니다. 이 앱에서 리스트뷰는 ListFragment를 통해서 표시됩니다. 이 새로운 프로젝트의 이름은 ListFragmentApp으로 하겠습니다. 우선 텍스트뷰를 표시할 fragment부터 만들겠습니다. fragment2.xml 이라는 layout xml을 res/layout 폴더에 만들겠습니다.


fragment2.xml



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <TextView 
        android:id="@+id/selectedopt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Please select a product"
       android:textSize="@dimen/text_size"
       android:textStyle="bold"  />
 </LinearLayout>




id가 selectedopt라는 텍스트뷰가 LinearLayout 안에 정의돼 있습니다. 일단 Please select a product라는 문자가 세팅돼 있습니다. 이 텍스트는 볼드체이고 size는 dimension resource에서 지정된 text_size를 사용합니다. 이 텍스트 뷰는 자바 코드에서 리스트뷰에서 선택된 아이템의 값을 표시하도록 만들어질 겁니다.



device의 화면크기에 맞게 리스트뷰의 아이템들이 표시되도록 하기 위해서 list_item.xml 파일을 res/layout 폴더 밑에 만들어 넣겠습니다.



list_item.xml



<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="6dp"
      android:textSize="@dimen/text_size"
      android:textStyle="bold" />
     


보시면 리스트뷰에 있는 아이템들은 6dp 의 패딩을 갖게 돼 있습니다. 그리고 텍스트는 bold 체이고 크기는 dimension resource에 있는 text_size를 갖다가 사용합니다.


fragment2.xml로 부터 fragment의 UI를 load 하기 위해 자바 클래스를 만들겠습니다.



Fragment2Activity.java

    


public class Fragment2Activity extends Fragment {
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment2, container, false);
    }

    public void dispOption(String msg){
        TextView selectedOpt = (TextView) getActivity().findViewById(R.id.selectedopt);
        selectedOpt.setText("You have selected "+msg);  
    }
}

 


이 클래스는 Fragment base 클래스를 extends 했습니다. onCreateView() 메소드를 오버라이드 해 fragment2.xml에서 정의한 텍스트 뷰 UI를 inflate 하기 위해 LayoutInflater를 그 안에서 사용했습니다.

 

이제 리스트뷰를 만들기 위해서 ListFragment를 사용할 겁니다. 이 ListFragment는 리스트뷰를 갖고 있습니다. 그러니까 이 fragment를 위해서 따로 UI를 정의할 필요는 없습니다. ListFragment 클래스를 extends한 자바클래스에서 동적으로 추가하시면 됩니다. 이 자바 클래스 안에서 리스트뷰에 들어갈 아이템들을 동적으로 할당할 겁니다. 그리고 리스트뷰에서 선택된 아이템을 Fragment2의 텍스트 뷰로 전달해서 화면에 표시하도록 할 거구요.
 
 

Fragment1Activity.java
 
 

public class Fragment1Activity extends ListFragment {
    String[] products={"Camera", "Laptop", "Watch",  "Smartphone", "Television"};

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ArrayAdapter<String> arrayAdpt = new ArrayAdapter  <String>(getActivity(),R.layout.list_item, products);
        setListAdapter(arrayAdpt);
    }

    @Override
    public void onListItemClick(ListView l, View v, int  position, long id) {
        Fragment2Activity frag = (Fragment2Activity) getFragmentManager().findFragmentById(R.id.fragment2);
        frag.dispOption(((TextView) v).getText().toString());                         
    }
}



보시다 시피 ListFragment를 extends 했습니다. 리스트뷰를 사용할 거기 때문이죠. 이 리스트뷰에 아이템을 표시하기 위해서 아이템들이 들어있는 products라는 배열을 만들었습니다. 그리고 onCreate() 메소드를 보시죠. 여기서는 ArrayAdapter를 만들어서 list_item.xml 을 할당해 주고 위에서 만든 products 배열도 할당해 줬습니다. 그리고 setListAdapter()메소드를 사용해서 이 ArrayAdapter를 세팅했구요. 이러면 이 ListView에 이 값들이 세팅이 되서 화면에 아이템으로 뿌려질 수 있습니다.



그리고 onListItemClick() 리스너를 사용했죠. 해당 아이템일 클릭되면 실행될 콜백 메소드 입니다. 여기서는 Fragment2Activity 클래스를 초기화한 frag라는 객체를 만들어서 이 frag의 disOption()이라는 메소드를 호출하도록 코딩을 했습니다 코딩할 때 텍스트뷰의 텍스트를 전달하고 있구요.



이제 이 두개의 fragment들을 함께 화면에 표시하도록 해야 합니다.
우선 layout xml을 만듭니다.


activity_list_fragment_app.xml



<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >
    <fragment
        android:name="com.androidtablet.listfragmentapp.Fragment1Activity"
        android:id="@+id/fragment1"
        android:layout_weight="1"
        android:layout_width="wrap_content"
        android:layout_height="match_parent" />
    <fragment
        android:name="com.androidtablet.listfragmentapp.Fragment2Activity"
        android:id="@+id/fragment2"
        android:layout_weight="0"
        android:layout_width="wrap_content"
        android:layout_height="match_parent" />
</LinearLayout>


여기에는 저 위에 만들었던 두개의 fragment가 들어있습니다. 이 두 fragment들을 추가하기 위해 <fragment> element를 사용했죠. android:name 이라는 속성에는 각각 fragment의 클래스 이름이 들어가 있습니다.
이제 이 layout xml을 load 할 자바 클래스를 만들겠습니다. 이 자바 클래스에는 특별히 코딩을 할 필요는 없습니다.



ListFragmentAppActivity.java



public class ListFragmentAppActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list_fragment_app);
    }
}



이렇게 하면 예제가 완성됐습니다.

반응형

Comment

[CookBook] Fragments 이해하기 -7-

2013. 9. 29. 16:47 | Posted by 솔웅


Implementing Communication Between Fragments



fragment들 사이에서 데이터를 전달할 수 있도록 하기 위해 Fragment 클래스에서는 두개의 메소드를 제공하고 있습니다. 바로 setArguments() 와 getArguments() 인데요. setArguments() 메소드는 fragment의 Bundle을 저장합니다. getArguments() 메소드는 이 전달된 정보를 얻기 위해 Bundle을 retrieve 하죠.



아래 코드는 fragment1에서 fragment2로 정보를 전달합니다. frag2_container는 layout xml 에서 지정한 fragment container의 ID 입니다.



#1 Fragment2Activity fragment2 = new Fragment2Activity();
#2 Bundle args = new Bundle();
#3 String message = "Message from Fragment1";
#4    if(null==fragmentManager.findFragmentByTag(TAG2)){
#5        Fragment2Activity fragment2 = new Fragment2Activity();
#6      args.putString("msg", message);
#7      fragment2.setArguments(args);
#8      fragmentTransaction.replace(R.id.frag2_container,fragment2);
#9        String tag = null;
#10        fragmentTransaction.addToBackStack(tag);
#11        fragmentTransaction.commit();
#12 }



#1에 있는 Fragment2Activity는 fragment2의 자바 클래스를 말합니다. fragment2라는 자바 클래스 인스턴스를 생성했습니다.
#2에서는 args라고 하는 Bundle 객체를 생성했습니다.
#3에서는 message라고 하는 String 객체를 생성했죠. 이 메세지는 fragment2에 전달 될 겁니다.
#4번에서는 TAG2라는 fragment가 있는지 여부를 체크합니다. TAG2는 fragment2를 말하는 거니까 fragment2가 있는지 여부를 체크하는 겁니다.
#5번은 Fragment2Activity 자바클래스의 인스턴스를 만들고 #6번에서는 args 번들에 3번에서 정의했던 메세지를 세팅합니다.
#7에서는 fragment2에 이 Bundle 을 세팅하구요.
#8번과 #9번에서는 이전의 fragment를 navigating하는 부분이구요.



이 activity stack은 이전의 activity들을 계속 track 합니다. Back 버튼을 누르면 activity stack에 있는 activity가 pop up 되서 그 activity 가 화면에 보여지게 됩니다. 다른 말로 이 activity stack은 Back 버튼을 누르면 이전 화면으로 돌아갈 수 있도록 만드는 거죠.



fragment도 이러한 개념을 사용합니다. back stack에 FragmentTransaction을 추가하려면 commit() 하기 전에 FragmentTransaction의 addToBackStack() 메소드를 호출해야 합니다.



위 코드에서 보면 처음에는 xml에 있는 fragment가 화면에 보였다가 두번째 fragment로 replace됐습니다. 그리고 이전 fragment는 BackStack에 넣구요. 그래서 Back 버튼을 누르면 이전의 FragmentTransaction이 돌아와서 이전의 fragment가 화면에 보여질 겁니다.



Bundle을 통해서 위에 setArguments()메세지를 통해 세팅된 정보를 어떻게 다음 fragment에서 이 메세지를 받고 display하는지에 대해서도 궁금할 겁니다. 그 답은 Bundle을 사용해서 fragment에서 메세지를 받는 것은 getArgument() 메소드를 사용한다 입니다.
아래에 샘플 코드가 있습니다.



String messageReceived = "";
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    ViewGroup container, Bundle savedInstanceState) {
        Bundle bundle = getArguments();
        if(bundle != null) {
            messageReceived = bundle.getString("msg");
        }
    }
}


fragment끼리의 communication을 이해하기 위해 FragmentsByCodeApp 어플리케이션에서 fragment1에서 fragment 2로 메세지를 보내는 것을 구현하겠습니다. 이를 위해 FragmentsByCodeAppActivity.java 의 코드를 수정합니다.



public class FragmentsByCodeAppActivity extends Activity implements OnOptionSelectedListener {
    private static final String TAG1 = "1";
    private static final String TAG2 = "2";
   
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragments_by_code_app);
        FragmentManager fragmentManager = getFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        if(null==fragmentManager.findFragmentByTag(TAG1)){
            Fragment1Activity fragment1 = new Fragment1Activity();
            fragmentTransaction.add(R.id.frag1_container, fragment1, TAG1); 
        }
        Bundle args = new Bundle();
        String message="Message from Fragment 1";  

        if(null==fragmentManager.findFragmentByTag(TAG2)){
            Fragment2Activity fragment2 = new Fragment2Activity();
            args.putString("msg", message);
            fragment2.setArguments(args);

            fragmentTransaction.add(R.id.frag2_container, fragment2, TAG2); 
        }
        fragmentTransaction.commit();
        }

    public void onOptionSelected(String msg){
        Fragment2Activity frag2 = (Fragment2Activity) getFragmentManager().findFragmentById(R.id.frag2_container);
        frag2.dispOption(msg);             
    }
}



우선 Bundle 인스턴스인 args를 생성했습니다. 이 args 객체에 message라는 String 객체를 put 했구요. 그리고 이 Bundle을 fragment2에 넣었습니다. 그 다음에 fragment2에서는 이 Bundle 을 getArguments() 메소드를 이용해서 이 정보를 받습니다. 그 받는 부분을 한번 보겠습니다.



public class Fragment2Activity extends Fragment {
    TextView selectedOpt;
    String messageReceived="";


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View vw= inflater.inflate(R.layout.fragment2,  container, false);
           Bundle bundle=getArguments(); 
           if(bundle !=null) {
               messageReceived = bundle.getString("msg");
               selectedOpt = (TextView) vw.findViewById(R.id.selectedopt);
               selectedOpt.setText(messageReceived);
           }
           return vw;     

    }

    public void dispOption(String msg){
        TextView selectedOpt = (TextView) getActivity().findViewById(R.id.selectedopt);
        selectedOpt.setText("You have selected "+msg);  
    }
}


이렇게 getArguments()를 사용해서 해당 정보를 받습니다. 이렇게 받은 정보는 TextView에 뿌려질 거구요.



이 어플리케이션을 실행하면 아래와 같은 화면을 볼 수 있습니다.






Fragment base 클래스 이외에도 fragment는 다른 몇개의 Fragment 클래스의 subclass들을 extend 할 수 있습니다. DialogFragment, ListFragment, PreferenceFragment들이 그것입니다.

반응형

Comment

[CookBook] Fragments 이해하기 -6-

2013. 9. 29. 06:47 | Posted by 솔웅


Fragment1Activity.java



public class Fragment1Activity extends Fragment {
    OnOptionSelectedListener  myListener;
    @Override
    public View onCreateView(LayoutInflater inflater,
        ViewGroup container, Bundle savedInstanceState) {
        Context c = getActivity().getApplicationContext();
        View vw = inflater.inflate(R.layout.fragment1, container, false);
        String[] products={"Camera", "Laptop", "Watch", "Smartphone", "Television"};
        ListView productsList = (ListView) vw.findViewById(R.id.products_list);
        ArrayAdapter<String> arrayAdpt= new ArrayAdapter <String>(c, R.layout.list_item, products);
        productsList.setAdapter(arrayAdpt);
        productsList.setOnItemClickListener(new OnItemClickListener(){
            @Override
            public void onItemClick(AdapterView<?> parent, View v, int position, long id){
                myListener.onOptionSelected(((TextView) v).getText().toString());  
            }
        });
        return vw;
    }
   
    public interface OnOptionSelectedListener {
        public void onOptionSelected(String message);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            myListener = (OnOptionSelectedListener) activity;
        } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString() + " must implement OnItemClickListener");
        }
    } 
}




이 Fragment1Activity 클래스는 Fragment 클래스를 extends 했습니다. onCreateView() 메소드 안에서는 LayoutInflater 객체가 fragment1.xml 에서 정의된 ListView를 inflate하기 위해 사용됐습니다. 이 리스트 뷰는 product_list라는 id를 가진 xml 내의 listview 를 가져와서 productsList 라는 객체에 넣었습니다. 그리고 ArrayAdapter 를 사용해서 list_item.xml 을 이용해서 각 아이템들을 세팅하고 구체적인 아이템 내용은 products 배열에서 가져오고 있습니다. 그리고 이 Adapter를 productsList에 세팅합니다. 그리고 productsList 에 onItemClickListener를 달아서 리스트뷰 내의 아이템이 선택되면 어떤 일을 하도록 정의합니다. setOnItemClickListener 안에는 inItemClick()이라는 콜백 메소드가 있는데요. 아이템이 클릭되면 이 메소드가 자동으로 호출됩니다. 여기서는 아이템이 클릭되면 onOptionSelected() interface를 호출하네요. 그 인터페이스 안에는 onOptionSelected() 메소드가 선언돼 있구요. 이 fragment와 연관된 activity는 OnOptionSelectedListener 인터페이스를 implements 했을 겁니다. 그리고 이 인터페이스의 body인 onOptionSelected() 메소드를 구현했을 거구요. 그러니까 여기서 아이템이 클릭되면 구체적인 동작은 이 OnOptionSelectedListener 인터페이스를 implements 한 activity에서 구현됩니다.



이제 만들어야 할 파일은 Fragment2Activity.java인데요. 여기서는 아래 두가지 일을 해야 합니다.

- fragment2.xml 에서 UI를 load 합니다.
- 유저가 선택한 제품을 출력할 dispOption 메소드를 정의 해야 합니다.



이 두가지 일을 하기 위해서 아래와 같이 코딩을 합니다.



Fragment2Activity.java



public class Fragment2Activity extends Fragment {
    TextView selectedOpt;
    String messageReceived="";

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View vw= inflater.inflate(R.layout.fragment2,  container, false);
           Bundle bundle=getArguments(); 
           if(bundle !=null) {
               messageReceived = bundle.getString("msg");
               selectedOpt = (TextView) vw.findViewById(R.id.selectedopt);
               selectedOpt.setText(messageReceived);
           }
           return vw;     
    }

    public void dispOption(String msg){
        TextView selectedOpt = (TextView) getActivity().findViewById(R.id.selectedopt);
        selectedOpt.setText("You have selected "+msg);  
    }
}


여기서도 fragment를 생성하기 위해 Fragment 클래스를 extends 했습니다. 그리고 onCreateView() 메소드를 오버라이드 해서 그 안에서 LayoutInflater 객체를 이용해 fragment2.xml 안에 정의된 TextView를 inflate 했습니다. 리스트뷰에서 선택된 제품 이름은 dispOption() 메소드로 전달되서 이 TextView 를 통해 화면에 뿌려질 겁니다.



이제 맨 처음 만든 activity_fragments_by_code_app.xml 을 불러와서 사용할 java 파일을 만들 차례입니다.
이 xml layout에 정의된 frag1_container와 frag2_container로 id가 정의된 LinearLayout 에 위에서 만든 fragment들을 배치시켜야겠죠.
소스 내용은 아래와 같습니다.



FragmentsByCodeAppActivity.java



public class FragmentsByCodeAppActivity extends Activity implements OnOptionSelectedListener {
    private static final String TAG1 = "1";
    private static final String TAG2 = "2";
   
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragments_by_code_app);
        FragmentManager fragmentManager = getFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        if(null==fragmentManager.findFragmentByTag(TAG1)){
            Fragment1Activity fragment1 = new Fragment1Activity();
            fragmentTransaction.add(R.id.frag1_container, fragment1, TAG1); 
        }
        Bundle args = new Bundle();
        String message="Message from Fragment 1";  
        if(null==fragmentManager.findFragmentByTag(TAG2)){
            Fragment2Activity fragment2 = new Fragment2Activity();
            args.putString("msg", message);
            fragment2.setArguments(args);
            fragmentTransaction.add(R.id.frag2_container, fragment2, TAG2); 
        }
        fragmentTransaction.commit();
        }

    public void onOptionSelected(String msg){
        Fragment2Activity frag2 = (Fragment2Activity) getFragmentManager().findFragmentById(R.id.frag2_container);
        frag2.dispOption(msg);             
    }
}


보시면 FragmentManager 객체를 보실 수 있을 겁니다. fragment에 접근할 수 있도록 해주는 클래스 입니다. fragment를 추가하거나 제거하거나 replace를 해야 될 때 사용할 FragmentTransaction도 이 FragmentManager의 beginTransaction() 메소드를 사용해서 정의했습니다.



그리고 frag1_container에 첫번째 fragment를 넣고 frag2_container에 두번째 fragment를 넣었습니다. 넣기 전에 이미 존재하는지 여부를 먼저 체크했습니다. 이때 Fragmentmanager의 findFragmentByTag() 메소드를 사용했죠. 두번째 fragment에는 메세지도 argument로 세팅했습니다. 그 다음에 FragmentTransaction 클래스의 commit()메소드를 사용해서 작업을 완료합니다.



그 아래에 onOptionSelected() 메소드가 있는데요. 이것은 ListView에서 아이템이 선택됐을 때 실행될 메소드 입니다. 선택된 제품을 fragment2의 TextView에 표시하도록하는 메소드죠.



이로서 동적으로 fragment를 생성하고 할당해서 사용하는 코드가 완성됐습니다.

반응형

Comment

[CookBook] Fragments 이해하기 -5-

2013. 9. 28. 07:28 | Posted by 솔웅


Creating Fragments Dynamically at Runtime



layout xml 파일에 fragment 가 <fragment> element를 사용해서 정의 되었어도 runtime 시 자바코딩에 의해서 이 fragment를 재정의 할 수 있습니다. 액티비티에 fragment를 dynamic 하게 생성,추가,replace 하기 위해 FragmentManager를 사용하실 수 있습니다.



이와 관련한 예제를 다룰건데요. FragmentsByCodeApp이라는 안드로이드 프로젝트를 생성하겠습니다. 이 앱에서는 두개의 fragment를 dynamic하게 생성할 겁니다. Fragment1과 Fragment2인데요. Fragment1은 선택 위젯인 ListView를 가지게 되고 여기에 product들이 display 될 겁니다. Fragment2는 TextView를 가지고 있고 Fragment1의 리스트뷰에서 선택된 제품이 display 될 겁니다.



이 두 Fragment들을 표시하기 위해 적당한 layout xml을 만들어야 합니다.



activity_fragments_by_code_app.xml



<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >
    <LinearLayout
        android:id="@+id/frag1_container"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:orientation="horizontal"/>
    <LinearLayout
        android:id="@+id/frag2_container"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_weight="0"
        android:orientation="horizontal"/>
</LinearLayout>




여기 보시면 두개의 LinearLayout가 있습니다. 각각 id를 가지고 있죠. 하나는 frag1_container이고 다른 하나는 frag2_container 입니다. 자바 코드에서 이 아이디를 가지고 동적으로 fragment들을 생성하고 다룰 겁니다. 전체 틀을 구성하는 LinearLayout은 horizontal 로 돼 있죠. 그러니까 두 fragment들은 좌우로 배치 될 겁니다. 첫번째 fragment에는 제품을 표시할 리스트뷰가 들어갈 것이고 두번째 fragment에는 이 제품을 표시할 텍스트 뷰가 들어갈 겁니다. 이 두 fragment들에 view를 정의하기 위해 두개의 xml 파일을 만들겠습니다. fragment1.xml과 fragment2.xml 인데요. res/layout 폴더 밑에 만들어 넣습니다.
첫번째 fragment에 리스트뷰를 넣기 위해 아래와 같이 만듭니다.



fragment1.xml



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#0000FF"  >
    <ListView
        android:id="@+id/products_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:drawSelectorOnTop="false" />
</LinearLayout>



안에 리스트 뷰가 있죠? id 는 products_list 로 했습니다. 두개의 fragment를 구별하기 쉽게 첫번째는 배경색을 파란색으로 했습니다.
두번째 fragment에 텍스트뷰를 정의하기 위해 아래와 같은 xml 파일을 만듭니다.


fragment2.xml



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >   
    <TextView 
        android:id="@+id/selectedopt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text=" Please select a product "
        android:textSize="@dimen/text_size"
        android:textStyle="bold"  />  
</LinearLayout>



selectedopt라는 아이디를 가진 텍스트뷰가 있습니다. 그리고 text가 이미 정해져 있는데요. "Please select a product" 라고 돼 있습니다. 텍스트 사이즈는 dimention 리소스 안에 있는 사이즈를 불러다 쓰구요.
디바이스의 화면 크게에 맞게 리스트 아이템들을 표시하기 위해 아래 xml 하일을 하나 더 만들겠습니다.



list_item.xml



<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="6dp"
      android:textSize="@dimen/text_size"
      android:textStyle="bold" />
     

여기를 보면 각 아이템은 리스트 뷰 내에서 6dp 만큼 패딩을 가질 겁니다. 그리고 bold체로 설정을 했고 폰트 싸이즈는 dimention 리소스 안에 있는 사이즈를 불러다 씁니다.
이제 fragment1.xml과 fragment2.xml 에 설정된 UI를 로드할 자바코드를 만들 차례입니다. 두개의 자바코드가 만들어 져야겠죠. 하나는 Fragment1Activity.java 이고 나머지 하나는 Fragment2Activity.java 입니다.


이 자바 코드는 다음 글에서 다뤄 보겠습니다.

반응형

Comment

[CookBook] Fragments 이해하기 -4-

2013. 9. 27. 06:06 | Posted by 솔웅


Understanding the Role of FragmentManager and FragmentTransaction in Handling Fragments




FragmentManager는 이름 그대로 액티비티 내에서 fragments를 관리하는데 사용됩니다. 액티비티에서 사용 가능한 fragment에 접근할 수 있게 해 주고 fragments를 추가하거나 제거하거나 재배치할 필요가 있을 때 FragmentTransaction을 사용합니다. FragmentManager에 접근하려면 getFragmentManager() 메소드를 사용합니다.



FragmentManager fragmentManager = getFragmentManager();



fragment transaction을 사용하려면 FragmentTransaction의 인스턴스를 사용합니다.


FragmentTransaction fragmentTransaction = fragmentMaanger.beginTransaction();




새로운 FragmentTransaction은 FragmentManager의 beginTransaction() 메소드를 사용해서 생성합니다.



private static final String TAG1 = "1";
FragmentManager fragmentManager = getFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment1Activity fragment = new FragmentActivity();
fragmentTransaction.add(R.id.fragment_container, fragment, TAG1);
fragmentTransaction.commit();


여기 보시면 Fragment1Activity는 fragment의 자바 클래스입니다. 이것은 XML 파일로부터 fragment의 UI를 로드해서 사용하죠. 밑에서 두번째 줄을 보시면 fragment_container가 id라는 걸 알 수 있을 겁니다. 아마 이 id는 LinearLaout이나 FrameLayout 일 거라고 예상할 수 있을 겁니다. 그리고 commit()메소드가 보이는데요. 이것은 변화된 사항을 적용하기 위해 사용하는 메소드 입니다. 이 commit()은 즉시 발효되는 것은 아니고 쓰레드가 준비 됐을 때 발효가 될 겁니다. 그러니까 방금 생성한 fragment에 대한 reference를 곧바로 get하는 것은 문제가 생길 수 있습니다.



Note
fragment를 dynamic 하게 추가하려면 container view는 fragment 가 display될 view 가 있는 layout에 존재해야 합니다.



fragment를 추가하기 전에 이것이 존재하는지 확인할 필요가 있을 겁니다. 아래처럼 하시면 됩니다.



private static final String TAG1 = "1";
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
if(null == fragmentManager.findFragmentByTag(TAG1)){
    Fragment1Activity fragment = new Fragment1Activity();
    fragmentTransaction.add(R.id.fragment_container, fragment, TAG1);
}
fragmentTransaction.commit();



FragmentManager의 findFragmentByTag() 메소드가 주어진 tag의 fragment가 있는지 여부를 체크 하고 있습니다. 이 findFragmentById() 메소드는 activity layout 에 추가된 fragment를 identify 합니다. 아니면 findFragmentByTag() 메소드를 사용할 수도 있습니다. 위에 있는 Fragment1Activity는 fragment의 layout 파일에서 정의된 view들이 로드되는 자바 클래스 입니다.



fragment나 content를 replace하기위해 FragmentTransaction의 replace()메소드를 사용합니다.



private static final String TAG2 = "2";
fragmentTransaction.replace(R.id.fragment_container, fragment2, TAG2);



여기 보시면 fragment2의 view는 액티비티 layout의 fragment_container안에있는 view를 다시 replace할 겁니다. fragment를 없애려면 findFragmentById()나 findFragmentByTag()메소드를 사용해서 그 fragment 구분해 내야 합니다. 아래 코드는 findFragmentById()메소드를 사용해서 fragment를 정의하고 그 fragment를 remove 하는 과정을 보여줍니다.


FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment fragment = fragmentManager.findFragmentById(R.id.fragment);
fragmentTransaction.remove(fragment);
fragmentTransaction.commit();



위에 보시면 없애고자 하는 fragment를 정의하기 위해 findFragmentById()를 사용하신 걸 보실 수 있습니다. 이것을 findFragmentByTag()를 사용하시려면 아래와 같이 사용하실 수 있습니다.
Fragment fragment = fragmentManager.findFragmentByTag(TAG1);

반응형

Comment

  1. 2019.12.25 21:45

    이해가 쉽네요 잘보고 갑니다 ㅎㅎ

[CookBook] Fragments 이해하기 -3-

2013. 9. 26. 19:15 | Posted by 솔웅


Adding and Removing Fragments with Device Orientation



fragments를 사용할 때 가장 큰 잇점 중의 하나는 디바이스를 가로 세로 방향으로 돌려서 볼 때 쉽게 fragment를 넣고 뺄 수 있어서 적당한 layout의 view를 사용자에게 보여질 수 있도록 하는 것입니다. 그럼 이렇게 만들기 위해 이전 글에서 다뤘던 소스들을 약간 수정해 보겠습니다. (안드로이드 전화기에서 세웠을 때는 한개의 fragment만 보여지도록요.)


activity_foreground_fragment.xml 에서 두번째 fragment 부분을 주석 처리 합니다.



<LinearLayout 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"
    android:orientation="horizontal" >
    <fragment
        android:name="com.androidtablet.foregroundfragmentapp.Fragment1Activity"
        android:id="@+id/fragment1"
        android:layout_weight="1"
        android:layout_width="wrap_content"
        android:layout_height="match_parent" />
 <!--    <fragment
        android:name="com.androidtablet.foregroundfragmentapp.Fragment2Activity"
        android:id="@+id/fragment2"
        android:layout_weight="0"
        android:layout_width="wrap_content"
        android:layout_height="match_parent" /> -->
</LinearLayout>




이렇게 하는 이유는 오직 1개의 fragment만 화면에 표시하기 위해서죠. 이 경우는 안드로이드 전화기가 세워졌을 때 뿐입니다. 눕혀졌을 때나 태블릿 일 경우에는 두개의 fragments들이 좌우로 배치되어야 하겠죠.
그러면 이런 경우를 위해서 어떤 작업을 해야 합니다. 우선 res 폴더 아래에 layout-land 폴더를 생성하세요. 그리고 그 폴더에 activity_foreground_fragment.app 을 복사해 넣으세요.



전화기가 세워졌다가 옆으로 뉘여졌을 때 이 activity는 layout-land 에 있는 layout xml 파일을 사용해서 화면에 표시해 줄 겁니다. 전화기가 다시 세워지면 layout 폴더 안에 있는 xml 을 사용해서 화면을 갱신시킬거구요.



layout_land 폴더에 있는 activity_foreground_fragment.xml 파일에서는 위에 있는 주석을 풀어 주시면 됩니다.



그 다음에는 Fragment1Activity.java 파일을 수정하시면 됩니다.



public class Fragment1Activity extends Fragment {
    OnOptionSelectedListener  myListener;
    boolean large, xlarge;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Context c = getActivity().getApplicationContext();
        View vw = inflater.inflate(R.layout.fragment1, container, false);
        String[] products={"Camera", "Laptop", "Watch",  "Smartphone", "Television"};
        large = ((getResources().getConfiguration().screenLayout  & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_LARGE);             
        xlarge =((getResources().getConfiguration().screenLayout  & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE);      
        ListView productsList = (ListView) vw.findViewById(R.id.products_list);
        ArrayAdapter<String> arrayAdpt= new ArrayAdapter<String> (c, R.layout.list_item, products);
        productsList.setAdapter(arrayAdpt);        
        productsList.setOnItemClickListener(new OnItemClickListener(){
            @Override
            public void onItemClick(AdapterView<?> parent, View v, int position, long id){                 
                if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE || large || xlarge){                                      
                    myListener.onOptionSelected(((TextView)  v).getText().toString());  
                 } else {
                    Intent intent = new Intent(getActivity().getApplicationContext(),  DisplayItemActivity.class);
                    intent.putExtra("item", ((TextView) v).getText().toString());
                    startActivity(intent);
                 }

            }
        });
        return vw;
    }
   
    public interface OnOptionSelectedListener {
        public void onOptionSelected(String message);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            myListener = (OnOptionSelectedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + "  must implement OnItemClickListener");
        }
    }   
}



위의 굵은 글씨체가 추가된 부분입니다. 리스트뷰의 아이템이 클릭 됐을 때 onItemClick() 메소드를 보면  우선 device가 landscape 이거나 large 혹은 xlarge 즉 태블릿인 경우를 체크합니다. 만약에 그렇다면 fragment2를 보여줍니다. 즉 그럴 경우 onOptionSelected() 메소드가 호출되고 이때 선택된 아이템의 이름이 파라미터로 전달 되는 겁니다. 그러면 Fragment2에서 이 파라미터를 받아서 화면에 출력하겠죠.



위 경우가 아니라면 즉 디바이스가 전화기이고 세워진 상태라면 (Portrait orientation) fragment2는 호출 되지 않을 겁니다. 리스트뷰에 있는 아이템이 선택 됐으면 그 이름이 다른 화면에 표시 되어야 합니다. 다른 화면에 표시된다는 얘기는 다른 activity가 필요하다는 얘기입니다. 그리고 다른 activity로 옮길 경우에는 Intent를 사용해야 되구요. 이 경우 현재의 Context를 같이 보내 주어야 합니다. getActivity().getApplicationContext(). 그리고 불려질 Activity를 정해주고 (DisplayItemActivity.class) 그리고 추가적으로 데이터를 넣어서 이것을 전달합니다. 마지막으로 startActivity() 를 하면 다음 화면이 불려 오겠죠.



그러면 이제 할 일을 DisplayItemActivity.java 를 만들어야 겠네요.
소스 코드는 아래와 같습니다.



public class DisplayItemActivity extends Activity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
            finish();
            return;
        }
        setContentView(R.layout.fragment2);
        Bundle extras = getIntent().getExtras();
        if (extras != null) {
            String selectedItem = extras.getString("item");
            TextView selectedOpt = (TextView) findViewById(R.id.selectedopt);
            selectedOpt.setText("You have selected "+selectedItem);
        }
    }
}



onCreate() 만 있죠? 맨 처음에 이 기계가 Landscape 모드이면 그냥 끝내고 return 을 해 버립니다. 그리고 난 후 fragment2.xml 을 view로 세팅하죠.
그리고 나서 이전 파일에서 전달해 준 데이터를 체크합니다. getExtra()가 그 일을 합니다. 그리고 item이란 key 값을 이용해서 그 value 값을 selectedItem에 담구요. 그 값을 텍스트뷰에 넣어 줍니다.


이렇게 하면 완성된 겁니다.
그런데 한가지 더 해야 될 게 있는데요.
DisplayItemActivity 라는 Activity가 새로 생겼으면 이것을 AndroidManifest.xml 에 신고를 해야 합니다.
<activity android:name=".DisplayItemActivity" android:label="@string/app_name" /> 을 추가해야 하는데요.
혹시 Manifest 파일에 액티비티 추가하는 방법을 모르시면 관련 글을 보시고 참고하시기 바랍니다.



그리고 한가지 더 해야 할 일이 있는데요.
현재 layout과 layout-land 폴더에만 xml layout 파일이 있습니다. 이건 전화기에만 해당되는데요. 태블릿에도 적용시키기 위해서 두개의 폴더를 더 만듭니다. layout-sw600dp 와 layout-sw720dp 폴더를요. 그리고 이 앱에서는 전화기의 landscape 인 경우와 7인치 10인치 태블릿의 landscape, portrait orientation 모두 그 구성이 같습니다. 그러니까 lauout-land 에 있는 activity_foreground_fragment_app.xml 파일을 복사해서 위 두 폴더 밑에 넣습니다.



이제 이 앱을 돌리면 안드로이드 전화기인 경우에는 Portrait orientation인 경우 첫화면에는 리스트뷰만 표시되고 그 리스트 뷰의 아이템을 클릭했을 때 그 이름이 두번째 화면에서 표시되게 됩니다. landscape orientation으로 바뀌게 되면 태블릿과 마찬가지로 두개의 fragment들이 좌우로 배치되고 좌측의 리스트뷰 아이템을 클릭하면 그 이름이 우측의 텍스트 뷰에 출력되게 됩니다.

반응형

Comment

[CookBook] Fragments 이해하기 -2-

2013. 9. 26. 06:16 | Posted by 솔웅


Creating Foreground Fragments, and Knowing the Difference Between Foreground and Background Fragments



Foreground Fragments는 유저와 상호작용이 있는 fragment입니다. 입력을 받고 일을 진행하고 원하는 결과를 display 하는것 같은 상호 작용이 있게 됩니다. 이 Foreground Fragments는 view들의 조합을 갖고 있고 이 view들을 화면에 보여 줍니다. Background Fragments는  반대로 유저와 상호작용이 없이 백그라운드에서 activity내에서 어떤 일을 수행하는 경우에 사용 됩니다. 이 Background Fragment에는 어떤 User Interface(UI) 도 포함되지 않습니다. 그러니까 이 background fragments를 정의할 때 onCreateView()는 호출 될 필요가 없겠죠.



Foreground Fragments에 대한 개념을 이해하기 위해 ForegroundFragmentApp이라는 안드로이드 프로젝트를 생성하겠습니다. 여기에는 두개의 Fragment를 만들 겁니다. Fragment1은 selection 위젯, 리스트 뷰가 있게 될 겁니다. 여기에는 선택할 수 있는 몇가지 제품들이 들어갈 거구요. Fragment2에는 TextView가 세팅이 될 텐데요. 여기에는 Fragment1의 리스트 뷰에서 선택 된 제품이 표시 될 겁니다.



이 두개의 fragment들은 각각 개별적인 XML layout 파일을 사용해서 view들을 정의 합니다. 그래서 이 두개의 fragments들을 위해 fragment1.xml 과 fragment2.xml을 res/layout 폴더에 넣겠습니다.



첫번째 fragment에 리스트뷰를 정의하기 위해 아래와 같이 fragment1.xml을 만듭니다.



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#0000FF" >
    <ListView
        android:id="@+id/products_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:drawSelectorOnTop="false"  />
 </LinearLayout>



보시면 리스트 뷰 선택 위젯이 정의된 것이 보이죠? id 는 products_list 입니다. 이 fragment의 배경색은 파란색으로 해서 나중에 두번째 fragment랑 구분이 될 수 있도록 만들었습니다.
아래는 fragment2.xml 입니다.



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <TextView 
        android:id="@+id/selectedopt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text=" Please select a product "
        android:textSize="@dimen/text_size"
        android:textStyle="bold"  />
</LinearLayout>



id가 selectedopt 인 TextView 가 보이실 겁니다. 텍스트가 이미 지정이 돼 있죠. 이 문자의 스타일은 bold 이고 사이즈는 dimen.xml 에 있는 사이즈를 채택할 겁니다. 그리고 화면에 보여주겠죠.

NOTE
dimens.xml 파일은 이미 values,values-sw600dp, values-sw720dp 폴더에 있는 상황을 염두에 둔 상황입니다. 그 파일 안에 각각 화면 크기에 맞게 text_size가 세팅 돼 있어야 합니다.



리스트 뷰의 디폴트 사이즈는 전화기에 알맞게 돼 있습니다. 그러니까 태블렛에는 약간 작겠죠. 리스트 뷰의 리스트 아이템을 디바이스에 맞게 resize 하려면 list_item.xml 이라는 XML 파일을 하나 더 만들어야 합니다.



<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="6dp"
      android:textSize="@dimen/text_size"
      android:textStyle="bold" />
     
     


보시면 리스트 뷰의 리스트 아이템은 6dp의 padding을 가지게 됩니다. 그리고 bold체이고 크기는 dimension 리소스에 정의된 text_size 를 가지게 됩니다.


각 fragment는 XML 파일에서 UI를 로드하는 별도의 Java 클래스를 갖고 있습니다. 그러니까 이 앱은 두개의 fragment와 두개의 자바 클래스를 가지게 되는 거죠. Fragment1Activity.java와 Fragment2Activity.java를 추가 하겠습니다.



Fragment1Activity.java



public class Fragment1Activity extends Fragment {
    OnOptionSelectedListener  myListener;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Context c = getActivity().getApplicationContext();
        View vw = inflater.inflate(R.layout.fragment1, container, false);
        String[] products={"Camera", "Laptop", "Watch",  "Smartphone", "Television"};
        ListView productsList = (ListView) vw.findViewById(R.id.products_list);
        ArrayAdapter<String> arrayAdpt= new ArrayAdapter<String> (c, R.layout.list_item, products);
        productsList.setAdapter(arrayAdpt);        
        productsList.setOnItemClickListener(new OnItemClickListener(){
            @Override
            public void onItemClick(AdapterView<?> parent, View v, int position, long id){                 
              myListener.onOptionSelected(((TextView)  v).getText().toString());  
            }
        });
        return vw;
    }
   
    public interface OnOptionSelectedListener {
        public void onOptionSelected(String message);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            myListener = (OnOptionSelectedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + "  must implement OnItemClickListener");
        }
    }   
}



보시면 이 클래스가 Fragment 클래스를 extends 하신 걸 알게 될 겁니다. 이 fragment에 접근하고 UI를 그려넣으려면 onCreateView() 메소드를 오버라이드 해야 합니다. 이 onCreateView() 메소드 안에는 LayoutInflater object 가 있는데요. 이것은 fragment1.xml 에서 정의했던 리스트 뷰 UI를 inflate하기 위한 겁니다. 리스트뷰와 텍스트 뷰는 XML에서 정의한 것이죠. fragment1.xml 에는 리스트 뷰가 있고 이 리스트 뷰는 fragment1Activity.java에서 productList라는 객체를 만들어 가져다 씁니다. 텍스트 뷰는 fragment2.xml 에 정의 되어 있고 Fragment2Activity.java에서 selectedOpt라는 객체 담아서 쓰고요.


ArrayAdapter 에는 리스트의 아이템에 products 객체에 있는 배열을 세팅하고 있습니다. 그리고 이 각 아이템에 setOnItemClickListener를 답니다. onItemClick() 콜백 메소드를 오버라이드 해서 이 리스트 뷰 내의 한 아이템을 클릭하면 그 안의 text를 받아서 표시하는데요. OnOptionSelectedListener 인터페이스가 정의 돼 있는  것이 보이죠? 그 안에는 onOptionSelected() 메소드의 body 가 있습니다. 이렇게 하면 리스트 뷰의 어떤 아이템이 선택되면 onoptionSelected() 메소드가 호출이 되고 선택된 아이템 이름을 파라미터로 해서 이 메소드에 전달 되게 됩니다. 



두번째 파일인 fargment2.xml의 UI를 로드하기 위해서 아래 자바코드를 작성합니다.



Fragment2Activity.java



public class Fragment2Activity extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment2, container, false);
    }

    public void dispOption(String msg){
        TextView selectedOpt = (TextView) getActivity().findViewById(R.id.selectedopt);
        selectedOpt.setText("You have selected "+msg);  
    }
}



위에서 봤던 자바파일 처럼 Fragment 클래스를 extends 했습니다. 그리고 onCreateView() 메소드도 오버라이드 했구요. 여기서도 LayoutInflater object가 fragment2.xml 안에 정의 된 텍스트 뷰를 inflate 하기 위해 사용됩니다. dispOption() 메소드 안에는 텍스트 뷰 객체가 만들어 졌고 전달 받은 product 이름을 이 텍스트 뷰에 표시하도록 돼 있습니다.



NOTE
getActivity() 메소드가 Fragment1Activity.java 파일에서 사용이 됐는데요. 여기서는 현재 fragment와 연관이 있는 activity를 return 하게 됩니다. 이 메소드는 activity와 fragment의 상호작용을 가능하게 해 주는 메소드 입니다.



어플리케이션에 이 두개의 fragment들을 자리잡게 하려면 아래와 같은 layout xml이 있어야 합니다.


activity_foreground_fragment_app.xml



<LinearLayout 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"
    android:orientation="horizontal" >
    <fragment
        android:name="com.androidtablet.foregroundfragmentapp.Fragment1Activity"
        android:id="@+id/fragment1"
        android:layout_weight="1"
        android:layout_width="wrap_content"
        android:layout_height="match_parent" />
    <fragment
        android:name="com.androidtablet.foregroundfragmentapp.Fragment2Activity"
        android:id="@+id/fragment2"
        android:layout_weight="0"
        android:layout_width="wrap_content"
        android:layout_height="match_parent" />
</LinearLayout>



여기를 보시면 두개의 <fragment> 태그가 있는 것이 보이시죠? 각 태그에는 android:name 속성을 사용해서 Fragment1Activity와 Fragment2Activity 가 세팅돼 있습니다.



이 layout xml을 사용할 자바 파일을 만들어야 합니다.


ForegroundFragmentAppActivity.java



public class ForegroundFragmentAppActivity extends Activity implements OnOptionSelectedListener {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_foreground_fragment_app);
    }
    public void onOptionSelected(String msg) {
        Fragment2Activity frag2 = (Fragment2Activity) getFragmentManager().findFragmentById(R.id.fragment2);
        frag2.dispOption(msg);  
    } 
}



onOptionSelected() 메소드는 리스트뷰에서 아이템을 선택했을 때 실행이 될 겁니다. 이 onOptionSelected() 메소드에 리스트뷰의 선택된 아이템의 이름이 파라미터로 전달 됩니다. 이 아이템 이름은 fragment2의 dispOption() 메소드를 호출해서 스크린에 표시하게 되구요. 이렇게 FragmentManager를 사용하시면 됩니다. fragment2 에 접근해서 그 안에 있는 dispOption() 메소드를 사용하기 위해 이 FragmentManager() 가 위와 같이 사용됩니다.


태블렛에서 어플리케이션을 실행하고 나면 Fragment1과 Fragment2에 정의된 UI가 왼쪽 오른쪽으로 display 될 겁니다. fragment1의 리스트뷰는 아이템 리스트를 표시하고 Fragment2는 Please select a product라는 텍스트를 텍스트 뷰에 표시할 겁니다. 왼쪽 리스트에서 제품이 선택되면 그 이름이 오른쪽 텍스트 뷰에 표시되게 됩니다.





안드로이드 전화기에서 landscape로 이 앱이 실행이 되도 이 두 fragment들은 태블릿이랑 똑 같이 화면에 나타날 겁니다. 왜냐하면 전화기라도 landscape 모드로 놓이게 되면 좌우로 공간이 충분하니까 이렇게 표시되도 별 문제는 없습니다. 하지만 문제는 전화기를 세웠을 때도(Portrait orientation) 똑같이 나타난다는 거죠. 전화기를 세우면 좌우 공간이 충분하지 않은데 두개의 fragment들을 좌우로 나란히 배열한다는게 좀 무리입니다.



만약 전화기가 세워졌을 경우에는 이 fragment 중 하나만 화면에 나타나도록 하면 더 좋겠죠. 그러니까 처음에는 리스트 뷰만 표시되면 좋을 겁니다. 그리고 그 리스트 뷰에서 한 아이템이 선택 되면 그 아이템의 이름이 표시되는 화면 즉 두번째 fragment로 화면이 넘어가구요.



이렇게 한번 만들어 보겠습니다.

반응형

Comment

[CookBook] Fragments 이해하기 -1-

2013. 9. 25. 05:58 | Posted by 솔웅


Fragments



이번에는 Fragments에 대해서 배워보겠습니다. 안드로이드 어플리케이션은 때때로 fragments로 나눠지기도 하는데요 좀 더 manageable 하다는 얘기죠. fragment들은 각각 독립적으로 유저 인터페이스를 진행하고 또 쉽게 어플리케이션에서 추가나 삭제도 가능하고 서로 다른 화면 크기에 맞게 세팅을 할 수도 있습니다. 바로 이런 작업을 하능하게 하는 fragment를 사용하는 법과 그 life cycle에 대해 배우겠습니다. 그리고 이 fragment가 실제 안드로이드 어플리케이션에는 어떻게 적용 되는지도 알아 보구요. 또 자바 코드에서 어떻게 이 fragment들이 dynamic하게 추가 되는지도 알아 봅니다. 그리고 두개의 fragment 사이에서 어떻게 data를 넘겨주고 받는지도 보구요. ListFragment, DialogFragment, PreferenceFragment 들에 대해서도 다룹니다.



이 기능은 타블렛을 위해 나온 기능 입니다. 화면이 큰 타블렛의 공간을 나눠서 활용하기 위해 이 개념이 나왔는데요. 그러니까 7인치나 10인치 타블렛이 이 기능을 적용하기에 적합합니다.



이 글에서 소개 되는 어플리케이션은 아래 구조를 따릅니다.



1. res/values 폴더 밑에 dimens.xml 이라는 이름으로 아래 내용을 넣습니다.
  
2. res 폴더 밑에 values-sw600dp 와 values-sw720dp 라는 폴더를 생성합니다. 그리고 1번의 dimens.xml을 이 폴더 밑에 복사해 넣습니다.


3. 이 dimens.xml 파일 안의 text_size를 values-600dp 안에 있는 것은 24sp 로 values-720dp 안에 있는 것은 32sp 로 수정합니다.



Introducing Fragments



Fragments 는 activity 를 encapsulated 된 재사용 가능한 모듈로 나눕니다. 각각은 각각의 user interface 를 가지고 있습니다. 어플리케이션을 서로 다른 화면 크기에도 적당하게 표시되도록 할 수 있습니다. 그리고 activity 에서 이 fragments를 추가하거나 삭제 할 수도 있습니다.



디바이스 방향이 portrait 에서 landscape 로 바뀔 때 높이와 너비가 바뀌게 됩니다. 너비가 더 넓어 져서 오른쪽에 더 많은 공간이 생기게 되죠. 높이는 더 짧아져서 아랫쪽에 있는 부분은 짤릴겁니다. 같은 tablet이라도 이렇게 가로방향이냐 세로 방향이냐에 따라서 화면 크기가 다릅니다.



어플리케이션을 개발할 때 유저가 가로방향으로 볼 때와 세로 방향으로 볼 때 모두 어색함이 없도록 그 화면을 구성하는 view들의 구성을 미리 다 마련해 놓아야 합니다. 이럴 때 fragments를 활용할 수 있습니다.


fragments는 activity와 layout 그리고 view들의 조합이 합쳐진 겁니다. 이 세가지가 합쳐져서 user interface를 가지게 되는 거죠. 예를 들어 디바이스의 가로 세로가 바뀔 때 빈부분을 채워주고 넘치는 부분을 제대로 자리잡게 하기 위해서 이 fragments를 activity 안에 넣어서 사용하는 겁니다. 또한 필요에 따라서는 dynamic 하게 remove 될 수도 있구요.



Fragment1, Fragment2 이렇게 두개의 Fragments를 가지고 있다고 가정합시다. 이 두개의 Fragments는 각각 나름대로의 view들의 조합구성을 가지고 있습니다. 디바이스가 portrait 모드 일 때 두개의 activity를 생성하고 각각 fragment 하나씩을 배치해 놓을 수 있습니다. 그리고 한번에 한가지 activity 를 표시하도록 할 수 있죠. 그리고 디바이스에 따라서 이 두 fragments를 동시에 표시할 수 있다면 (좀 더 큰 디바이스 이거나 세로 방향일 때) 그 화면을 가득 채우기 위해 이 두개의 Fragment들을 한개의 activity에 넣어서 화면을 꽉 채울 수 있죠.

각각의 fragment들은 또한 나름대로의 life cycle과 view hierarchy 를 가지고 있습니다.activity 가 실행되는 동안 이 fragment들을 추가하거나 삭제 할 수 있습니다. 이 fragment들은 activity 의 context 안에 존재합니다.



Understanding the Life Cycle of a Fragment



Fragment를 생성하려면 Fragment 클래스를 extend하고 몇개의 life cycle 콜백 메소드를 implement 해야 합니다. fragment의 라이프 사이클은 해당 activity의 라이프 사이클에 종속 됩니다. 그러니까 activity 가 paused 된 상태이면 그 안의 모든 fragment들도 paused 됩니다. 마찬가지로 activity 가 destroy 되면 그 안의 fragment들도 같이 destroy 됩니다. 이 fragment들의 라이프 사이클에는 몇가지 콜백 메소드들이 포함돼 있습니다.


- onAttach() - activity 에 fragment가 attach 될 때 호출 됨


- onCreate() - fragment를 생성할 때 호출 됨. 이 메소드는 fragment 가 paused 됐거나 stopped 된 후에 다시 resumed 될 때 계속 남아있기를 바라는 fragment의 item들을 초기화 할 때 사용합니다.


- onCreateView() - fragment의 view를 생성하기 위해 호출 합니다.


- onActivityCreated() - 액티비티의 onCreate() 메소드가 return 됐을 때 호출 됩니다.


- onStart() - fragment가 visible하게 됐을 때 호출 됩니다. 이 메소드는 액티비티의 onStart() 메소드와 연관이 있습니다.


- onResume() - fragment가 visible되고 running 할 때 호출 됩니다. 이 메소드는 activity의 onResume() 메소드와 관련이 있습니다.


- onPause() - fragment가 visible 이지만 focus를 받고 있지 않을 때 호출 됩니다. 이 메소드는 activity의 onPause() 메소드와 연관이 있습니다.


- onStop() - fragment가 visible이지 않을 때 호출 됩니다. 이 메소드는 액티비티의 onStop()메소드와 연관이 있습니다.


- onDestroyView() - fragment가 save 되거나 destroy 되어야 할 때 호출 됩니다. view hierarchy가 fragment로부터 remove 됩니다.


- onDestroy() - fragment가 더이상 사용되지 않을 때 호출 됩니다. fragment와 관련된 view hierarchy는 없지만 fragment는 계속 activity에 attach 돼 있습니다.


- onDetach() - fragment가 activity에서 detach될 때 호출 됩니다.  fragment에 할당된 리소스가 release 됩니다.

반응형

Comment

이전 1 2 3 4 5 다음