[Android] 안드로이드 프래그먼트 (Fragment) (3)

Android/Android · 2020. 6. 15. 15:27
반응형

https://survivalcoding.com/p/android_basic

 

될 때까지 안드로이드

될 때까지 안드로이드에 수록된 예제의 라이브 코딩 해설

survivalcoding.com

위 서적을 참고하였습니다.

 

 

 

 이번 포스트에선 프래그먼트로 리스트를 추가하여 색깔을 바꾸는 앱을 만들어 보겠습니다. 바로 가겠습니다.

 

 

 activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="hello.world.study.MainActivity">

    <fragment
        android:id="@+id/fragment_list_color"
        android:name="hello.ColorListFragment"
        android:layout_weight="1"
        android:layout_width="match_parent"
        android:layout_height="0dp"/>

    <fragment
        android:id="@+id/fragment_color"
        android:name="hello.ColorFragment"
        android:layout_weight="1"
        android:layout_width="match_parent"
        android:layout_height="0dp"/>

</LinearLayout>

 

 2개의 프래그먼트를 지정해 줬습니다. 하나는 리스트, 하나는 색깔을 나타내는 프래그먼트입니다. android:name에 빨간 줄이 그일 텐데, 이는 아직 클래스를 만들지 않아서입니다. 이제 클래스를 만들러 가보겠습니다.

 

 자신의 패키지 이름에 마우스 우클릭을 누른 후 [ New -> Java Class ] 를 눌러주시고, 다음과 같이 작성해주세요.

 

 

 총 2개의 클래스를 생성하겠습니다. Superclass에 Fragment, ListFragment만 쳐도 자동완성이 됩니다. 이제 코드를 작성해보겠습니다.

 

 

 ColorFragment.java

public class ColorFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        //context를 얻기 위해 호스트 액티비티 전달
        return new View(getActivity());
    }

    public void setColor(int color) {
        getView().setBackgroundColor(color);
    }
}

 

 ColorFragment 클래스입니다. 여기서는 onCreateView를 오버라이딩하여 context를 얻기 위해 View(GetActivity());를 return 한 다음, 백그라운드 색을 바꾸기 위한 setColor() 메소드를 작성했습니다.

 

 

 ColorFragmentList.java

public class ColorListFragment extends ListFragment {

    private OnColorSelectedListener mListener;

    /*일반적인 뷰와 같은 이벤트 리스너 연결 방법
    public void setOnColorSelectedListener(OnColorSelectedListener listener) {
        mListener = listener;
    }
    */

/////////////2번/////////////
    //Button의 OnClickListener과 비슷
    public interface OnColorSelectedListener {
        void onColorSelected(int color);
    }
/////////////2번/////////////

/////////////3번/////////////
    //리스너 구현을 강제함(필수)
    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        //context가 OnColorSelectedListener를 구현하고 있다는 가정 하에 강제 캐스팅
        try {
            mListener = (OnColorSelectedListener) context;
        }catch(ClassCastException e) {
            throw new ClassCastException(((Activity)context).getLocalClassName()+ " 는 onColorSelectedListener를 구현해야 합니다.");
        }
    }
/////////////3번/////////////    
    
/////////////1번/////////////
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        List<String> colorList = Arrays.asList("Red", "Green", "Blue");
        ArrayAdapter<String> adapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_1, colorList);
        setListAdapter(adapter);
    }
/////////////1번/////////////

/////////////4번/////////////
    @Override
    public void onListItemClick(@NonNull ListView l, @NonNull View v, int position, long id) {
        ArrayAdapter<String> adapter = (ArrayAdapter<String>) l.getAdapter();
        String colorString = adapter.getItem(position);
        int color = Color.RED;
        switch (colorString) {
            case "RED" :
                color = Color.RED;
                break;
            case "Green" :
                color = Color.GREEN;
                break;
            case "Blue" :
                color = Color.BLUE;
                break;
        }
        //mListener이 null 체크
        if(mListener != null) {
            mListener.onColorSelected(color);
        }
    }
