240124 TIL #303 Android #16 JetPack - 1

김춘복·2024년 1월 24일
0

TIL : Today I Learned

목록 보기
303/494
post-thumbnail

Today I Learned

오늘은 안드로이드 공부!


  • 플랫폼 API

    ART(Android Runtime)에서 제공하는 안드로이드 앱의 핵심 라이브러리
    API레벨에 따른 호환성 문제가 발생한다.

Jetpack

안드로이드 앱을 개발하는 데 필요한 다양한 라이브러리 모음
플랫폼 API 외에 따로 추가된 API

  • androidx로 시작하는 패키지 명을 사용한다.

  • 앱 개발에 필요한 권장 아키텍처를 제공한다.

  • API 레벨의 호환성 문제를 해결한다.


appcompat 라이브러리

액티비티를 만들며 API레벨의 호환성 문제를 해결해준다.

  • build.gradle의 dependancy
implementation("androidx.appcompat:appcompat:1.6.1")
  • Activity 클래스 : AppCompatActivity() 상속
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {
// ...

기본 뷰 클래스

플랫폼 API에서 제공하는 기본뷰를 appcompat 라이브러리에서도 제공한다.

  • TextView는 AppCompatTextView라는 클래스로 제공한다. 그 외에 AppCompatImageView나 AppCompatButton 등이 다양하게 제공된다.

  • 사용 목적이나 기능은 거의 같지만 API 레벨 호환성을 고려하지 않아도 된다는 장점이 있다.


액션바

화면 위에 타이틀 문자열이 출력되는 영역

  • 액션바 아래의 content 영역에는 기본적으로 setContentView() 함수가 출력하는 내용이 나온다.

색상 설정

  • 테마 스타일은 res/values의 themes.xml 파일에 기본적으로 선언되어 있다.

  • 메니페스트 파일의 android:theme에서 테마를 설정할 수 있다.

  • colorPrimary와 colorSecondary는 앱의 전반적인 테마를 결정하는 색상이다. colorPrimary는 액션바와 버튼의 배경색으로, colorSecondary는 활성 상태를 표현한다.

  • colorOnPrimary와 colorOnSecondary는 위 색깔의 전경색으로 사용된다. colorPrimaryVariant는 colorSecondaryVariant는 그림자 색상으로 사용된다.


액션바 숨기기 설정

  • 액티비티 창은 기본적으로 액션바를 포함한다.

  • 액션바를 숨기고 싶으면 아래처럼 테마를 상속하면 된다.

<style name="Theme.AndroidLab" parent="android:Theme.Material.Light.NoActionBar" />

업버튼 설정

  • Up 버튼은 액티비티 화면이 앱의 첫화면이 아닐 때 이전화면으로 돌아가는 기능을 한다.

  • 매니페스트 파일에서 android:parentActivityName=".MainActivity" 설정을 등록만 해도 액티비티 화면에서 업버튼이 나온다.

  • 위의 방법을 쓰지않고 액티비티 코드로도 가능하다.
    이때는 자동으로 이전화면으로 되돌아 가지 않으므로 onSupportNavigateUp()를 재정의해 onBackPressed() 구문 등으로 기능을 넣어야 한다.

    override fun onCreate(savedInstanceState: Bundle?) {
    	//...
        supportActionBar?.setDisplayHomeAsUpEnabled(true)
    }
    	
        // 업버튼 클릭시 자동으로 호출되는 함수 재정의(선택사항)
        override fun onSupportNavigateUp(): Boolean {
        Log.d("event","네비게이션")
        onBackPressed()
        return super.onSupportNavigateUp()
    }

메뉴 구성

  • 액티비티에 메뉴를 추가하면 액션바 오른쪽에 오버플로 메뉴가 나타난다. 이 버튼을 누르면 메뉴가 아래로 확장되고, 몇몇은 액션 아이템으로 아이콘처럼 나올 수 있게 할 수 있다.

  • onCreateOptionsMenu : 액티비티가 실행되면서 처음 한번만 호출
    정적인 메뉴를 구성할 때 사용. 대부분 이거 씀.

  • onPrepareOptionMenu : 오버플로 메뉴가 나타날 때 마다 반복해서 호출
    동적인 메뉴를 구성하고 싶을 때 사용.

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        val menuItem1: MenuItem? = menu?.add(0,0,0,"menu1")
        val menuItem2: MenuItem? = menu?.add(0,1,0,"menu2")
        return super.onCreateOptionsMenu(menu)
    }
  • 매개변수의 Menu 객체를 메뉴바로 보면된다. 메뉴를 추가할 때 add() 함수를 이용한다. add()의 두번째 매개변수가 식별자고 4번째 매개변수가 문자열이다.

  • 메뉴 선택시 이벤트 처리
    위의 식별자를 이용해 = when(){} 구문으로 처리한다.

    override fun onOptionsItemSelected(item: MenuItem): Boolean = when(item.itemId) {
        0 -> {
            Log.d("event","menu1 click")
            true
        }
        1 -> {
            Log.d("event","menu2 click")
            true
        }
        else -> super.onOptionsItemSelected(item)
    }

