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

최근에 받은 트랙백

글 보관함

[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