전공/Android
[영화 정보 앱 만들기] 06. 기타 기능
BBungyaLog
2024. 6. 22. 00:21
# 사진 넣기
1. 어댑터 클래스에서 뷰 홀더를 bind 해줄 때 어댑터 position을 이용한다
override fun onBindViewHolder(holder: MovieViewHolder, position: Int) {
when (position) {
0 -> {
holder.itemBinding.ivPhoto.setImageResource(R.drawable.movie01)
}
1 -> {
holder.itemBinding.ivPhoto.setImageResource(R.drawable.movie02)
}
2 -> {
holder.itemBinding.ivPhoto.setImageResource(R.drawable.movie03)
}
3 -> {
holder.itemBinding.ivPhoto.setImageResource(R.drawable.movie04)
}
4 -> {
holder.itemBinding.ivPhoto.setImageResource(R.drawable.movie05)
}
5 -> {
holder.itemBinding.ivPhoto.setImageResource(R.drawable.movie06)
}
}
if (position > 5) {
holder.itemBinding.ivPhoto.setImageResource(R.drawable.ic_launcher_foreground)
}
...
- 더 효율적으로 작성하려면?
val imageResources = arrayOf(
R.drawable.movie01,
R.drawable.movie02,
R.drawable.movie03,
R.drawable.movie04,
R.drawable.movie05,
R.drawable.movie06
)
if (position < imageResources.size) {
holder.itemBinding.ivPhoto.setImageResource(imageResources[position])
} else {
// Set a default image for positions outside the range of the array
holder.itemBinding.ivPhoto.setImageResource(R.drawable.ic_launcher_foreground)
}
2. update activity에 사진 넣기
- 위와 같이 이미지 배열 사용
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(updateBinding.root)
val dto = intent.getSerializableExtra("dto") as MovieDto
...
if (dto.id in 1..imageResources.size) {
updateBinding.ivUpdatePhoto.setImageResource(imageResources[dto.id - 1])
} else {
updateBinding.ivUpdatePhoto.setImageResource(R.drawable.ic_launcher_foreground)
}
...
# 검색 기능 만들기
1. 검색 레이아웃과 메뉴칸을 추가한다
- 레이아웃
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView5"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="영화 제목 검색하기"
android:textSize="25sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/etSearch"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:ems="10"
android:hint="검색어 입력"
android:inputType="text"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView5" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvSearch"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="20dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btnSearchCancel"
app:layout_constraintVertical_bias="0.0" />
<Button
android:id="@+id/btnSearchCancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="뒤로가기"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/etSearch" />
</androidx.constraintlayout.widget.ConstraintLayout>
- 메뉴칸 추가
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/menuItem01"
android:title="영화 추가" />
<item
android:id="@+id/menuItem02"
android:title="개발자 정보" />
<item
android:id="@+id/menuItem03"
android:title="영화 정보 검색" />
<item
android:id="@+id/menuItem04"
android:title="앱 종료"/>
</menu>

2. 검색 기능을 위한 어댑터를 새로 생성한다
- 기존 어댑터와 유사하지만 다른 점은 onBindHolder에서 item을 바인딩할 때 전체 리스트가 아닌 검색한 리스트를 바인딩한다.
override fun onBindViewHolder(holder: SearchViewHolder, position: Int) {
if (position < imageResources.size) {
holder.itemBinding.ivPhoto.setImageResource(imageResources[position])
} else {
// Set a default image for positions outside the range of the array
holder.itemBinding.ivPhoto.setImageResource(R.drawable.ic_launcher_foreground)
}
holder.itemBinding.tvMovie.text = filteredList[position].movie
holder.itemBinding.tvRate.text = filteredList[position].rate
holder.itemBinding.tvDirector.text = filteredList[position].director
}
- getFilter 함수를 구현한다
override fun getFilter(): Filter {
return object : Filter() {
override fun performFiltering(input: CharSequence?): FilterResults {
val userInput = input.toString().lowercase() // 사용자가 입력한 값
filteredList = if (userInput.isEmpty()) {
unFilteredList
} else {
val filteringList = ArrayList<MovieDto>()
for (movie in unFilteredList) {
if (movie.movie.lowercase().contains(userInput) || movie.director.lowercase().contains(userInput)) {
filteringList.add(movie)
}
}
filteringList
}
val filterResults = FilterResults()
filterResults.values = filteredList
return filterResults
}
@SuppressLint("NotifyDataSetChanged")
override fun publishResults(p0: CharSequence?, results: FilterResults?) {
filteredList = results?.values as List<MovieDto>
notifyDataSetChanged() // 필터링 된 결과가 갱신 되었음을 알려준다.
}
}
}
- 전체 코드는 다음과 같다
더보기
package ddwu.com.mobile.finalreport
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Filter
import android.widget.Filterable
import androidx.recyclerview.widget.RecyclerView
import ddwu.com.mobile.finalreport.data.MovieDto
import ddwu.com.mobile.finalreport.databinding.ListItemBinding
class SearchAdapter(val movies: ArrayList<MovieDto>)
: RecyclerView.Adapter<SearchAdapter.SearchViewHolder>(), Filterable {
val imageResources = arrayOf(
R.drawable.movie01,
R.drawable.movie02,
R.drawable.movie03,
R.drawable.movie04,
R.drawable.movie05,
R.drawable.movie06
)
private var filteredList: List<MovieDto> = movies
private val unFilteredList: List<MovieDto> = movies
override fun getItemCount(): Int = filteredList.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchViewHolder {
val itemBinding = ListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return SearchViewHolder(itemBinding)
}
override fun onBindViewHolder(holder: SearchViewHolder, position: Int) {
if (position < imageResources.size) {
holder.itemBinding.ivPhoto.setImageResource(imageResources[position])
} else {
// Set a default image for positions outside the range of the array
holder.itemBinding.ivPhoto.setImageResource(R.drawable.ic_launcher_foreground)
}
holder.itemBinding.tvMovie.text = filteredList[position].movie
holder.itemBinding.tvRate.text = filteredList[position].rate
holder.itemBinding.tvDirector.text = filteredList[position].director
}
inner class SearchViewHolder(val itemBinding: ListItemBinding)
: RecyclerView.ViewHolder(itemBinding.root) {
}
override fun getFilter(): Filter {
return object : Filter() {
override fun performFiltering(input: CharSequence?): FilterResults {
val userInput = input.toString().lowercase() // 사용자가 입력한 값
filteredList = if (userInput.isEmpty()) {
unFilteredList
} else {
val filteringList = ArrayList<MovieDto>()
for (movie in unFilteredList) {
if (movie.movie.lowercase().contains(userInput) || movie.director.lowercase().contains(userInput)) {
filteringList.add(movie)
}
}
filteringList
}
val filterResults = FilterResults()
filterResults.values = filteredList
return filterResults
}
@SuppressLint("NotifyDataSetChanged")
override fun publishResults(p0: CharSequence?, results: FilterResults?) {
filteredList = results?.values as List<MovieDto>
notifyDataSetChanged() // 필터링 된 결과가 갱신 되었음을 알려준다.
}
}
}
}
3. SearchActivity 구현하기
- SearchAdapter를 활용하여 액티비티를 완성한다.
package ddwu.com.mobile.finalreport
import android.annotation.SuppressLint
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import ddwu.com.mobile.finalreport.data.MovieDao
import ddwu.com.mobile.finalreport.data.MovieDto
import ddwu.com.mobile.finalreport.databinding.ActivitySearchBinding
class SearchActivity : AppCompatActivity() {
val searchBinding by lazy {
ActivitySearchBinding.inflate(layoutInflater)
}
lateinit var movies: ArrayList<MovieDto> // 데이터
val movieDao by lazy {
MovieDao(this)
}
@SuppressLint("Range")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(searchBinding.root)
// Fetch all movies from the database
movies = movieDao.getAllMovies()
// Setup RecyclerView
val adapter = SearchAdapter(movies)
searchBinding.rvSearch.layoutManager = LinearLayoutManager(this)
searchBinding.rvSearch.adapter = adapter
// Add TextWatcher to filter movies based on user input
searchBinding.etSearch.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
adapter.filter.filter(s)
}
override fun afterTextChanged(s: Editable?) {}
})
// Cancel button click listener
searchBinding.btnSearchCancel.setOnClickListener {
finish()
}
}
}
4. 메인에서 메뉴 클릭 시 액티비티 전환 구현
R.id.menuItem03 -> {
val intent = Intent(this@MainActivity, SearchActivity::class.java)
startActivity(intent)
}
5. 오류
- 검색 시 이미지가 filteredList의 position에 걸리는 것이 아닌 dto의 id에 영향을 받아야 제대로 이미지가 나온다
when (filteredList[position].id) {
1 -> {
holder.itemBinding.ivPhoto.setImageResource(R.drawable.movie01)
}
2 -> {
holder.itemBinding.ivPhoto.setImageResource(R.drawable.movie02)
}
3 -> {
holder.itemBinding.ivPhoto.setImageResource(R.drawable.movie03)
}
4 -> {
holder.itemBinding.ivPhoto.setImageResource(R.drawable.movie04)
}
5 -> {
holder.itemBinding.ivPhoto.setImageResource(R.drawable.movie05)
}
6 -> {
holder.itemBinding.ivPhoto.setImageResource(R.drawable.movie06)
}
}
if (filteredList[position].id > 6) {
holder.itemBinding.ivPhoto.setImageResource(R.drawable.ic_launcher_foreground)
}
동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.