Day2

sumi Yoo·2022년 9월 1일

bottom navigation

bottom_navigation_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/page_game"
        android:enabled="true"
        android:icon="@android:drawable/ic_menu_slideshow"
        android:title=""/>
    <item
        android:id="@+id/page_setting"
        android:enabled="true"
        android:icon="@android:drawable/ic_menu_manage"
        android:title="설정"/>
</menu>

GameFragment.kt

class GameFragment: Fragment(){
    private var binding: GameFragmentBinding?=null
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = GameFragmentBinding.inflate(inflater, container, false)
        return binding!!.root
    }
}

GameFragment.kt

class GameFragment: Fragment(){
    private var binding: GameFragmentBinding?=null
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = GameFragmentBinding.inflate(inflater, container, false)
        return binding!!.root
    }
}

SettingFragment.kt(viewbinding 이용)

class SettingFragment:Fragment(){
    private var binding:SettingFragmentBinding?=null
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = SettingFragmentBinding.inflate(inflater, container, false)
        return binding!!.root
    }
}

game_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:id="@+id/fragment_container"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@+id/bnv_main"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bnv_main"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:menu="@menu/bottom_navigation_menu" />
</androidx.constraintlayout.widget.ConstraintLayout>

GameActivity.kt

class GameActivity : AppCompatActivity() {

    private var binding: GameActivityBinding?=null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // 바인딩 초기화
        binding=GameActivityBinding.inflate(layoutInflater)
        // 레이아웃(root뷰) 표시
        setContentView(binding!!.root)

        supportFragmentManager.beginTransaction().add(R.id.fragment_container, SettingFragment()).commit()
        
        binding!!.bnvMain.selectedItemId = R.id.page_setting
        binding!!.bnvMain.setOnItemSelectedListener{
            replaceFragment(
                when(it.itemId){
                    R.id.page_game->GameFragment()
                    else-> SettingFragment()
                }
            )
            true
        }

    }
    private fun replaceFragment(fragment: Fragment) {
        supportFragmentManager.beginTransaction().replace(R.id.fragment_container, fragment).commit()
    }
}

추가로 앱을 처음 열때 원하는 메뉴를 지정하고 싶을때는 밑의 코드를 추가해준다.
bnv_main.selectedItemId = R.id.first

bottom navigation
bottom navigation

open url

binding!!.btHelp.setOnClickListener {
            val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://boostcamp.connect.or.kr"))
            startActivity(intent)
        }

open url

다이얼로그 띄우기

binding!!.btInit.setOnClickListener {
            val builder = AlertDialog.Builder(this.context)
            builder
                .setTitle("게임 초기화")
                .setMessage(
                    "모든 설정을 지우려고 합니다.\n" +
                            "지금 지우면 점수를 잃어버릴 수 있습니다. " +
                            "다시 한 번 생각해보세요.\n" +
                            "별거 아니니까 지울까요?"
                )
                .setNegativeButton("CANCEL",object :DialogInterface.OnClickListener{
                    override fun onClick(p0: DialogInterface?, p1: Int) {
                        Log.d("SettingFragment","CANCEL")
                    }
                })
                .setPositiveButton("RESET",object :DialogInterface.OnClickListener{
                    override fun onClick(p0: DialogInterface?, p1: Int) {
                        Log.d("SettingFragment","RESET")
                    }
                })
                .create()
                .show()
        }

다이얼로그 띄우기

액티비티 생명주기

  • onCrate(): 최초로 앱을 실행하면 호출된다.
  • onStart(): 이 시점부터 사용자가 액티비티를 볼 수 있다.
  • onResume(): 실제 사용자와 상호작용이 가능한 포그라운드에 위치할때 호출된다. 이 상태를 액티비티가 실행 중인 것으로 본다.
  • onPause(): 사용자와 상호작용이 불가능한 상태, 포커스를 잃은 상태일때 호출된다.
  • onStop(): 액티비티가 더 이상 보이지 않을 때 호출된다.
  • onDestory(): 액티비티가 종료되거나 앱 프로세스 자체가 종료되면 호출된다.

