-
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주차 과제(2단계) #55
base: sumintnals
Are you sure you want to change the base?
Changes from 29 commits
1ca1295
da15dde
3ca3473
dcb732e
c2149fd
0fe2c16
6d8ba38
fc3fd89
89f53cf
828ecd4
86fdd51
8072b2d
89855fe
3980334
917463a
5013e50
5740bec
bc9c04c
67a59dd
c8faa90
b2da1d3
1871f63
db3e5c7
76f177a
595cef6
5fc7d6e
0d8ab18
b7d2069
3fdaa81
2722e8d
8642b58
674d762
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 | ||
# android-map-keyword (2단계) | ||
|
||
### 기능 사항 | ||
- 검색어 입력 레이아웃 구현한다.0 | ||
- 검색에 사용될 데이터를 로컬 데이터베이스에 생성한다.0 | ||
- 검색어를 입력하면 검색 결과 목록이 표시된다.0 | ||
- 검색 결과 목록은 세로 스크롤이 된다. RecyclerView를 사용한다.0 | ||
- 입력한 검색어는 X를 눌러서 삭제할 수 있다. 0 | ||
- 검색 결과 목록에서 하나의 항목을 선택할 수 있다.0 | ||
- 선택된 항목은 검색어 저장 목록에 추가된다. 0 | ||
- 저장된 검색어 목록은 가로 스크롤이 된다. 0 | ||
- 저장된 검색어는 X를 눌러서 삭제할 수 있다. | ||
- 저장된 검색어는 앱을 재실행하여도 유지된다.0 | ||
|
||
### 프로그래밍 요구 사항 | ||
- 가능한 MVVM 아키텍처 패턴을 적용하도록 한다. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package campus.tech.kakao.map | ||
|
||
object Constants { | ||
const val SEARCH_HISTORY_KEY = "searchHistory" | ||
const val PREFERENCE_NAME = "preference_name" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package campus.tech.kakao.map | ||
|
||
import android.content.Context | ||
import android.database.sqlite.SQLiteDatabase | ||
import android.database.sqlite.SQLiteOpenHelper | ||
|
||
class DBHelper (context: Context): SQLiteOpenHelper(context, "place.db", null, 1) { | ||
override fun onCreate(db: SQLiteDatabase?) { | ||
db?.execSQL( | ||
"CREATE TABLE IF NOT EXISTS ${PlaceContract.TABLE_NAME} ("+ | ||
"${PlaceContract.TABLE_COLUMN_NAME} VARCHAR(30) NOT NULL,"+ | ||
"${PlaceContract.TABLE_COLUMN_ADDRESS} VARCHAR(50) NOT NULL,"+ | ||
"${PlaceContract.TABLE_COLUMN_CATEGORY} VARCHAR(30) NOT NULL"+ | ||
");" | ||
) | ||
insertDummyData(db) | ||
} | ||
|
||
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) { | ||
db?.execSQL("DROP TABLE IF EXISTS ${PlaceContract.TABLE_NAME}") | ||
onCreate(db) | ||
} | ||
|
||
fun insert(db: SQLiteDatabase, place: Place) { | ||
val sql = " INSERT INTO " + | ||
"${PlaceContract.TABLE_NAME}("+ | ||
"${PlaceContract.TABLE_COLUMN_NAME}, ${PlaceContract.TABLE_COLUMN_ADDRESS}, ${PlaceContract.TABLE_COLUMN_CATEGORY})"+ | ||
" VALUES(${place.name}, ${place.address}, ${place.category});" | ||
|
||
db.execSQL(sql) | ||
} | ||
|
||
fun select(db: SQLiteDatabase, name:String, address: String, category: String) : String? { | ||
return null | ||
} | ||
|
||
private fun insertDummyData(db: SQLiteDatabase?) { | ||
db?.let { | ||
val categories = listOf("카페", "약국", "식당") | ||
val baseAddress = "서울 성동구 성수동" | ||
|
||
categories.forEach { category -> | ||
for (i in 1..15) { | ||
val name = "$category $i" | ||
val address = "$baseAddress $i" | ||
val sql = "INSERT INTO ${PlaceContract.TABLE_NAME} (" + | ||
"${PlaceContract.TABLE_COLUMN_NAME}, ${PlaceContract.TABLE_COLUMN_ADDRESS}, ${PlaceContract.TABLE_COLUMN_CATEGORY}) " + | ||
"VALUES ('$name', '$address', '$category');" | ||
it.execSQL(sql) | ||
} | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
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 | ||
import campus.tech.kakao.map.databinding.SearchHistoryItemBinding | ||
|
||
class HistoryAdapter(var items: List<SearchHistory>, val inflater: LayoutInflater) : RecyclerView.Adapter<HistoryAdapter.HistoryViewHolder>() { | ||
|
||
interface OnItemClickListener { | ||
fun onItemClick(position: Int) | ||
fun onXMarkClick(position: Int) | ||
} | ||
|
||
var itemClickListener: OnItemClickListener? = null | ||
sumintnals marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HistoryViewHolder { | ||
val binding = SearchHistoryItemBinding.inflate(inflater, parent, false) | ||
return HistoryViewHolder(binding) | ||
} | ||
|
||
override fun onBindViewHolder(holder: HistoryViewHolder, position: Int) { | ||
holder.bind(items[position]) | ||
} | ||
|
||
override fun getItemCount(): Int { | ||
return items.size | ||
} | ||
|
||
fun setData(searchHistory: List<SearchHistory>) { | ||
items = searchHistory | ||
notifyDataSetChanged() | ||
} | ||
|
||
inner class HistoryViewHolder(private val binding: SearchHistoryItemBinding) : RecyclerView.ViewHolder(binding.root) { | ||
fun bind(searchHistory: SearchHistory) { | ||
binding.history.text = searchHistory.searchHistory | ||
} | ||
|
||
init { | ||
sumintnals marked this conversation as resolved.
Show resolved
Hide resolved
|
||
itemView.setOnClickListener { | ||
itemClickListener?.onItemClick(absoluteAdapterPosition) | ||
} | ||
|
||
binding.xmark.setOnClickListener { | ||
itemClickListener?.onXMarkClick(absoluteAdapterPosition) | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,110 @@ | ||
package campus.tech.kakao.map | ||
|
||
import android.os.Bundle | ||
import android.text.Editable | ||
import android.text.TextWatcher | ||
import android.view.LayoutInflater | ||
import android.view.View | ||
import androidx.activity.viewModels | ||
import androidx.appcompat.app.AppCompatActivity | ||
import androidx.lifecycle.Observer | ||
import androidx.recyclerview.widget.LinearLayoutManager | ||
import campus.tech.kakao.map.databinding.ActivityMainBinding | ||
|
||
class MainActivity : AppCompatActivity() { | ||
private val viewModel: MainViewModel by viewModels { | ||
ViewModelFactory(applicationContext) | ||
} | ||
|
||
private lateinit var placeAdapter: PlaceAdapter | ||
private lateinit var historyAdapter: HistoryAdapter | ||
sumintnals marked this conversation as resolved.
Show resolved
Hide resolved
|
||
private var placeList: List<Place> = emptyList() | ||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
sumintnals marked this conversation as resolved.
Show resolved
Hide resolved
|
||
super.onCreate(savedInstanceState) | ||
setContentView(R.layout.activity_main) | ||
|
||
val mainBinding = ActivityMainBinding.inflate(layoutInflater) | ||
setContentView(mainBinding.root) | ||
mainBinding.placeResult.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) | ||
|
||
if (placeList.isNullOrEmpty()) { | ||
mainBinding.emptyMainText.visibility = View.VISIBLE | ||
} else { | ||
mainBinding.emptyMainText.visibility = View.GONE | ||
} | ||
|
||
// 검색어 기록 토대로 UI 업데이트 | ||
viewModel.searchHistoryList.observe(this@MainActivity, Observer { | ||
historyAdapter.setData(it) | ||
}) | ||
viewModel.getSearchHistoryList() | ||
|
||
// 검색 필터링 | ||
val searchEditText = mainBinding.search | ||
|
||
// 검색 결과 토대로 UI 업데이트 | ||
viewModel.placeList.observe(this@MainActivity, Observer { | ||
(mainBinding.placeResult.adapter as? PlaceAdapter)?.setData(it) | ||
if (it.isNullOrEmpty()) { | ||
mainBinding.emptyMainText.visibility = View.VISIBLE | ||
} else { | ||
mainBinding.emptyMainText.visibility = View.GONE | ||
} | ||
}) | ||
|
||
placeAdapter = PlaceAdapter(placeList, LayoutInflater.from(this@MainActivity)) | ||
mainBinding.placeResult.apply { | ||
layoutManager = LinearLayoutManager(this@MainActivity, LinearLayoutManager.VERTICAL, false) | ||
adapter = placeAdapter | ||
} | ||
|
||
searchEditText.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) {} | ||
|
||
override fun afterTextChanged(s: Editable?) { | ||
val searchText = searchEditText.text.toString() | ||
viewModel.getSearchResult(searchText) | ||
} | ||
} | ||
) | ||
|
||
// X 누르면 초기화 | ||
mainBinding.xmark.setOnClickListener { | ||
searchEditText.setText("") | ||
} | ||
|
||
historyAdapter = HistoryAdapter( | ||
viewModel.searchHistoryList.value ?: emptyList() | ||
, LayoutInflater.from(this@MainActivity)) | ||
mainBinding.searchHistory.apply { | ||
layoutManager = LinearLayoutManager(this@MainActivity, LinearLayoutManager.HORIZONTAL, false) | ||
adapter = historyAdapter | ||
} | ||
|
||
// PlaceAdapter OnclickListener | ||
placeAdapter.itemClickListener = object : PlaceAdapter.OnItemClickListener { | ||
override fun onItemClick(position: Int) { | ||
val item = placeAdapter.getItem(position) | ||
val searchHistory = SearchHistory(item.name) | ||
viewModel.saveSearchHistory(searchHistory) | ||
} | ||
} | ||
|
||
// HistoryAdapter OnclickListener | ||
historyAdapter.itemClickListener = object : HistoryAdapter.OnItemClickListener { | ||
override fun onItemClick(position: Int) { | ||
val item = viewModel.searchHistoryList.value?.get(position) | ||
if (item != null) { | ||
searchEditText.setText(item.searchHistory) | ||
} | ||
} | ||
|
||
override fun onXMarkClick(position: Int) { | ||
viewModel.deleteSearchHistory(position) | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package campus.tech.kakao.map | ||
|
||
import android.util.Log | ||
import android.widget.EditText | ||
import androidx.lifecycle.LiveData | ||
import androidx.lifecycle.MutableLiveData | ||
import androidx.lifecycle.ViewModel | ||
|
||
class MainViewModel(private val placeRepository: PlaceRepository) : ViewModel() { | ||
private val searchRepository: SearchRepository = SearchRepository() | ||
|
||
private var _placeList = MutableLiveData<List<Place>>() | ||
private val _searchHistoryList = MutableLiveData<List<SearchHistory>>() | ||
|
||
val searchHistoryList: LiveData<List<SearchHistory>> | ||
get() = _searchHistoryList | ||
init { | ||
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. nit; 위의 코멘트를 확인해주세요. + 코드 포맷팅도 함께 적용해주세요. IDE에서 제공하는 기본 기능을 활용하시면 될 것 같습니다. 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. 넵! 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. 초기화 블록을 위로 올리고, Code->Reformat Code를 하면 될까요?? |
||
_searchHistoryList.value = searchRepository.getSearchHistory() | ||
} | ||
val placeList: LiveData<List<Place>> | ||
get() = _placeList | ||
|
||
fun insertPlace(place: Place) { | ||
placeRepository.insertPlace(place) | ||
} | ||
|
||
fun getPlace(): List<Place>? { | ||
return placeRepository.getPlace() | ||
} | ||
|
||
override fun onCleared() { | ||
super.onCleared() | ||
placeRepository.dbClose() | ||
} | ||
|
||
fun getSearchResult(searchText: String) { | ||
if (searchText.isEmpty()) { | ||
_placeList.postValue(emptyList()) | ||
} else { | ||
val results = placeRepository.getSearchResults(searchText) | ||
_placeList.postValue(results) | ||
} | ||
} | ||
|
||
fun saveSearchHistory(searchHistory: SearchHistory) { | ||
searchRepository.saveSearchHistory(searchHistory) | ||
_searchHistoryList.value = searchRepository.getSearchHistory() | ||
} | ||
|
||
fun deleteSearchHistory(position: Int) { | ||
searchRepository.deleteSearchHistory(position) | ||
_searchHistoryList.value = searchRepository.getSearchHistory() | ||
} | ||
|
||
fun getSearchHistoryList() { | ||
_searchHistoryList.value = searchRepository.getSearchHistory() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package campus.tech.kakao.map | ||
|
||
import android.app.Application | ||
import android.content.SharedPreferences | ||
|
||
class MyApplication: Application() { | ||
companion object { | ||
lateinit var prefs: PreferenceManager | ||
} | ||
|
||
override fun onCreate() { | ||
prefs = PreferenceManager(applicationContext) | ||
super.onCreate() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package campus.tech.kakao.map | ||
|
||
import androidx.annotation.DrawableRes | ||
|
||
data class Place ( | ||
val name: String, | ||
val address: String, | ||
val category: String, | ||
) |
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.
코드가 아직 구현되지 않은 것 같은데 맞을까요?
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.
앗 네!! 현재 PlaceRepository의 getSearchResults()함수를 사용해서 검색 결과를 가져오고 있습니다! 이 함수의 위치를 옮겨야 할까요?
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.
그리고 Repository를 삭제하고 뷰모델에 기존 Repository에 있던 함수를 옮기면 될까요??