관리 메뉴

막내의 막무가내 프로그래밍 & 일상

[안드로이드] 프래그먼트(fragment) 중요 및 주의할 점 본문

안드로이드/자바 & Previous

[안드로이드] 프래그먼트(fragment) 중요 및 주의할 점

막무가내막내 2019. 2. 12. 23:44
728x90



P.S) 2019.05.06

처음공부할 때 작성한 글인데 잘 못 작성한 부분들이 많습니다. 다른글을 읽어보시거나 만약 보신다면 뭐하는코드인지 이부분만 읽어주시면 될 것 같습니다. 지금 다시보니깐 프래그먼트 전환하는 소스코드인데 프래그먼트에서 onAttach할때 activity라는 액티비티객체에 getActivity()를 사용해서 해당 프래그먼트를 참조하고있는 액티비티를 담습니다. (인터페이스를 사용해서 할수도 있는데 객체명이 activity인것을 보니 인터페이스를 사용한것 같지는 않습니다.) 프래그먼트에서 이 객체를 사용해서 activity.onImageChange()라는 메소드를 호출하고 이 메소드는 다른 프래그먼트에서 정의하고 있는 메소드인 setImage()를 불러주는 예제 입니다.


정리하자면, 한 프래그먼트에서 다른프래그먼트와 소통 및 화면전환을 하는 예제입니다.  액티비티와 액티비티 사이에서 인텐트를 사용해서 데이터를 전달받고 전환 및 교류하는 것처럼 프래그먼트와 프래그먼트는 액티비티, 프로그래먼트 매니저를 사용해야합니다.


예제실행순서

1. 프래그먼트1 에서 getActivity()를 사용해 activity에 이 프래그먼트를 참조하고있는 액티비티를 담는다.


2. 프래그먼트1에서 activity는 액티비티를 참조하고있으므로 해당 액티비티에서 정의해논 메소드를 사용할 수 있다. activity.onImageChange() 사용


3. 액티비티의 onImageChange()는 프래그먼트2의 setImage() 메소드를 호출하게 구현되어있다. 

fragment2.setImage() 사용


4. 프래그먼트2에는 setImage()메소드가 정의되어있다. (자신의 뷰 배경을 바꾸는)




https://developer.android.com/guide/components/fragments?hl=ko


프래그먼트는 하나의 프래그먼트에서 다른 프래그먼트를 직접 띄울 수 없습니다.

그래서 프래그먼트 매니저가 이러한 사항을 담당합니다.


프래그먼트 매니저는 엑티비티 쪽에 있으므로 다른 프래그먼트를 띄우거나 다른 프래그먼트에 데이터를 전달하고 싶으면 일단 해당 프래그먼트에서 액티비티 쪽으로 데이터를 전달하거나 명령을 전달해야합니다. 즉 사실상 액티비티가 프래그먼트매니저 역할을 한다고도 볼 수 있습니다.


그러면 액티비티 쪽에 메서드를 만들고 그 메서드를 호출하는데 코드의 동일성 및 일관성을 위해 인터페이스를 하나 정의하고 액티비티에서 그 인터페이스를 구현하는 방식으로 하는게 좋습니다. 

=>예시(안드로이드개발자 문서)

액티비티로의 이벤트 콜백 생성

어떤 경우에는 프래그먼트로 하여금 액티비티와 이벤트를 공유하게 해야 할 수 있습니다. 이렇게 하기 위한 한 가지 좋은 방법은 프래그먼트 내부의 콜백 인터페이스를 정의한 다음 해당 호스트 액티비티가 이를 구현하도록 하는 것입니다. 액티비티가 인터페이스를 통해 콜백을 수신하면, 필요에 따라 그 정보를 레이아웃 내의 다른 프래그먼트와 공유할 수 있습니다.

예를 들어 어떤 뉴스 애플리케이션에서 액티비티 하나에 프래그먼트가 두 개 있습니다. 하나는 기사 목록을 표시(프래그먼트 A)하고 다른 하나는 기사 하나를 표시(프래그먼트 B)하는 경우 목록 항목이 선택되면 프래그먼트 A가 액티비티에 알려야 프래그먼트 B에 해당 기사를 표시하라고 알릴 수 있습니다. 이 경우, OnArticleSelectedListener 인터페이스는 프래그먼트 A 내부에 선언됩니다.

public static class FragmentA extends ListFragment {
   
...
   
// Container Activity must implement this interface
   
public interface OnArticleSelectedListener {
       
public void onArticleSelected(Uri articleUri);
   
}
   
...
}