image

주의할 점은 다이얼로그를 띄워서 액티비티가 최상단이 아닌 상황에는 onPause()가 호출되어야 할 것 같지만 실제로는 그렇지 않다. 다이얼로그가 액티비티의 일부이기 때문이다. 즉, 새로운 액티비티가 최상단으로 온 상황이 아니기 때문이다.
따라서 아무런 함수가 호출되지 않는다.

액티비티 생명주기

프래그먼트 생명주기

  1. onAttach()
  • 프래그먼트가 액티비티에 붙을 때 호출
  • 인자로 Context가 주어진다.
  1. onCreate()
  • 프래그먼트가 액티비티의 호출을 받아 생성
  • Bunddle로 액티비티로부터 데이터가 넘어옴
  • UI 초기화는 불가능
  1. onCreateView()
  • 레이아웃 inflate 담당
  • savedInstanceState로 이전 상태에 대한 데이터 제공
  • View와 관련된 객체를 초기화 할 수 있음
  1. onViewCreated()
  • onCreagteView()를 통해 반환된 View 객체는 onViewCreated()의 파라미터로 전달 된다.
  • 이 때 Lifecycle이 INITIALIZED 상태로 업데이트가 됨
  • 때문에 View의 초기값 설정, LiveData 옵저빙, RecyclerView, ViewPager2에 사용될 Adapter 세팅은 이 메소드에서 해주는 것이 적절함
  1. onViewStateRestored()
  • 저장해둔 모든 state 값이 Fragment의 View의 계층 구조에 복원되었을 때 호출 ex) 체크박스 위젯이 현재 체크되어있는가
  1. onStart()
  • 사용자에게 보여질 수 있을 때 호출
  • Activity의 onStart() 시점과 유사
  1. onResume()
  • 사용자와 프래그먼트가 상호작용 할 수 있는 상태일 때 호출
  1. onPause()
  • Fragment가 visible 일 때 onPause()가 호출
  1. onStop()
  • Fragment가 더 이상 화면에 보여지지 않게 되면 onStop() 콜백 호출
  • 부모 액티비티, 프래그먼트가 중단될 때, 상태가 저장될 때 호출
  1. onDestoryView()
  • 모든 exit animation, transaction이 완료되고 Fragment가 화면으로부터 벗어났을 경우 호출
  1. onDestroy()
  • Fragment가 제거되거나, FragmentManager가 destroy 됐을 경우, onDestroy() 콜백 함수가 호출
  1. onDetach()
  • 프래그먼트가 액티비티로부터 해제되어질 때 호출된다.

프래그먼트 생명주기

View

화면의 기본적인 컴포넌트이다. TextView, ImageView, EditText, ... 등 자주 쓰는 모든 UI 관련된 컴포넌트는 View라는 클래스를 상속 받는다.
image

ViewGroup

ViewGroup은 n개의 View를 포함할 수 있는 Container이다. 우리가 자주 쓰는 레이아웃이 ViewGroup에 해당한다.
View를 상속받아 만들어진 클래스이다. ex) LinearLayout, ConstraintLayout ...
image
ViewGroup에는 View, ViewGroup이 포함될 수 있으며, ViewGroup은 결국 View를 포함한다. 모든 UI는 결국 View이다.

View와 ViewGroup

Q1. ViewGroup이 view를 포함할 수 있는 근거

textview는 textview를 담을 수 없다.
하지만 constraint layout은 textview를 담을 수 있다.
textview는 view고, constraint layout도 view인데 왜 안될까?
[ViewGroup.class]

public abstract class ViewGroup extends android.view.View implements android.view.ViewParent, android.view.ViewManager { ... }

[View.class]

public class View implements android.graphics.drawable.Drawable.Callback, android.view.KeyEvent.Callback, android.view.accessibility.AccessibilityEventSource { ... }

상속 받는 클래스의 종류가 서로 다르다.

0개의 댓글