-
Notifications
You must be signed in to change notification settings - Fork 33
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
base: main
Are you sure you want to change the base?
Changes from 18 commits
9f4d9a7
b2b33de
8eac791
a21a7c2
f5e9623
b3a1339
deb1a3c
d3adda3
b959286
a03fdc7
84ad89b
496dd1d
a62f21e
b043982
7c26fd4
e404119
61d5adc
62e3846
9dd9533
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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를 눌러서 삭제 가능, 앱을 재실행해도 검색어 저장 목록은 유지 |
This file was deleted.
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 | ||
) |
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) | ||
} | ||
|
||
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 | ||
} | ||
} |
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> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 결과값을 MutableList로 보낼 필요가 있나요? |
||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. RecyclerView가 변경감지를 최소화하도록 잘 작업하셨군요 👏 |
||
} | ||
} | ||
override fun getItemCount(): Int { return searchHistory.size } | ||
} |
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> |
This file was deleted.
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> |
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> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(지금 대응하지 않으셔도 됩니다)
db에 쿼리를 날리는건 에러가 발생할수 있는 대표적인 케이스중 하나입니다
어떤 에러가 발생할수 있는지와 케이스별로 어떻게 대응하면 좋을지 한번 고민 해보시겠어요?