71일차(2)/Android App(16) : Fragment
- 새 모듈 생성- step07fragment
MainActivity
package com.example.step07fragment;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentManager;
public class MainActivity extends AppCompatActivity implements MyFragment.MyFragmentListener {
MyFragment mf1, mf2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//전개된 view에는 MyFragment 객체가 2개 있다. 만일 해당 객체의 참조값이 액티비티에서 필요하다면?
//FragmentManager 객체의 참조값을 얻어내서
FragmentManager fm=getSupportFragmentManager();
//해당 객체의 메소드를 활용해서 프래그먼트의 참조값을 얻어낸다.
mf1=(MyFragment) fm.findFragmentById(R.id.fragment1);
mf2=(MyFragment) fm.findFragmentById(R.id.fragment2);
}
@Override
public void sendMsg(String msg) {
Toast.makeText(this,msg,Toast.LENGTH_SHORT).show();
}
public void resetFragment(View v){
mf1.reset();
mf2.reset();
}
}
- 나중에는 여기서도 fragment 를 만들 수 있다.
- 일단 지금은 커스텀으로 만들어보기!
- new java class생성
MyFragment
package com.example.step07fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
/*
[Fragment]
- 액티비티의 제어 하에 존재하는 미니 Controller
- 재활용을 염두에 두고 만드는 경우가 많다
- 재활용이라는 것은 여러개의 액티비티에서 활용된다는 의미
[ Fragment 만드는 방법 ]
1. Fragment 클래스를 상속받는다.
2. Fragment 의 layout xml 문서를 만든다.
3. onCreateView() 메소드를 오버라이딩 한다.
*/
public class MyFragment extends Fragment implements View.OnClickListener {
//필드
TextView textView;
int count=0;
// layout 으로 사용할 View를 만들어서 리턴해주어야 한다.
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//1. fragment_my.xml 문서를 전개해서 View를 만든 다음
View view=inflater.inflate(R.layout.fragment_my, container);
//만든 View에서 TextView의 참조값을 알아낸다.
textView=view.findViewById(R.id.textView);
textView.setOnClickListener(this);
//2. 해당 View 를 리턴해 준다.
return view;
}
@Override
public void onClick(View v) {
//카운트 값을 1 증가시키고
count++;
//정수를 문자열로 만들어서 TextView에 출력하기
textView.setText(Integer.toString(count));
/*
만일 count 값이 10의 배수이면 이 fragment를 제어하고 있는 액티비티에 해당 정보를 알려주기!
- 액티비티의 특정 메소드를 호출하면서 문자열 전달하기
- 단 특정 액티비티의 의존성은 없어야 된다.
*/
//이 fragment 를 제어하고 있는 액티비티의 참조값 알아내기.
FragmentActivity fa = getActivity();
//혹시 액티비티가 MyFragmentListener 인터페이스를 구현하지않았을 수도 있기 때문에 type을 확인해서 캐스팅한다.
if(count%10==0 && fa instanceof MyFragmentListener){
MyFragmentListener ma=(MyFragmentListener)fa;
ma.sendMsg(count+" 입니다!");
}
}
//액티비티에서 특정시점에 호출할 예정인 메소드
public void reset(){
count=0;
textView.setText("0");
}
//이 fragment 에서 전달하는 메세지를 받을 액티비티에서 구현할 인터페이스를 클래스 안에 정의하기
public interface MyFragmentListener{
public void sendMsg(String msg);
}
}
- import 할 때 이렇게 두가지라면 'x' 가 붙은 것으로 import 하는 게 좋다.
- 이쪽이 더 최신 버전이다.
[ Fragment ]
- 액티비티의 제어 하에 존재하는 미니 Controller
- 재활용을 염두에 두고 만드는 경우가 많다
- 재활용이라는 것은 여러개의 액티비티에서 활용된다는 의미
[ Fragment 만드는 방법 ]
1. Fragment 클래스를 상속받는다.
2. Fragment 의 layout xml 문서를 만든다.
3. onCreateView() 메소드를 오버라이딩 한다.
- 한 화면이 액티비티로 구성된다면, 화면의 일부를 프래그먼트로 구성할 수 있다.
- 각각의 프래그먼트는 자기 자신만의 고유한 UI를 가진다. 분업화된 것이라고 볼 수 있다.
- ★재활용★한다는 개념이 중요하다! fragment를 만드는 이유.
- 한 액티비티의 프래그먼트가 다른 액티비티에서도 활용될 수 있다.
- 보통은 코드를 복사해서 똑같이 동작하도록 만들어야 하는데,
fragment가 있으면 그 fragment 를 import 해서 사용하면 된다.
- 액티비티를 조각내서 각각의 fragment가 제어하게 한다는 개념!
- res 폴더에 새 레이아웃 리소스 파일 / LinearLayout 으로 생성
fragment_my
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="0"
android:textSize="50sp"
android:gravity="center"
android:id="@+id/textView"
android:background="#11FF00"/>
</LinearLayout>
- 0 이라는 텍스트가 들어가 있는 textView를 하나 만들고 id 부여하기
- MyFragment 클래스에서 onCreateView를 override 해준다.
- 이 메소드에서는 View를 리턴한다.
- 이 프래그먼트가 Activity의 일부 공간을 차지할 것이다.
- 그 공간에서 사용할 view를 리턴해주면 된다.
- onCreateView 메소드에는 레이아웃 전개자(LayoutInflater) 객체가 전달된다.
@NonNull / @Nullable
- 이 annotation은 null이 들어갈 수 있음, 없음에 대해서 설명해주는 역할을 한다.
- java에서 좀 더 엄격한 문법으로 사용하는 것
- 두번째 인자로 View Group을 넣어준다.
- 이 view를 가져와주는 것이 중요한 작업!
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"
tools:context=".MainActivity"
android:orientation="vertical">
<fragment
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_margin="10dp"
android:name="com.example.step07fragment.MyFragment"
android:id="@+id/fragment1"/>
<fragment
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_margin="10dp"
android:name="com.example.step07fragment.MyFragment"
android:id="@+id/fragment2"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="resetFragment"
android:text="리셋"
android:id="@+id/Btn"/>
</LinearLayout>
- fragment 의 name 속성에 myFragment 넣어주기!!
- Gradle Script 폴더 안에서 targerSdk를 32->33 으로 수정
- run 했을때 나오는 오류메세지에서 수정하라고해서 33으로 바꾸어 봤더니 잘 실행됨!
- 그래들 설정을 변경하고 나면 Sync 한번 맞춰주기!
- 위와 같이 프래그먼트 2개로 화면을 구성했다.
- 박스를 클릭할 때마다 숫자를 증가시킬 수 있도록 할 것!
- this 는 MyFragment를 가리킨다. 여기에 구현한다! 프래그먼트가 리스너역할을 할 수 있도록.
- override한 OnClick 메소드 안에서 화면의 숫자가 올라가도록 하는 코드를 작성
- 그럼 textView안의 텍스트를 onClick() 메소드 안에서 바꿔주어 한다.
→ textView의 참조값이 필요하므로 textView를 필드로 만들어야 한다.
- 필드로 만들어서 어디에서나 참조할 수 있도록 수정
- 이제 클릭하면 숫자가 하나씩 늘어난다.
- 이런 프래그먼트를 만들어서 여기저기에서 재활용하기 위해서는
특정 액티비티에 의존성이 없어야 한다!
- 액티비티에 의존성이 없다는 것은,
여기에 특정 activity에서 가져온 import 값이 없어야 한다는 것을 의미한다.
- 만약 숫자가 10의 배수가 될 때마다 알림을 띄우고자 한다면?
- 이렇게 메시지를 전달하는 메소드를 작성한다.
- 이 메소드에 프래그먼트로부터 정보가 들어가야한다.
- myfragment에서 보면 .getActivity() 라는 메소드가 있다. fragment를 리턴한다.
- 프래그먼트 파일에서 getActivity 하면
제어하고 있는 액티비티의 참조값이 부모타입으로 리턴된다.
- 이것을 MainActivity 타입으로 받으려면 캐스팅하면 된다!
- 액티비티의 참조값을 알아내서 특정 조건일 때 액티비티에 어떤 정보를 전달하도록 했다.
- 10이 되면 이렇게 토스트 메시지가 뜨도록 했다.
MainActivity ma=(MainActivity) getActivity();
if(count%10==0){
ma.sendMsg(count+" 입니다!");
}
}
- 현재 myFragment는 이 부분 때문에 오직 MainActivity 에서만 동작할 수 있다.
- 이렇게 작성한 프래그먼트는 재활용이 안된다!
- 여기서 의존성을 없애는 방법은?
- 프래그먼트에 MyFragmentListener 인터페이스를 구현하고,
액티비티가 이것을 구현하도록 한다.
MyFragmentListener ma=(MyFragmentListener) getActivity();
if(count%10==0){
ma.sendMsg(count+" 입니다!");
}
- 변수를 인터페이스 타입으로 선언하고, getActivity()를 인터페이스 타입으로 캐스팅한다
- 구현하고, sendMsg 메소드 오버라이드하기.
- 이렇게 인터페이스로 구현하면 어떤 액티비티에서든 가져다가 사용할 수 있다.
instanceof 타입명
- 객체의 타입을 확인해서 해당 타입으로 형변환이 가능하면 T, 불가능하면 F를 반환한다.
- 항상 캐스팅하는것이 아니라 if로 한번 확인해서 캐스팅할 수 있으면 더 좋다.
- 의존성을 삭제하고도 똑같이 잘 작동한다.
- fragment는 fragment일 뿐이다. view가 아니다!
- main에서 생성된 fragment는 이렇게 2개이다.
- 하지만 간접적으로 전개된 것이다.우리가 직접 전개해준 것은 아니다.
- 직접 전개해서 사용하려면 참조값을 얻어와서 사용해야 한다.
- view가 아니므로 findViewById() 로 참조값을 알아낼 수 없다.
getSupportFragmentManager()
- fragment의 참조값을 얻어오는 데 사용한다.
- 이 안에 findFragmentById() 메소드가 있다.
- 부모타입으로 리턴된다. 원래 타입으로 캐스팅해서 받으면 된다!
- MyFragment에 메소드 추가
- 이 메소드가 호출되면 count가 0으로 바뀌도록 할 것
- 버튼 요소를 하나 추가하고 onclick 속성 하나 추가!
- fragment의 참조값이 필요하다. fragment의 참조값을 담을 곳을 지역변수가 아닌 필드로 만들어주기!
- 액티비티/프래그먼트가 어떻게 소통하는가를 익히면 된다.
- fragment는 미니 컨트롤러의 연할을 한다.
- 원래 액티비티가 하던 일을 fargment가 일부 분담한다.
- MainActivity 에서는 getSupportFragmentManager 에서 참조값을 얻어내는 것이다.
- 액티비티는 특정 프래그먼트에 의존한다.
- 반면 프래그먼트는 재활용성을 고려했으므로 특정 Activity에 의존하면 안 된다.
- 그래서 ① 인터페이스를 하나 정의해놓고,
② 나를 제어하는 액티비티의 참조값을 찾아와서,
③ 해당 인터페이스로 구현한 후에
④ 그 인터페이스타입을 활용해서 메소드를 호출하면서
⑤ 어떤 정보를 전달하는 등으로 사용하면 된다.
- 프래그먼트는 매우 많이 쓰인다!
- 안드로이드의 코드 자유도는 일반 웹 java 코딩에 비해 매우 높다.
'국비교육(22-23)' 카테고리의 다른 글
72일차(2)/Android App(18) : Fragment(2), Fragment Lifecycle (0) | 2023.01.18 |
---|---|
72일차(1)/Android App(17) : Kotlin에서 java 클래스 사용, Lambda 표현식 (0) | 2023.01.18 |
71일차(1)/Android App(15) : Kotlin Abstract Class, Companion, Map (0) | 2023.01.17 |
70일차(2)/Android App(14) : Kotlin Extend, Interface / inner class 사용하기 (0) | 2023.01.16 |
70일차(1)/Android App(13) : Custom Adapter 생성, Serializable 인터페이스 구현 (0) | 2023.01.16 |