오늘은 안드로이드 공부!
ART(Android Runtime)에서 제공하는 안드로이드 앱의 핵심 라이브러리
API레벨에 따른 호환성 문제가 발생한다.
안드로이드 앱을 개발하는 데 필요한 다양한 라이브러리 모음
플랫폼 API 외에 따로 추가된 API
androidx로 시작하는 패키지 명을 사용한다.
앱 개발에 필요한 권장 아키텍처를 제공한다.
API 레벨의 호환성 문제를 해결한다.
액티비티를 만들며 API레벨의 호환성 문제를 해결해준다.
implementation("androidx.appcompat:appcompat:1.6.1")
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
// ...
플랫폼 API에서 제공하는 기본뷰를 appcompat 라이브러리에서도 제공한다.
TextView는 AppCompatTextView라는 클래스로 제공한다. 그 외에 AppCompatImageView나 AppCompatButton 등이 다양하게 제공된다.
사용 목적이나 기능은 거의 같지만 API 레벨 호환성을 고려하지 않아도 된다는 장점이 있다.
화면 위에 타이틀 문자열이 출력되는 영역
테마 스타일은 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)
}
뷰이지만 액티비티처럼 동작하는 뷰이다.
액티비티에 작성할 수 있는 코드는 프래그먼트에서 사용할 수 있다.
프래그먼트는 액티비티처럼 동작하는 뷰다. 따라서 액티비티와 생명주기가 같다.
생명주기는 크게 초기화, 생성, 시작, 재개, 소멸로 구분된다.
백스백 : 프래그먼트가 화면에서 보이지 않아도 제거하지 않고 다시 사용할 수 있는 기능
transaction.addToBackStack(null)
코드로 사용을 설정해야 한다.
초기화
- 프래그먼트의 화면을 구성할 뷰가 준비되지 않은 상태
onAttach() : 프래그먼트가 액티비티에 붙을 때 호출
onCreate()
: 프래그먼트가 생성될 때 호출, 여기서 초기화 로직을 수행
생성
- 프래그먼트 화면을 구성할 뷰를 준비
onCreateView()
: 매개변수로 LayoutInflater 객체가 전달되어 프래그먼트 화면을 구성할 뷰 객체를 여기서 준비한다.
onActivityCreated() : 주로 액티비티와 프래그먼트 간의 상호 작용을 설정하는 데 사용
시작
- 프래그먼트 화면이 사용자에게 보여짐
onStart() : UI가 사용자에게 보여지기 전에 수행해야 하는 작업을 구현
재개
- 포커스를 가지고 사용자의 이벤트를 처리
onResume() - 재개 단계에서 프래그먼트가 사용자와 상호 작용 가능한 상태가 되었을 때 호출
소멸
백스택 사용불가면 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
}
}
<fragment
android:id="@+id/fragmentView"
android:name="com.example.ch11_jetpack.OneFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
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() - 화면에 적용