자동완성을 지원하는 텍스트 뷰
<AutoCompleteTextView
android:id="@+id/main_auto_complete_tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:completionThreshold="2"
android:hint="검색어를 입력하세요"
android:maxLines="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
adapter로 데이터를 전달해주어야하므로
val wordList = mutableListOf("one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten")
val adapter = ArrayAdapter(this, android.R.layout.simple_dropdown_item_1line, wordList)
binding = ActivityAutoCompleteBinding.inflate(layoutInflater).apply {
mainAutoCompleteTv.setAdapter(adapter)
}
간단한 arrayAdapter를 사용한다.
를 사용하면 , 를 tokenizer로 사용하여 2개 이상의 값을 검색할 수 있다.
binding = ActivityMultiAutoBinding.inflate(layoutInflater).apply {
mainMultiAutoCompleteTv.apply {
setAdapter(adapter)
setTokenizer(MultiAutoCompleteTextView.CommaTokenizer())
}
}
이 외에 중간부터 검색되는 기능 등을 위해서는
binding.mainMultiAutoCompleteTv.addTextChangedListener {
}
textChangedListener
등을 정의하여 직접 구현해주어야 한다.
EditText에 입력된 텍스트를 기반으로 텍스트가 유동적으로 반응하도록 하기 위해 Material Design에서 만든 뷰이다.
https://m2.material.io/components/text-fields/android#using-text-fields
간단한 예제로 비밀번호를 아이콘을 누르면 입력 내용을 보이게 하는 등을 따로 코드 작업 없이 할 수 있다.
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="User Name"
app:endIconMode="password_toggle"
app:startIconDrawable="@drawable/baseline_favorite_24">
<com.google.android.material.textfield.TextInputEditText
android:inputType="textPassword"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/teal_200" />
</com.google.android.material.textfield.TextInputLayout>
TextInputLayout isErrorEnabled, error를 멤버변수로가지고 있고, 각각
에러 아이콘을 보여줄지, 에러 메시지를 무엇으로 할지를 의미한다.
binding.num.addTextChangedListener {
if(it.toString().toInt() >= 1000){
binding.outlinedTextField1.isErrorEnabled = true
binding.outlinedTextField1.error = "1000보다 작아야 합니다"
}
else{
binding.outlinedTextField1.isErrorEnabled = false
binding.outlinedTextField1.error = null
}
}
스크롤 뷰 해당 영역을 화면 스크롤이 되도록 해준다.
HorizontalScrollView
는 태그로 horizontal scrollview를 만들 수 있다.보통 RecyclerView가 ScrollView 안에 들어가는 경우에 종종 쓰는데, recycler뷰가 따로 스크롤 되지 않고 전체와 같이 스크롤 되도록 해준다.
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="80dp"
android:textSize="20sp"
android:background="@android:color/holo_green_light"
android:gravity="center"
android:text="Lorem ipsum dolor sit amet" />
<TextView
android:layout_width="match_parent"
android:layout_height="80dp"
android:textSize="20sp"
android:background="@android:color/holo_red_light"
android:gravity="center"
android:text="consectetur adipisicing elit" />
<TextView
android:layout_width="match_parent"
android:layout_height="80dp"
android:textSize="20sp"
android:background="@android:color/darker_gray"
android:gravity="center"
android:text="sed do eiusmod tempor incididunt" />
<TextView
android:layout_width="match_parent"
android:layout_height="80dp"
android:textSize="20sp"
android:background="#00ffff"
android:gravity="center"
android:text="ut labore et dolore magna aliqua. " />
<TextView
android:layout_width="match_parent"
android:layout_height="80dp"
android:textSize="20sp"
android:background="@android:color/holo_orange_light"
android:gravity="center"
android:text="Ut enim ad minim veniam," />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:background="@android:color/holo_purple"
android:gravity="center"
android:text=" quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. " />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/main_rv2"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/recyclerview_item" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
사용방법은 단순히 scrollview 태그 대신 nestedScrollView를 사용하면 된다.
여러가지 항목을 목록형태로 보여주는 뷰이다.
역시 adapter로 데이터를 연결해주어야 하고, 선택에 따른 분기 실행을 할 수 있다.
``
```kotlin
val idolList = arrayOf("아이유", "브레이브걸스", "방탄소년단", "SG워너비", "블랙핑크", "아이즈원", "샤이니", "트와이스", "ITZY", "오마이걸")
val spinnerAdapter = ArrayAdapter(this, android.R.layout.simple_spinner_dropdown_item, idolList)
binding = ActivitySpinnerBinding.inflate(layoutInflater).apply {
mainSpinner.adapter = spinnerAdapter
mainSpinner.onItemSelectedListener = object : OnItemSelectedListener{
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
Toast.makeText(this@SpinnerActivity, "position:$position", Toast.LENGTH_SHORT).show()
}
override fun onNothingSelected(parent: AdapterView<*>?) {
TODO("Not yet implemented")
}
}
}
이 역시 Material Design에서 제공하는 컴포넌트가 있다.
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:src="@drawable/outline_done_black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
이 생성한 플로팅 버튼에 움직이는 기능 등의 애니메이션을 추가하면서 버튼을 누를 때 다른 버튼들이 위로 펼쳐올라오게끔 하려고 한다.
애니메이션 문서의 ObjectAnimator
를 사용하여 뷰에 대해 x,y축에 대한 변경을 할 수 있다
https://developer.android.com/develop/ui/views/animations/prop-animation
https://developer.android.com/guide/topics/graphics/prop-animation?hl=ko
(x축방향으로 100위치에 1000밀리초동안 이동)
binding.fab.ObjectAnimator.ofFloat(fab, "translationX", 100f).apply {
duration = 1000
start()
}
(버튼 누를 때마다 서로 다른 버튼 여러개를 각각 다른 y축 값에 배치하도록 토글하는 함수)
private fun toggle() {
if (isOpen) {
ObjectAnimator.ofFloat(binding.fab1, "translationY", 0f).apply {
start()
}
ObjectAnimator.ofFloat(binding.fab2, "translationY", 0f).apply {
start()
}
} else {
ObjectAnimator.ofFloat(binding.fab1, "translationY", -300f).apply {
start()
}
ObjectAnimator.ofFloat(binding.fab2, "translationY", -600f).apply {
start()
}
}
isOpen = !isOpen
}
중요도가 낮거나 거의 사용되지 않는 뷰를 나중에 실제로 필요할 때까지로딩을 지연시키기 위해 사용
따라서 빈 껍데기만 미리 마련하고 나중에 실제 뷰를 렌더링 할 때 사용되는 빈 껍데기 뷰이다.
RecyclerView도 위의 원리로 돌아간다.
<ViewStub
android:id="@+id/main_stub"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inflatedId="@+id/layout_viewstub"
android:layout="@layout/layout_viewstub"/>
layout 속성에 뷰에넣을 그림 등의 레이아웃을 넣어주고
visibility를 VISIBLE로 하거나 stub.visibility = View.VISIBLE
, inflate()
를 통해 화면에 렌더링해준다. visible로 하는 것 또한 inflate되지 않은 뷰는 inflate 해주는 코드가 내장되어있기 때문에 가능한 것이다.
mainBtnShow.setOnClickListener {
// mainStub.visibility = View.VISIBLE
mainStub.inflate()
}
inflate는 한번 이미 inflate된것을 또하려고 하면 오류가 나니 주의해야 한다.
if (mainStub.parent is ViewGroup)
mainStub.inflate()
조건문을 통해 inflate를 제어하는 등의 코드를 써야한다.
xml에 같은 레이아웃을 여러번 복사붙여넣기하는 것이 아닌, 자주 사용하는 레이아웃을 따로 만들고 필요할 때마다 사용하기 위한 뷰.
layout_include로 만든 xml 파일을 include 태그로 재사용하기
<include
layout="@layout/layout_include"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
레이아웃에서 특정 뷰에 포커스를 두도록 하기 위해 사용하는 뷰
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content">
<requestFocus />
</EditText>
이에 더해 포커스가 있는 뷰를 코드상에서 currentFocus
로 가져올 수도 있다.
참고로 currentFocus는 view가 invisible하다면 가져와지지 않고 null 을 반환한다.
DrawerLayout과 NavigationView는 일반적으로 함께사용한다.
xml 최상단을 DrawerLayout으로 하고 메뉴가 열리기 전화면을 activity, 열린 메뉴의 화면은 NavigationView가 담당한다.
그리고 activity의 레이아웃을 navigationView의 레이아웃보다 앞에 선언해야 한다.
<androidx.drawerlayout.widget.DrawerLayout
.
.
.
mainView 영역
.
.
.
<com.google.android.material.navigation.NavigationView
android:id="@+id/navigation_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#CCCBCB"
android:orientation="vertical"
app:headerLayout="@layout/nav_header"
app:menu="@menu/drawer"
app:itemTextColor="@color/red"/>
</androidx.drawerlayout.widget.DrawerLayout>
navigationView를 화면에 보여지게 할 때는
binding.mainInclude.mainBtn.setOnClickListener {
binding.mainDrawerLayout.openDrawer(GravityCompat.START) //어느 쪽에서 open 할 지
}
위와 같이
NavigationView를 띄우는 것이 아닌 DrawerLayout의 openDrawer를 불르면 안의 navigation을 불러준다.
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
를 작성하면 좌상단에 기본으로 백 버튼을 가진 아이콘이 보여진다. 엄밀히 말하면 Home을 활성화시켜주는 것이다.
supportActionBar?.setHomeAsUpIndicator(R.drawable.baseline_menu_24)
적당히 원하는 아이콘으로 바꿔주고
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if(item.itemId == android.R.id.home){
Log.d("싸피", "onOptionsItemSelected: ${item.itemId}")
if(binding.mainDrawerLayout.isDrawerOpen(GravityCompat.START))
binding.mainDrawerLayout.closeDrawer(GravityCompat.START)
else
binding.mainDrawerLayout.openDrawer(GravityCompat.START)
}
return super.onOptionsItemSelected(item)
}
onOptionsItemSelected
를 오버라이딩한다. 참고로 툴바의 좌상단 아이콘 영역은 앞서 언급한대로 Home
이므로 item.itemId == android.R.id.home
와 같이 작성해준다.