그러면 프래그먼트를 호스팅하는 액티비티가 OnArticleSelectedListener 인터페이스를 구현하고 onArticleSelected()를 재정의하여 프래그먼트 A로부터 발생한 이벤트를 프래그먼트 B에 알립니다. 호스트 액티비티가 이 인터페이스를 구현하도록 하려면 프래그먼트 A의 onAttach() 콜백 메서드(프래그먼트를 액티비티에 추가할 때 시스템이 호출하는 메서드)가 OnArticleSelectedListener의 인스턴스를 생성해야 합니다. 이때 onAttach()로 전달되는 Activity를 형변환하는 방법을 씁니다.

public static class FragmentA extends ListFragment {
   
OnArticleSelectedListener mListener;
   
...
   
@Override
   
public void onAttach(Activity activity) {
       
super.onAttach(activity);
       
try {
            mListener
= (OnArticleSelectedListener) activity;
       
} catch (ClassCastException e) {
           
throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
       
}
   
}
   
...
}

액티비티가 인터페이스를 구현하지 않은 경우, 프래그먼트가 ClassCastException을 발생시킵니다. 성공 시, mListener 멤버가 액티비티의 OnArticleSelectedListener 구현에 대한 참조를 보유하므로, 프래그먼트 A가 액티비티와 이벤트를 공유할 수 있습니다. 이때 OnArticleSelectedListener 인터페이스가 정의한 메서드를 호출하는 방법을 사용합니다. 예를 들어 프래그먼트 A가 ListFragment의 확장인 경우, 사용자가 목록 항목을 클릭할 때마다 시스템이 프래그먼트 안의 onListItemClick()을 호출하고, 그러면 이것이 onArticleSelected()를 호출하여 해당 이벤트를 액티비티와 공유하는 것입니다.

public static class FragmentA extends ListFragment {
   
OnArticleSelectedListener mListener;
   
...
   
@Override
   
public void onListItemClick(ListView l, View v, int position, long id) {
       
// Append the clicked item's row ID with the content provider Uri
       
Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);
       
// Send the event and Uri to the host activity
        mListener
.onArticleSelected(noteUri);
   
}
   
...
}

onListItemClick()에 전달된 id 매개변수는 클릭한 항목의 행 ID이며, 액티비티(또는 다른 프래그먼트)가 이것을 사용해 애플리케이션의 ContentProvider에서 기사를 가져옵니다.

콘텐츠 제공자 사용법에 대한 자세한 정보는 콘텐츠 제공자 문서에서 이용하실 수 있습니다.




액티비티에서 프래그먼트 쪽으로 뭔가 데이터를 주거나 프래그먼트 쪽에 조작을 할 때는 프래그먼트에 메서드를 만들고 액티비티에서 그 메서드를 호출하도록 합니다.


=>ex프래그먼트에서 액티비티에있는 매소드 호출-> 해당 액티비티메소드에서 다른프래그먼트 메소드 호출


프래그먼트1 부분코드

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_list,container,false);
Button button = rootView.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//메인엑티비티 메소드 사용가능
activity.onImageChange(0);
}
});

Button button2 = rootView.findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
activity.onImageChange(1);
}
});

Button button3 = rootView.findViewById(R.id.button3);
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
activity.onImageChange(2);
}
});

return rootView;
}


메인액티비티 부분코드

public  void onImageChange(int index){
fragment2.setImage(index);
}


프래그먼트2 부분코드

public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_viewer, container, false);
imageView = rootView.findViewById(R.id.imageView);
return rootView;
}

public void setImage(int index) {
if (index == 0) {
imageView.setImageResource(R.drawable.a);
} else if (index == 1) {
imageView.setImageResource(R.drawable.b);
} else if (index == 2) {
imageView.setImageResource(R.drawable.c);
}
}




그리고 프래그먼트를 이 프래그먼트에 띄울건지 다른 프래그먼트에 띄울건지는 액티비티에서 결정해주면 됩니다. 뷰페이저(ViewPager)는 그중에 어떤 프래그먼트를 화면에 보여줄 것인지 코드에서 지정할 수도 있다.(메서드 호출을 통해서 몇번째 프래그먼트를 보여줄건지 결정 가능)

뷰페이저가 아니라면 액티비티를 매개로해서 프래그먼트 간의 메서드호출을 통해 구현해야합니다.

=> ex메인액티비티에서

ViewPager pager;
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//1=> 2번쨰 프래그먼트를 보여주라
pager.setCurrentItem(1);
}
});


728x90
Comments