Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

부산대 Android_김민혁_2주차_과제_STEP2 #49

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
9f4d9a7
docs: add project overview of STEP1 to README
kyleidea1 Jul 1, 2024
b2b33de
feat(view): Add activity_search.xml view for SearchView
kyleidea1 Jul 1, 2024
8eac791
feat(view): Add SearchMainActivity.kt
kyleidea1 Jul 1, 2024
a21a7c2
feat(view): Add DBHelper.kt which is local storage of places.
kyleidea1 Jul 2, 2024
f5e9623
feat: Added 9 data into Place.db
kyleidea1 Jul 2, 2024
b3a1339
docs: Modified README.md
kyleidea1 Jul 2, 2024
deb1a3c
feat: Create STEP1 branch and move existing work to it
kyleidea1 Jul 2, 2024
d3adda3
docs: Modified README.md for STEP2
kyleidea1 Jul 2, 2024
b959286
feat(db): add SearchHistoryDBHelper and initial place data
kyleidea1 Jul 5, 2024
a03fdc7
refactor(db): extract Place data class to separate file
kyleidea1 Jul 5, 2024
84ad89b
feat(ui): add SearchHistoryRecyclerViewAdapter for displaying search …
kyleidea1 Jul 5, 2024
496dd1d
fix(SearchActivity): Fix filtering logic to correctly filter search r…
kyleidea1 Jul 5, 2024
a62f21e
refactor: Split DBHelper into PlaceDBHelper and SearchHistoryDBHelper
kyleidea1 Jul 5, 2024
b043982
Refactor XML layout to fix visibility bug in SearchActivity
kyleidea1 Jul 5, 2024
7c26fd4
Add: Extracted Korean strings into string resources and updated decla…
kyleidea1 Jul 5, 2024
e404119
Add: Extracted Korean strings into string resources and updated decla…
kyleidea1 Jul 5, 2024
61d5adc
Refactor: Changed variable names to enhance consistency
kyleidea1 Jul 5, 2024
62e3846
docs: Modified README.md
kyleidea1 Jul 5, 2024
9dd9533
Added missing files
kyleidea1 Jul 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
# android-map-keyword
# android-map-keyword STEP 2

## 개요
android-map-keyword STEP2에서는 STEP1에서 생성한 로컬 데이터베이스에 들어있는 장소들에 대한 검색 기능을 구현합니다.

## 기능
- 검색어를 입력하면 검색 결과 세로 스크롤이 되는 검색 결과 목록이 표시됨
- 검색어는 X를 눌러서 삭제 가능
- 검색 결과 목록에서 하나의 항목을 선택 가능, 선택된 항목은 가로 스크롤 가능한 검색어 저장 목록에 추가
- 저장된 검색어는 X를 눌러서 삭제 가능, 앱을 재실행해도 검색어 저장 목록은 유지
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
android:theme="@style/Theme.Map"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:name=".SearchActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand Down
11 changes: 0 additions & 11 deletions app/src/main/java/campus/tech/kakao/map/MainActivity.kt

This file was deleted.

8 changes: 8 additions & 0 deletions app/src/main/java/campus/tech/kakao/map/Place.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package campus.tech.kakao.map

data class Place(
val idx: Int,
val name: String,
val category: String,
val address: String
)
72 changes: 72 additions & 0 deletions app/src/main/java/campus/tech/kakao/map/PlaceDBHelper.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package campus.tech.kakao.map

import android.content.ContentValues
import android.content.Context
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
class PlaceDBHelper(context: Context) : SQLiteOpenHelper(context, "Place.db", null, 2) {

override fun onCreate(db: SQLiteDatabase?) {
createPlaceTable(db)
insertInitialPlaceData(db)
}

override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
db?.execSQL("DROP TABLE IF EXISTS PlaceTable")
onCreate(db)
}

private fun createPlaceTable(db: SQLiteDatabase?) {
val placeTableSQL = """
CREATE TABLE IF NOT EXISTS PlaceTable (
idx INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
category TEXT NOT NULL,
address TEXT NOT NULL
)
"""
db?.execSQL(placeTableSQL)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(지금 대응하지 않으셔도 됩니다)
db에 쿼리를 날리는건 에러가 발생할수 있는 대표적인 케이스중 하나입니다
어떤 에러가 발생할수 있는지와 케이스별로 어떻게 대응하면 좋을지 한번 고민 해보시겠어요?

}

