보통 액티비티에서 Fragment 를 띄우는 경우,
액티비티 내부에서 사용할 수 있었던 메서드들이 Fragment에선 사용할 수 없는 경우가 많다.
또한 파라미터로 Context를 받는 메서드들도 어떻게 사용해야할지 난감할 수 있다.
Fragment클래스는 Context를 상속 받지 않기 때문에 발생하는 일이다.
즉, 일반적인 방법으로는 Fragment 내부에서 아래의 메서드들을 사용하기가 어렵다.
1. Context 에 정의된 메서드
- findViewById, runOnUIThread, getApplicationContext , getSystemService, startActivity 등
2. Context를 파라미터(매개변수)로 받는 메서드
- Toast 등
# Framgment에서 Context 사용 방법
핵심은 Context를 어디선가 취득하는 것이다.
Fragment클래스의 생명주기 콜백 메서드인 onAttach()의 매개변수로 Context가 들어온다.
또한 매개변수로 들어온 Context는 플래그먼트를 띄운 Activity로 형변환하여 사용할 수 있다.
따라서 Flagment의 전역변수(프로퍼티)로 선언하고, onAttach()에서 할당해두면
어디에서든 Context(액티비티)를 참조하여 사용할 수 있게 된다.
즉, 아래처럼 코드를 작성하면 된다.
1. Fragment클래스의 프로퍼티로 Context를 참조할 변수 선언
2. onAttach() 콜백메서드에서 Context를 MainActivity로 형변환하여 할당
class FragmentText : Fragment() {
// 1. Context를 할당할 변수를 프로퍼티로 선언(어디서든 사용할 수 있게)
lateinit var mainActivity: MainActivity
override fun onAttach(context: Context) {
super.onAttach(context)
// 2. Context를 액티비티로 형변환해서 할당
mainActivity = context as MainActivity
}
}
이렇게 해두면,
Fragment에서도 runOnUiThread, findViewById, getApplicationCotext, Toast 등을 아래처럼 모두 사용할 수 있다.
1. Context 에 정의된 메서드
- mainActivity.findViewById
- mainActivity.runOnUIThread
- mainActivity.getApplicationContext
- mainActivity.getSystemService
- mainActivity.startActivity 등
2. Context를 파라미터(매개변수)로 받는 메서드
- Toast(mainActivity, "메세지", Toast.LENGTH_LONG) 등
# Fragment Context 사용 예제 코드
아래는 테스트 해본 코드 전문이다.
1. 레이아웃 XML파일에 TextView, Button 하나씩 생성해두고,
2. onAttach() 에서 Context 를 받아오고,
3. onStart() 콜백 메서드에서 사용한다.
- TextView를 참조해서 스레드를 통해 3초후에 내용이 바뀌게 하고,
- Button을 참조해서 클릭하면 Toast가 뜨도록 구현했다.
class FragmentText : Fragment() {
// 1. Context를 받아올 변수 선언
lateinit var mainActivity: MainActivity
override fun onAttach(context: Context) {
super.onAttach(context)
// 2. Context를 Activity로 형변환하여 할당
mainActivity = context as MainActivity
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_life, container, false)
}
override fun onStart() {
super.onStart()
// Button을 참조해서 클릭 리스너 달기
mainActivity.findViewById<Button>(R.id.buttonTest).setOnClickListener {
Toast.makeText(mainActivity, "플래그먼트에서도 Toast가 됩니다", Toast.LENGTH_LONG).show()
}
// TextView를 참조해서 텍스트 바꾸기
mainActivity.findViewById<TextView>(R.id.textViewTest).text = "처음"
// 스레드 돌리기
thread(start = true){
Thread.sleep(3000)
// 3초후에 runOnUiThread를 통해 TextView의 텍스트 바꾸기
mainActivity.runOnUiThread {
mainActivity.findViewById<TextView>(R.id.textViewTest).text = "3초후에 바뀌었네요"
}
}
}
}
- 실행 결과
주의할 점은
onCreateView에서 mainActivity.findViewById()메서드를 통해 Button 등의 리소스를 취득하려 하면,
참조를 못해서 null 오류가 발생할 수 있다는 것이다.(아직 액티비티에게 전달되지 않은 생태라 시간차가 있다)
이 경우에는 아래와 같이 레이아웃을 inflate하여 View 객체에 할당한 다음 findViewById()를 호출하면 된다.
class FragmentText : Fragment() {
// 1. Context를 받아올 변수 선언
lateinit var mainActivity: MainActivity
override fun onAttach(context: Context) {
super.onAttach(context)
// 2. Context를 Activity로 형변환하여 할당
mainActivity = context as MainActivity
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// 1. 레아이웃을 inflate()하여 view를 변수에 저장
val view = inflater.inflate(R.layout.fragment_life, container, false)
// 2. view객체에서 findViewById()메서드 호출
view.findViewById<TextView>(R.id.textViewTest).text = "처음"
view.findViewById<Button>(R.id.buttonTest).setOnClickListener {
Toast.makeText(mainActivity, "플래그먼트에서도 Toast가 됩니다", Toast.LENGTH_LONG).show()
}
// 3. 레이아웃 view를 액티비티에게 반환
return view
}
}
또는 개발환경에 따라 onStart() 등에서 mainActiviy.findViewById()를 사용하여 리소스를 참조해서 사용하면 될듯 하다.
그리고 사실 이 문제는 뷰바인딩을 사용하면 깔끔하게 해결된다.
[안드로이드 Kotlin] Fragment 에서 뷰바인딩(View Binding) 사용 방법
'개발(Development) > Android(안드로이드)' 카테고리의 다른 글
[안드로이드 kotlin] Admob 앱 배너 광고 넣는 방법( + 테스트광고) (0) | 2021.07.23 |
---|---|
[안드로이드 Kotlin] JSON 파일 저장 및 파싱 방법: JSONArray, JSONObject (0) | 2021.07.21 |
[안드로이드 Kotlin] Fragment 에서 뷰바인딩(View Binding) 사용 방법 (0) | 2021.07.20 |
[안드로이드] RadioGroup의 RadioButton 가운데/비율 정렬 배치 방법 (0) | 2021.07.20 |
[안드로이드] Constraint Layout의 chain weight(비율) 설정 View 배치 방법 (0) | 2021.07.19 |
댓글