리소스로 메뉴 구현

  • onCreateOptionsMenu() 함수로 액티비티 코드에서 메뉴를 구성할 수 있지만, 액티비티 메뉴는 대부분 정적으로 제공되므로 리소스 XML 파일로 구성하는 방법도 있다.

  • res/menu 디렉터리에 menu_main.xml 처럼 만들어 사용한다.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/menu1"
        android:title="menu1"/>
    <item
        android:id="@+id/menu2"
        android:title="menu2"
        android:icon="@drawable/ic_menu_add"
        app:showAsAction="always"/>

    <item
        android:id="@+id/menu_search"
        android:title="search"
        app:actionViewClass="androidx.appcompat.widget.SearchView"
        app:showAsAction="always"/>
</menu>
  • <item> 태그 하나가 메뉴 하나에 해당한다.
    id로 메뉴를 식별하며 title과 icon으로 문자열과 아이콘을 지정한다.

  • showAsAction 속성값
    never - 기본. 항상 오버플로 메뉴로 출력
    ifRoom - 공간이 있으면 액션 아이템(밖으로)으로, 아니면 메뉴 안으로 출력
    always - 항상 액션 아이템으로 출력

  • 액티비티 코드에서 해당 xml 적용

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        menuInflater.inflate(R.menu.menu_main, menu)
        return super.onCreateOptionsMenu(menu)
    }

액션뷰

  • 액션바에서 특별한 기능을 제공하며 대표적으로 검색기능의 서치뷰(androidx.appcompat.widget.SearchView)가 있다.

  • xml 구현

    <item
        android:id="@+id/menu_search"
        android:title="search"
        app:actionViewClass="androidx.appcompat.widget.SearchView"
        app:showAsAction="always"/>
  • 액티비티 코드
    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        val inflater = menuInflater
        inflater.inflate(R.menu.menu_main, menu)
        val menuItem = menu?.findItem(R.id.menu_search)
        val searchView = menuItem?.actionView as SearchView
        searchView.setOnQueryTextListener(object: SearchView.OnQueryTextListener{
            override fun onQueryTextChange(newText: String?): Boolean {
                // 검색어 변경 이벤트
                return true
            }

            override fun onQueryTextSubmit(query: String?): Boolean {
                // 키보드의 검색버튼 클릭한 순간의 이벤트
                return true
            }
        })
        return true
    }

툴바

  • 액션바와 사용목적은 같지만 액티비티 창이 자동으로 출력하는 액션바와 달리, 개발자가 직접 제어하는 뷰이기 때문에 좀 더 다양한 기능을 제공할 수 있다.

  • 개발자가 직접 레이아웃 xml 파일에 툴바를 작성해야 한다.
    androidx.appcompat.widget.Toolbar 클래스를 이용한다.

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        style="@style/Widget.MaterialComponents.Toolvar.Primary"/>
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        setSupportActionBar(binding.toolbar)
    }

프래그먼트