private val context: Context = context.applicationContext
private fun insertInitialPlaceData(db: SQLiteDatabase?) {
val initialPlaces = listOf(
Place(1, context.getString(R.string.place_kakao_cafe), context.getString(R.string.category_cafe), context.getString(R.string.place_address_1)),
Place(2, context.getString(R.string.place_kakao_restaurant), context.getString(R.string.category_restaurant), context.getString(R.string.place_address_2)),
Place(3, context.getString(R.string.place_kakao_pub), context.getString(R.string.category_pub), context.getString(R.string.place_address_3)),
Place(4, context.getString(R.string.place_tech_cafe), context.getString(R.string.category_cafe), context.getString(R.string.place_address_4)),
Place(5, context.getString(R.string.place_tech_restaurant), context.getString(R.string.category_restaurant), context.getString(R.string.place_address_5)),
Place(6, context.getString(R.string.place_tech_pub), context.getString(R.string.category_pub), context.getString(R.string.place_address_6)),
Place(7, context.getString(R.string.place_campus_cafe), context.getString(R.string.category_cafe), context.getString(R.string.place_address_7)),
Place(8, context.getString(R.string.place_campus_restaurant), context.getString(R.string.category_restaurant), context.getString(R.string.place_address_8)),
Place(9, context.getString(R.string.place_campus_pub), context.getString(R.string.category_pub), context.getString(R.string.place_address_9))
)

initialPlaces.forEach { place ->
val values = ContentValues().apply {
put("name", place.name)
put("category", place.category)
put("address", place.address)
}
db?.insert("PlaceTable", null, values)
}
}

fun getAllPlaces(): MutableList<Place> {
val db = readableDatabase
val cursor: Cursor = db.rawQuery("SELECT * FROM PlaceTable", null)
val places = mutableListOf<Place>()
if (cursor.moveToFirst()) {
do {
val idx = cursor.getInt(cursor.getColumnIndexOrThrow("idx"))
val name = cursor.getString(cursor.getColumnIndexOrThrow("name"))
val category = cursor.getString(cursor.getColumnIndexOrThrow("category"))
val address = cursor.getString(cursor.getColumnIndexOrThrow("address"))
places.add(Place(idx, name, category, address))
} while (cursor.moveToNext())
}
cursor.close()
return places
}
}
63 changes: 63 additions & 0 deletions app/src/main/java/campus/tech/kakao/map/SearchHistoryDBHelper.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package campus.tech.kakao.map

import android.content.ContentValues
import android.content.Context
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
class SearchHistoryDBHelper(context: Context) : SQLiteOpenHelper(context, "SearchHistory.db", null, 1) {

override fun onCreate(db: SQLiteDatabase?) {
createSearchHistoryTable(db)
}

override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
db?.execSQL("DROP TABLE IF EXISTS SearchHistoryTable")
onCreate(db)
}

private fun createSearchHistoryTable(db: SQLiteDatabase?) {
val searchHistoryTableSQL = """
CREATE TABLE SearchHistoryTable (
idx INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
category TEXT NOT NULL,
address TEXT NOT NULL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
)
"""
db?.execSQL(searchHistoryTableSQL)
}

fun insertSearchHistory(place: Place): Long {
val db = writableDatabase
val values = ContentValues().apply {
put("name", place.name)
put("category", place.category)
put("address", place.address)
}
return db.insert("SearchHistoryTable", null, values)
}

fun deleteSearchHistoryByName(name: String): Int {
val db = writableDatabase
return db.delete("SearchHistoryTable", "`name` = ?", arrayOf(name))
}

