-
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 #61
base: ddangcong80
Are you sure you want to change the base?
Changes from 17 commits
2d0571f
bc375ec
15f7588
f6ef8e9
c141ea0
9642da2
5bb633b
f318765
458f985
0741690
e74f538
f47eaf2
489b13f
537b649
9d4a8e2
8f9c13d
7782ef9
5974469
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,17 @@ | ||
# android-map-keyword | ||
|
||
### Week2-Step1 로컬 데이터 | ||
- 검색어 입력 및 검색 결과를 표시할 기본 레이아웃을 구현한다. | ||
- 검색에 사용될 데이터를 로컬 데이터베이스에 생성한다. | ||
- 검색 데이터의 저장은 SQLite를 사용한다. | ||
- 가능한 MVVM 아키텍처 패턴을 적용하도록 한다. | ||
|
||
### Week2-Step2 검색 | ||
- 검색어를 입력하면 검색 결과 목록이 표시된다. | ||
- 검색 결과 목록은 세로 스크롤이 된다. | ||
- 입력한 검색어는 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.model | ||
|
||
data class Place( | ||
val name: String, | ||
val address: String, | ||
val category: String | ||
) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package campus.tech.kakao.map.model | ||
|
||
import android.provider.BaseColumns | ||
|
||
object PlaceContract { | ||
object PlaceEntry : BaseColumns { | ||
const val TABLE_NAME = "place" | ||
const val COLUMN_PLACE_NAME = "place" | ||
const val COLUMN_PLACE_ADDRESS = "address" | ||
const val COLUMN_PLACE_CATEGORY = "category" | ||
const val CREATE_QUERY = "CREATE TABLE IF NOT EXISTS $TABLE_NAME (" + | ||
"${BaseColumns._ID} INTEGER PRIMARY KEY," + | ||
"$COLUMN_PLACE_NAME varchar(30)," + | ||
"$COLUMN_PLACE_ADDRESS varchar(255)," + | ||
"$COLUMN_PLACE_CATEGORY varchar(30))" | ||
const val DROP_QUERY = "DROP TABLE IF EXISTS $TABLE_NAME" | ||
} | ||
|
||
object SavePlaceEntry : BaseColumns { | ||
const val TABLE_NAME = "savePlace" | ||
const val COLUMN_PLACE_NAME = "savePlaceName" | ||
private const val COLUMN_TIMESTAMP = "timestamp" | ||
const val CREATE_QUERY = "CREATE TABLE IF NOT EXISTS $TABLE_NAME (" + | ||
"${BaseColumns._ID} INTEGER PRIMARY KEY," + | ||
"$COLUMN_PLACE_NAME varchar(30)," + | ||
"$COLUMN_TIMESTAMP DATETIME DEFAULT CURRENT_TIMESTAMP)" | ||
const val DROP_QUERY = "DROP TABLE IF EXISTS $TABLE_NAME" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package campus.tech.kakao.map.model | ||
|
||
import android.content.Context | ||
import android.database.sqlite.SQLiteDatabase | ||
import android.database.sqlite.SQLiteOpenHelper | ||
|
||
class PlaceDBHelper(context: Context) : SQLiteOpenHelper(context, "placedb", null, 1) { | ||
override fun onCreate(db: SQLiteDatabase?) { | ||
db?.execSQL(PlaceContract.PlaceEntry.CREATE_QUERY) | ||
db?.execSQL(PlaceContract.SavePlaceEntry.CREATE_QUERY) | ||
} | ||
|
||
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) { | ||
db?.execSQL(PlaceContract.PlaceEntry.DROP_QUERY) | ||
db?.execSQL(PlaceContract.SavePlaceEntry.DROP_QUERY) | ||
onCreate(db) | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package campus.tech.kakao.map.model | ||
|
||
data class SavePlace ( | ||
val savePlace: String, | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
package campus.tech.kakao.map.repository | ||
|
||
import android.content.ContentValues | ||
import android.content.Context | ||
import android.database.Cursor | ||
import android.database.sqlite.SQLiteDatabase | ||
import android.util.Log | ||
import campus.tech.kakao.map.model.Place | ||
import campus.tech.kakao.map.model.PlaceContract | ||
import campus.tech.kakao.map.model.PlaceDBHelper | ||
import campus.tech.kakao.map.model.SavePlace | ||
|
||
class SearchRepository(context: Context) { | ||
private val dbHelper = PlaceDBHelper(context) | ||
|
||
fun isDataExists(category: String): Boolean { | ||
val db: SQLiteDatabase = dbHelper.readableDatabase | ||
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. DB관련 로직 + 쿼리는 Helper에 두고 해당 함수를 불러서 사용하는 식으로 하면 더 좋을 것 같습니다 |
||
val cursor: Cursor = db.query( | ||
PlaceContract.PlaceEntry.TABLE_NAME, | ||
arrayOf(PlaceContract.PlaceEntry.COLUMN_PLACE_CATEGORY), | ||
"${PlaceContract.PlaceEntry.COLUMN_PLACE_CATEGORY} = ?", | ||
arrayOf(category), | ||
null, | ||
null, | ||
null | ||
) | ||
val exists = cursor.count > 0 | ||
cursor.close() | ||
db.close() | ||
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. 여기서는 close를 잘 해주셨는데요 다른곳에서도 꼭 해주시기 바랍니다 |
||
return exists | ||
} | ||
|
||
fun insertPlaceDummyData(name: String, address: String, category: String) { | ||
if (isDataExists(category)) { | ||
Log.d("ddangcong80", "Category $category already exists.") | ||
return | ||
} | ||
|
||
val db: SQLiteDatabase = dbHelper.writableDatabase | ||
val values = ContentValues() | ||
for (i in 1..15) { | ||
values.put(PlaceContract.PlaceEntry.COLUMN_PLACE_NAME, name + i) | ||
values.put(PlaceContract.PlaceEntry.COLUMN_PLACE_ADDRESS, address + i) | ||
values.put(PlaceContract.PlaceEntry.COLUMN_PLACE_CATEGORY, category) | ||
db.insert(PlaceContract.PlaceEntry.TABLE_NAME, null, values) | ||
} | ||
} | ||
|
||
fun getSearchPlaces(placeCategory: String): MutableList<Place> { | ||
val db: SQLiteDatabase = dbHelper.readableDatabase | ||
val places = mutableListOf<Place>() | ||
var cursor: Cursor? = null | ||
try { | ||
val selection = "${PlaceContract.PlaceEntry.COLUMN_PLACE_CATEGORY} LIKE ?" | ||
val selectionArgs = arrayOf("$placeCategory%") | ||
|
||
cursor = db.query( | ||
PlaceContract.PlaceEntry.TABLE_NAME, | ||
null, | ||
selection, | ||
selectionArgs, | ||
null, | ||
null, | ||
null | ||
) | ||
|
||
if (cursor != null) { | ||
while (cursor.moveToNext()) { | ||
val name = | ||
cursor.getString(cursor.getColumnIndexOrThrow(PlaceContract.PlaceEntry.COLUMN_PLACE_NAME)) | ||
val address = | ||
cursor.getString(cursor.getColumnIndexOrThrow(PlaceContract.PlaceEntry.COLUMN_PLACE_ADDRESS)) | ||
val category = | ||
cursor.getString(cursor.getColumnIndexOrThrow(PlaceContract.PlaceEntry.COLUMN_PLACE_CATEGORY)) | ||
|
||
places.add(Place(name, address, category)) | ||
} | ||
} | ||
} catch (e: Exception) { | ||
Log.e("ddangcong80", "Error", e) | ||
} finally { | ||
cursor?.close() | ||
db.close() | ||
} | ||
|
||
return places | ||
} | ||
|
||
|
||
fun savePlaces(placeName: String) { | ||
val db: SQLiteDatabase = dbHelper.writableDatabase | ||
val values = ContentValues() | ||
values.put(PlaceContract.SavePlaceEntry.COLUMN_PLACE_NAME, placeName) | ||
|
||
val cursor = db.query( | ||
PlaceContract.SavePlaceEntry.TABLE_NAME, | ||
arrayOf(PlaceContract.SavePlaceEntry.COLUMN_PLACE_NAME), | ||
"${PlaceContract.SavePlaceEntry.COLUMN_PLACE_NAME} = ?", | ||
arrayOf(placeName), | ||
null, | ||
null, | ||
null | ||
) | ||
|
||
if (cursor.moveToFirst()) { | ||
db.delete( | ||
PlaceContract.SavePlaceEntry.TABLE_NAME, | ||
"${PlaceContract.SavePlaceEntry.COLUMN_PLACE_NAME} = ?", | ||
arrayOf(placeName) | ||
) | ||
} | ||
cursor.close() | ||
|
||
db.insert(PlaceContract.SavePlaceEntry.TABLE_NAME, null, values) | ||
} | ||
|
||
fun showSavePlace(): MutableList<SavePlace> { | ||
val db: SQLiteDatabase = dbHelper.readableDatabase | ||
val savePlaces = mutableListOf<SavePlace>() | ||
var cursor: Cursor? = null | ||
try { | ||
cursor = db.query( | ||
PlaceContract.SavePlaceEntry.TABLE_NAME, | ||
null, | ||
null, | ||
null, | ||
null, | ||
null, | ||
null | ||
) | ||
|
||
if (cursor != null) { | ||
while (cursor.moveToNext()) { | ||
val name = | ||
cursor.getString(cursor.getColumnIndexOrThrow(PlaceContract.SavePlaceEntry.COLUMN_PLACE_NAME)) | ||
|
||
savePlaces.add(SavePlace(name)) | ||
} | ||
} | ||
} catch (e: Exception) { | ||
Log.e("ddangcong80", "Error", e) | ||
} finally { | ||
cursor?.close() | ||
db.close() | ||
} | ||
|
||
return savePlaces | ||
} | ||
|
||
fun deleteSavedPlace(savedPlaceName: String) { | ||
val db: SQLiteDatabase = dbHelper.writableDatabase | ||
|
||
val cursor = db.query( | ||
PlaceContract.SavePlaceEntry.TABLE_NAME, | ||
arrayOf(PlaceContract.SavePlaceEntry.COLUMN_PLACE_NAME), | ||
"${PlaceContract.SavePlaceEntry.COLUMN_PLACE_NAME} = ?", | ||
arrayOf(savedPlaceName), | ||
null, | ||
null, | ||
null | ||
) | ||
|
||
if (cursor.moveToFirst()) { | ||
db.delete( | ||
PlaceContract.SavePlaceEntry.TABLE_NAME, | ||
"${PlaceContract.SavePlaceEntry.COLUMN_PLACE_NAME} = ?", | ||
arrayOf(savedPlaceName) | ||
) | ||
} | ||
cursor.close() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package campus.tech.kakao.map.view | ||
|
||
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 | ||
import campus.tech.kakao.map.R | ||
import campus.tech.kakao.map.model.SavePlace | ||
|
||
class SavePlaceAdapter( | ||
private val savePlaces: List<SavePlace>, | ||
private val onItemClickListener: (SavePlace) -> Unit | ||
) : RecyclerView.Adapter<SavePlaceAdapter.SavePlaceViewHolder>() { | ||
class SavePlaceViewHolder( | ||
itemView: View, | ||
private val onItemClickListener: (SavePlace) -> Unit | ||
) : RecyclerView.ViewHolder(itemView) { | ||
private val savePlaceTextView: TextView = itemView.findViewById(R.id.savePlace) | ||
private val savePlaceDeleteBtn: ImageView = itemView.findViewById(R.id.saveCancelBtn) | ||
|
||
fun bind(savePlace: SavePlace) { | ||
savePlaceTextView.text = savePlace.savePlace | ||
savePlaceDeleteBtn.setOnClickListener { | ||
onItemClickListener(savePlace) | ||
} | ||
} | ||
} | ||
|
||
override fun onCreateViewHolder( | ||
parent: ViewGroup, | ||
viewType: Int | ||
): SavePlaceViewHolder { | ||
val view = | ||
LayoutInflater.from(parent.context).inflate(R.layout.saveplace_item, parent, false) | ||
return SavePlaceViewHolder(view, onItemClickListener) | ||
} | ||
|
||
override fun onBindViewHolder(holder: SavePlaceViewHolder, position: Int) { | ||
val savePlace = savePlaces[position] | ||
holder.bind(savePlace) | ||
} | ||
|
||
override fun getItemCount(): Int { | ||
return savePlaces.size | ||
} | ||
} |
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.
이 함수는 내부에서만 사용한는 것으로 보이는데요 private을 붙여주면 좋습니다.
나중에 협업할 때 해당 함수가 외부에서 사용될 수 있다고 판단되어 무분별한 사용이 일어날 수 있습니다.