뷰이지만 액티비티처럼 동작하는 뷰이다.
액티비티에 작성할 수 있는 코드는 프래그먼트에서 사용할 수 있다.

  • 플랫폼API에서도 제공하지만 대부분 androidx.fragment 라이브러리를 사용한다.

  • 왼쪽 그림처럼 화면이 넓은 태블릿 같은 기기에서 한 액티비티에 너무 많은 코드를 작성해야 하는 문제가 있다. 목록과 항목 모두 한 액티비티 클래스에 구현하기는 부담스럽기 때문에 둘을 프래그먼트로 분리후 하나의 액티비티에서 조합해 사용한다.

프래그먼트 생명주기

프래그먼트는 액티비티처럼 동작하는 뷰다. 따라서 액티비티와 생명주기가 같다.

  • 생명주기는 크게 초기화, 생성, 시작, 재개, 소멸로 구분된다.

  • 백스백 : 프래그먼트가 화면에서 보이지 않아도 제거하지 않고 다시 사용할 수 있는 기능
    transaction.addToBackStack(null) 코드로 사용을 설정해야 한다.

  1. 초기화 - 프래그먼트의 화면을 구성할 뷰가 준비되지 않은 상태
    onAttach() : 프래그먼트가 액티비티에 붙을 때 호출
    onCreate() : 프래그먼트가 생성될 때 호출, 여기서 초기화 로직을 수행

  2. 생성 - 프래그먼트 화면을 구성할 뷰를 준비
    onCreateView() : 매개변수로 LayoutInflater 객체가 전달되어 프래그먼트 화면을 구성할 뷰 객체를 여기서 준비한다.
    onActivityCreated() : 주로 액티비티와 프래그먼트 간의 상호 작용을 설정하는 데 사용

  3. 시작 - 프래그먼트 화면이 사용자에게 보여짐
    onStart() : UI가 사용자에게 보여지기 전에 수행해야 하는 작업을 구현

  4. 재개 - 포커스를 가지고 사용자의 이벤트를 처리
    onResume() - 재개 단계에서 프래그먼트가 사용자와 상호 작용 가능한 상태가 되었을 때 호출

  5. 소멸
    백스택 사용불가면 onDestroy()가 호출되어 기존 프래그먼트는 제거.
    사용가능하면 onPause() -> onStop() -> onDestroyView()까지 호출되어 onCreateView()에서 생성된 뷰들이 소멸한다.
    여기에서 다시 onCreateView()로 시작할 수 있다.


프래그먼트 구현

  • 프래그먼트는 뷰지만 그 자체로는 화면에 아무것도 출력되지 않는다.
    따라서 먼저 레이아웃 XML을 작성하고 클래스 코드를 구현해야 한다.

  • 프래그먼트는 Fragment()를 상속받는다. 최소한 onCreateView() 함수는 있어야 한다.

class OneFragment : Fragment() {
    lateinit var binding: FragmentOneBinding
    
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        binding = FragmentOneBinding.inflate(inflater, container, false)
        return binding.root
    }

}
  • 이 프래그먼트를 포함할 액티비티의 레이아웃 xml에 등록해야 한다.
    <fragment
        android:id="@+id/fragmentView"
        android:name="com.example.ch11_jetpack.OneFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
  • 위의 방법 말고 코드에서 직접 객체를 생성해 화면에서 출력해야할 경우는 프래그먼트가 출력될 뷰를 xml에 하나 준비한다. 그리고 여기에다 동적으로 생성한 프래그먼트를 출력할 수도 있다.(레이아웃은 마음대로)
xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragment_content"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</LinearLayout>
// kotlin
val fragmentManager: FragmentManager = supportFragmentManager
val transaction: FragmentTransaction = fragmentManager.beginTransaction()
val fragment = OneFragment()
transaction.add(R.id.fragment_content, fragment)
transaction.commit()
  • 위는 FragmentTransaction 객체의 add()를 이용해 프래그 먼트 객체를 화면에 출력하는 코드다. 첫번째 매개변수가 프래그먼트가 출력될 뷰의 id값이다. commit()이 호출되면 적용된다.

  • add() - 새 프래그먼트를 화면에 추가
    replace() - 추가된 프래그먼트를 대체
    remove() - 프래그먼트 제거
    commit() - 화면에 적용

profile
꾸준히 성장하기 위해 매일 log를 남깁니다!

0개의 댓글