/////////////4번/////////////    
}

 

 여긴 살짝 좀 복잡합니다. 먼저 하셔야할 것이 1번입니다. 리스트를 하나 작성하여 색깔들을 추가하고, adapter에 등록합니다. 그다음 2번, Interface를 작성해 줍니다. 인터페이스를 작성하는 이유는 프래그먼트에서 액티비티 간에 통신을 하기 위한 것입니다. 

 

 일반적으로 액티비티가 프래그먼트를 포함하고 있으면 액티비티에서 프래그먼트를 사용하지만, 프래그먼트가 액티비티의 참조를 가지고 있으면 메모리 누수(leak)가 발생할 수 있습니다. 그러나 인터페이스를 작성하면 메모리 누수도 방지하고, 객체의 상태를 확인하기가 훨씬 쉬워집니다.

 인터페이스를 작성했으면 MainActivity에서 오류가 날 것입니다. 이는 MainActivity 작성할 때 처리하겠습니다.

 

 그 다음 작성해야 할 것이 3번입니다. onAttatch()는 프래그먼트 생성 시 제일 먼저 호출되는 콜백 메소드입니다. 오버라이딩한 onAttatch()의 Context는 이 프래그먼트를 포함하고 있는 액티비티(호스트 액티비티)의 인스턴스입니다. onAttatch() 메소드에 OnColorSelectedListener 인터페이스 구현을 강제하기 위해 3번의 코드를 작성하고, 예외가 발생하면 리스너를 구현해야 한다는 메시지를 표시하도록 합니다. 이는 오류 메시지 발생 시 Logcat에서 확인 가능합니다.

 OnAttatch() 메소드의 파라미터인 context는 인터페이스를 구현하고 있다는 가정 하에 강제 캐스팅을 하고 있습니다. 캐스팅 실패 시 예외를 발생시킵니다. 

 만약, 일반적인 뷰와 같이 public 메소드를 통해 리스너를 연결하고 싶으면 맨 위에 주석 친 곳처럼 구현하면 됩니다. 이것을 예로 들자면, Button 클래스의 SetOnClickListener()에 해당됩니다.

 

 마지막으로 4번만 구현해주면 끝납니다. List를 터치하면 그 항목 아이템의 position을 받고, 터치한 항목에 따라 색깔을 바꿔주는 메소드입니다.

 

 마지막 MainAcitivy입니다.

 

 MainActivity.java

public class MainActivity extends AppCompatActivity implements ColorListFragment.OnColorSelectedListener {

    private ColorFragment colorFragment;

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

        colorFragment = (ColorFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_color);
    }

    //fragment에서 activity로 콜백 메소드 구현
    @Override
    public void onColorSelected(int color) {
        colorFragment.setColor(color);
    }
}

 

 여기서 implements ColorListFragment.OnColorSelectedListener를 추가해주시면 그래도 빨간 불이 뜰 겁니다. 여기서 import 하듯이 [ Alt + Enter ]를 눌러주시면, implements method를 클릭해주세요. 그럼 onColorSelected() 메소드가 오버라이드 돼서 등장하게 됩니다. 이 부분은 별로 어려운 것은 없습니다. ColorFragment 객체를 만든 다음, getSupportFragmentManager().findFragmentById()를 해서 fragment객체를 생성해 주시고, onColorSelected() 메소드를 통해 ColorFragment 메소드로 전달하는 간단한 앱입니다.

 

 다음은 결과물입니다.

 

초기화면

Red 터치

Green 터치

 

 확실히 BaseAdapter를 사용했을 때보다 리스트 터치 이벤트라든지 프래그먼트의 생명주기 덕분에 좀 더 간단하게 구현할 수 있고, 프래그먼트에 익숙해지면 더욱 다양한 기능, 안전성 있는 앱을 개발할 수 있을 것 같습니다. 감사합니다.

반응형