기록장
[안드로이드] Fragment 이해하기(1) 본문
Fragment는 효율성과 재사용에 용의하다.
안드로이드에서 화면을 변경할 경우 Intent를 사용할 수 있다. 그러나 이 방법은 액티비티 갯수가 많으면 많아질수록 코드도 많아지고 켜지는 화면도 많고 관리가 어려워서 효율적이지 못하다. 그래서 틀을 만들고 필요한 화면을 가져와서 틀에 넣고 교체해주면서 사용하는, 재사용 개념을 가지고 있는 Fragment를 사용할 수 있다.(전환 효과) 또한, 한 화면에 여러 부분으로 나눠서 보여주거나 각각의 부분 화면 단위로 바꿔서 보여주고 싶을 때 사용할 수 있다.
위에 설명을 그림으로 나타낸 것이다.
오른쪽 그림은 한 화면에 하나의 액티비티로 2번째 액티비티를 보여주고 싶다면 Intent로 액티비티를 호출하는 것을 볼 수 있다.
왼쪽 그림은 하나의 화면에 여러 Fragment를 넣어 한 화면에 두 액티비티에 대한 정보를 보여줄 수 있고 FragementManager를 통해 서로 메소드 호출이 가능하다.
이 또한 위에 설명을 그림으로 나타낸 것이다.
꼭 여러 화면을 사용하는 것이 아니라 한 화면으로 추가, 제거 형식으로 액티비티를 전환하지 않고 전환 효과를 낼 수 있다.
Fragment 사용 목적은 부분 화면을 독립적으로 사용하기 위한 것이고 액티비티를 본떠 만든 것이다. 그래서 Fragment도 일반 액티비티와 같이 xml, java 파일로 구성된다.
지금까지 일반 액티비티를 생성하면 setContentView() 메소드로 xml 파일을 불러왔을 것이다. 그러나 Fragment에는 이 메소드가 없다. 그래서 LayoutInflater를 사용해 xml 파일을 인플레이션 후 클래스에서 사용할 수 있도록 onCreateView() 메소드에 들어간다. 이 메소드는 콜백 메소드로 인플레이션이 필요한 시점에 자동으로 호출된다.
이렇게 만든 Fragment 파일은 사용하기 위해 Fragment를 관리하는 메인 액티비티에 추가하여야 하는데 xml 레이아웃에 추가하거나 소스 코드에 new 연산자로 객체를 만든 후 FragmentManager로 추가할 수 있다.
Fragment를 상속하게 되면 사용할 수 있는 주요 메소드
- public final Activity getActivity(): 이 Fragment를 포함하는 액티비티를 반환
- public final FragmentManager getFragmentManager(): 이 Fragment를 포함하는 액티비티에서 Fragment객체들과 의사소통하는 FragmentManager를 반환
- public final Fragment getParentFragment(): 이 Fragment를 포함하는 부모가 Fragment일 경우 리턴 / 액티비티면 null 반환
- public final int getId(): 이 Fragment의 ID를 반환
FragmentManager 클래스의 주요 메소드
- public abstract FragmentTransaction beginTransaction(): Fragment를 변경하기 위한 트랜잭션을 시작
- public abstract Fragment findFragmentById(int id): ID를 이용해 Fragment 객체를 찾음
- public abstract Fragment findFragmentByTag(String tag): 태그 정보를 사용하여 Fragment 객체를 찾음
- public abstract boolean executePendingTransactions(): 트랜잭션은 commit() 메서드를 호출하면 실행되지만 비동기 방식으로 실행되므로 즉시 실행하고 싶다면 이 메서드를 추가로 호출해야 함
코드
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/relativelayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<fragment
android:id="@+id/main_fragment"
android:name="com.android.fragment_project.fragment1"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
name에는 Fragment를 상속하는 파일들 중 맨 앞에 보여주고 싶은 것으로 지정한다.
MainActivity.java
public class MainActivity extends AppCompatActivity {
fragment1 f1;
fragment2 f2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//activity_main.xml에서 정해줬던 맨 앞의 Fragment를
//FragmentManager의 ID를 통해 찾아 할당해준다.(f1에 할당)
//나머지 fragment2는 new 연산자를 사용하여 객체로 만들어 변수에 할당
FragmentManager fm = getSupportFragmentManager();
f1 = (fragment1) fm.findFragmentById(R.id.main_fragment);
f2 = new fragment2();
}
public void onFragmentChanged(int index) {
if (index == 0) {
//Fragment 변경을 위해 beginTransaction() 메소드와 replace() 메소드를 사용
getSupportFragmentManager().beginTransaction().replace(R.id.relativelayout1, f2).commit();
}
else if (index == 1) {
getSupportFragmentManager().beginTransaction().replace(R.id.relativelayout1, f1).commit();
}
}
}
activity_fragment1.xml
<?xml version="1.0" encoding="utf-8"?>
<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="vertical"
tools:context=".fragment1"
android:gravity="center"
android:background="#FFEB3B">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="프래그먼트1"
android:textSize="30dp"
android:id="@+id/textview1"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="프래그먼트2로"
android:id="@+id/button1"/>
</LinearLayout>
fragment1.java
public class fragment1 extends Fragment {
ViewGroup rootView;
Button b1;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = (ViewGroup) inflater.inflate(R.layout.activity_fragment1, container, false);
b1 = rootView.findViewById(R.id.button1);
b1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//화면을 띄우기 위해 메인 액티비티를 통해 호출한다.
MainActivity activity = (MainActivity) getActivity();
activity.onFragmentChanged(0);
}
});
return rootView;
}
}
최상위 레이아웃은 인플레이션을 통해 참조한 rootView 객체이다. 최상위 레이아웃(rootView)은 fragment1 안에 들어있는 것이고 fragment1은 이 레이아웃을 화면에 보여주기 위한 틀이다. rootView를 이용하여 다른 위젯들을 등록하고 사용할 수 있다.
activity_fragment2.xml
<?xml version="1.0" encoding="utf-8"?>
<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="vertical"
tools:context=".fragment2"
android:background="#03A9F4"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="프래그먼트2"
android:textSize="30dp"
android:id="@+id/textview1"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="프래그먼트1로"
android:id="@+id/button1"/>
</LinearLayout>
fragment2.java
public class fragment2 extends Fragment {
ViewGroup rootView;
Button b1;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = (ViewGroup) inflater.inflate(R.layout.activity_fragment2, container, false);
b1 = rootView.findViewById(R.id.button1);
b1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MainActivity activity = (MainActivity) getActivity();
activity.onFragmentChanged(1);
}
});
return rootView;
}
}
결과
+ 추가
Fragment에 대해 찾아보다 xml 코드에서 fragment를 사용하신 분들과 FrameLayout을 사용하신 분들이 계셨다. 인터페이스와 레이아웃인 차이인거 같은데 둘 다 되는거 같아서 계속 찾아보다가 발견한 글이다. 혹시 나처럼 몰랐던 분들이 계셨다면 보면 좋을 것 같아서 넣었다.
그리고 FrameLayout을 이용하여 구현한 버전도 아래에 추가했다. MainActivity와 activity_main 조금만 고쳐주면 된다.
1. 정적인 방법
Activity XML 레이아웃 파일에 <fragment>태그 추가하여 Fragment를 정의합니다. Fragment의 레이아웃은 Activity에서 inflate되어 ViewGroup이 됩니다.
--> onInflate 메소드 호출시 Fragment 라이프 사이클이 시작됩니다.
-->처음 화면에 출력될 프래그먼트 지정이나 화면에 동적변화 없을경우 사용됩니다.
-->android:class 속성에 프래그먼트 클래스를 적어줘야합니다.
-->실행중에 fragment를 추가/삭제/변경할 수 없습니다.
2. 동적인 방법 - Activity 실행중에 Fragment를 추가/삭제/변경
Activity XML 레이아웃 파일에 <FrameLayout>태그 추가 후, 프로그래밍적으로 FragmentManager를 이용하여 ViewGroup에 fragment를 동적으로 추가합니다.
-->onAttach 콜백 메소드에서 Fragment 라이프 사이클이 시작됩니다.
참고: https://webnautes.tistory.com/1089
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/relativelayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="@+id/main_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
fragment1 f1;
fragment2 f2;
FragmentManager fm;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fm = getSupportFragmentManager();
f1 = new fragment1();
f2 = new fragment2();
fm.beginTransaction().replace(R.id.main_fragment, f1).commit();
}
public void onFragmentChanged(int index) {
if (index == 0) {
fm.beginTransaction().replace(R.id.main_fragment, f2).commit();
}
else if (index == 1) {
fm.beginTransaction().replace(R.id.main_fragment, f1).commit();
}
}
}
부족한 점, 피드백 환영합니다.
감사합니다.
참고
정재곤, 『Do it! 안드로이드 앱 프로그래밍 - 개정 6판』, 이지스퍼블리싱(주)
'안드로이드' 카테고리의 다른 글
[안드로이드] Fragment 이해하기(3) / ViewPager 뷰페이저 (0) | 2021.01.04 |
---|---|
[안드로이드] Fragment 이해하기(2) / 상단 탭, 하단 탭 (0) | 2021.01.03 |
[안드로이드] SharedPreferences / 데이터 저장, 복원 (0) | 2021.01.01 |
[안드로이드] 액티비티의 생명주기 (0) | 2021.01.01 |
[안드로이드] Flag 플래그 이해하기 (0) | 2020.12.31 |