fun getAllSearchHistory(): MutableList<Place> {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

결과값을 MutableList로 보낼 필요가 있나요?
대부분 경우에서 수정을 제한하고자 Immutable한 값을 넘겨주곤 합니다

val db = readableDatabase
val cursor: Cursor = db.rawQuery("SELECT * FROM SearchHistoryTable ORDER BY timestamp DESC", null)
val searchHistory = mutableListOf<Place>()
if (cursor.moveToFirst()) {
do {
val idx = cursor.getInt(cursor.getColumnIndexOrThrow("idx"))
val name = cursor.getString(cursor.getColumnIndexOrThrow("name"))
val category = cursor.getString(cursor.getColumnIndexOrThrow("category"))
val address = cursor.getString(cursor.getColumnIndexOrThrow("address"))
searchHistory.add(Place(idx, name, category, address))
} while (cursor.moveToNext())
}
cursor.close()
return searchHistory
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package campus.tech.kakao.map

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView

class SearchHistoryRecyclerViewAdapter(
private var searchHistory: MutableList<Place>,
private val onItemClick: (Int) -> Unit,
private val onItemDelete: (Int) -> Unit
) : RecyclerView.Adapter<SearchHistoryRecyclerViewAdapter.ViewHolder>() {
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val searchHistoryBtn: TextView = itemView.findViewById(R.id.search_history_item)
val searchHistoryDeleteBtn: ImageView = itemView.findViewById(R.id.search_history_delete_button)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_search_history, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, index: Int) {
holder.searchHistoryBtn.text = searchHistory[index].name
holder.searchHistoryBtn.setOnClickListener { onItemClick(index) }
holder.searchHistoryDeleteBtn.setOnClickListener {
onItemDelete(index)
notifyItemRemoved(index)
notifyItemRangeChanged(index, searchHistory.size);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RecyclerView가 변경감지를 최소화하도록 잘 작업하셨군요 👏
그런데 이런 작업을 더 쉽게 해주는 DiffUtil이란 친구도 있습니다.
나중에 한번 알아보세요!

}
}
override fun getItemCount(): Int { return searchHistory.size }
}
8 changes: 8 additions & 0 deletions app/src/main/res/drawable/search_bar_background.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FFFFFF"/>
<corners android:radius="8dp"/>
<stroke
android:width="1dp"
android:color="#D1D1D1"/>
</shape>
19 changes: 0 additions & 19 deletions app/src/main/res/layout/activity_main.xml

This file was deleted.

43 changes: 43 additions & 0 deletions app/src/main/res/layout/activity_search.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FAF9F7"
android:orientation="vertical"
tools:context=".SearchActivity">

<androidx.appcompat.widget.SearchView
android:id="@+id/search_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:queryHint="@string/enter_keyword_label"
android:iconifiedByDefault="false"/>

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/horizontal_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:orientation="horizontal"/>

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:padding="8dp"
android:visibility="gone"/>

<TextView
android:id="@+id/no_results"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/no_search_result_label"
android:textColor="#9E9E9E"
android:textSize="16sp"
android:visibility="visible"
android:gravity="center"/>

</LinearLayout>
27 changes: 27 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
<resources>
<string name="app_name">Map</string>
<string name="enter_keyword_label">검색어를 입력해 주세요.</string>
<string name="no_search_result_label">검색 결과가 없습니다.</string>

<string name="place_kakao_cafe">카카오 카페</string>
<string name="place_kakao_restaurant">카카오 식당</string>
<string name="place_kakao_pub">카카오 포차</string>
<string name="place_tech_cafe">테크 카페</string>
<string name="place_tech_restaurant">테크 식당</string>
<string name="place_tech_pub">테크 포차</string>
<string name="place_campus_cafe">캠퍼스 카페</string>
<string name="place_campus_restaurant">캠퍼스 식당</string>
<string name="place_campus_pub">캠퍼스 포차</string>

<string name="category_cafe">카페</string>
<string name="category_restaurant">식당</string>
<string name="category_pub">주점</string>

<string name="place_address_1">금정구 1</string>
<string name="place_address_2">금정구 2</string>
<string name="place_address_3">금정구 3</string>
<string name="place_address_4">금정구 4</string>
<string name="place_address_5">금정구 5</string>
<string name="place_address_6">금정구 6</string>
<string name="place_address_7">금정구 7</string>
<string name="place_address_8">금정구 8</string>
<string name="place_address_9">금정구 9</string>

</resources>