-
SOPT 26th APPJAM - Team placepic
-
ํ๋ก์ ํธ ๊ธฐ๊ฐ: 2020.06.28 ~ 2020.07.18
์ฐ๋ฆฌ๋ค๋ผ๋ฆฌ ๊ณต์ ํ๋ ์ต์ ์ฅ์, ํ๋ ์ด์คํฝ
ํ๋ ์ด์คํฝ์ ์ ๋ขฐ์๋ ์ปค๋ฎค๋ํฐ ๊ธฐ๋ฐ์ ์ฅ์ ์ ๋ณด ๊ณต์ ํ๋ซํผ์ ๋๋ค.
Service key Feature
-
Exploration - ์ฝ๊ณ , ๋น ๋ฅด๊ฒ ์ํ๋ ์ฅ์๋ฅผ ํ์
-
Discovery - ๊ฐ๋ณด๊ณ ์ถ์ ์ฅ์๋ฅผ ๋ฐ๊ฒฌํ ์ ์์ด์ผํจ
-
Interaction - ์ํธ์์ฉ, ์ํตํ ์ ์๋ ๊ณต๊ฐ
- ์ฐ๋ฆฌ๊ฐ ์ธ ์ ์๊ณ , ์ฐ๊ณ ์ถ์ ์๋น์ค๋ฅผ ๋ง๋ค์ด๋ผ ๊ฒ์ด๋ค.
- ์ฌ์ฉ์ ๊ฒฝํ์ ๋ํ ์ง์ฐฉ
- ์๊ธฐ์ฃผ๋์ฑ
- ๋ฆฐ ์คํํธ์ ์ ์
- PRIDEโจ
- git branch
์ด์ ๋ณ๋ก branch ๊ตฌ์ฑ
- git commit message rule
[Feat] ๊ธฐ๋ฅ ์ถ๊ฐ
[Fix] ๋ฒ๊ทธ ์์
[Refactor] ๋ฆฌํฉํ ๋ง
[Chore] ๊ฐ๋จํ ์์
[Delete] ๊ธฐ๋ฅ ์ญ์
[Docs] ๋ฌธ์
- ์ฅ์๋ฆฌ์คํธ
- ์งํ์ฒ ๊ฒ์
- ์ต์ ์ฅ์ ์
๋ก๋ View
- ์ด๋ฏธ์ง ์
๋ก๋ ๊ตฌํ
- ์ธ์ฑ๋ธ๋ผ์ฐ์ ๊ตฌํ
- ์ ์ฅ ํ์ด์ง
- custom ํ์ฅ ํจ์ ์ฌ์ฉ
- kotlin collection ํ์ฅ ํจ์ ์ฌ์ฉ
- ๋ก๊ทธ์ธ, ๋ก๊ทธ์ธ ํ์ด์ง View Layout
- ๋ด ๊ทธ๋ฃน, ์น์ธ๋๊ธฐ ํ์ด์ง
- ๊ทธ๋ฃน ์ ์ฒญ ํ์ด์ง, ๊ทธ๋ฃน ์ ์ฒญ ์๋ฃ
- ๊ด๋ฆฌ์ ํ์ด์ง
- ๋ํ
์ผ ํ์ด์ง
- kotlin collection ํ์ฅ ํจ์ ์ฌ์ฉ : Let, forEach
- ์ฑ๊ธํด SharedPreference ๊ตฌํ
- custom enqueue ์์ฑ
- ์ฅ์๊ฒ์ ํ์ด์ง
- ํ๊ทธ์ ํ ํ์ด์ง
- Chip ๊ตฌํ
- PlacePicService ๊ตฌํ
- ์์ ํ์ด์ง
- ๋ก๊ทธ์ธ, ๋ก๊ทธ์ธ ๋ฉ์ธ, ํ์๊ฐ์
ํ์ด์ง
- ์ต์ ์ฅ์ ์
๋ก๋ View Layout
- ์ด๋ฏธ์ง ์
๋ก๋ ์ด๊ธฐ ๊ตฌํ
- Bottom Navigation ์ปค์คํ
- ๋ก๊ทธ์์
- ๋ง์ดํ์ด์ง
- ์คํ๋์ฌ ํ๋ฉด
- ์ ๋ ํฌ๊ธฐ ์ง์
์๋ฒ์ ์ ๋ก๋๋ ์ฌ์ง์ ํฌ๊ธฐ๊ฐ ์ผ์ ํ์ง ์์ผ๋ฏ๋ก ์ฌ์ฉ์์๊ฒ ๋ณด์ฌ์ฃผ๋ ์ฌ์ง์ ์ ๋ ํฌ๊ธฐ๋ก ์ง์ ํ์์ต๋๋ค.
- activity_detail_view.xml
- activity_end_group_join.xml
- activity_join_group.xml
- item_group_list.xml
- item_photo.xml
- item_get_image_button.xml
- item_image_to_upload.xml
- item_place.xml
- item_subway_line.xml
- fragment_my_page.xml
- item_group_list.xml
- item_like_user_list.xml
- item_place.xml
๋ทฐ์์ ํน์ ๊ตฌ์ญ์ ์ค์ ํ๊ธฐ ์ํด height ์์ฑ ์ง์
- item_member.xml
- ๋ชจ๋ ๋ทฐ์ ์๋จ ๋ฐ
์๋จ๋ฐ์ ๋์ด๋ฅผ 56dp๋ก ์ผ์ ํ๊ฒ ํ๊ธฐ ์ํด ๊ฐ์ ์ค height์ ๊ฐ์ ์ฃผ์์ต๋๋ค.
- activity_upload_place.xml
- fragment_places.xml
- item_place.xml
- item_place_search.xml
- item_search_subway.xml
- activity_user_info.xml
- activity_web.xml
- fragment_admin_page.xml
- item_group_list.xml
- ๋์์ธ๋ line์ ๋ฃ๊ธฐ ์ํด height ์์ฑ ์ง์
bottom navigation์ ์ปค์คํ ํ์ฌ ๊ฐ์ด๋ฐ๋ฅผ ๋ฒํผ์ผ๋ก ๋ฐ๊พธ๊ธฐ ์ํด ์์๋ก width, height ๊ฐ ์ง์ ํ์์ต๋๋ค.
- activity_main.xml
- match_constraint ์ฌ์ฉ ๋ทฐ
- activity_detail_view.xml
- activity_keword_tag.xml
- activity_login.xml
- activity_place_search.xml
- activity_search_subway.xml
- activity_sign_up.xml
- activiy_sign_up_second.xml
- activity_upload_place.xml
- activivty_useful_tag.xml
- fragment_places.xml
- item_place.xml
- activity_upload_place.xml
- activity_useful_tag.xml
- acticity_user_info.xml
...
- layout ์ฌ์ฉ
๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ผ๋ก ์ธํด layout์์ ConstraintLayout์ ์ฌ์ฉํจ
- activity_detail_veiw.xml
- activity_search_subway.xml
- activity_upload_place.xml
- fragment_loading.xml
- fragment_place_items.xml
- fragment_places.xml
- item_get_image_button.xml
- item_image_to_upload.xml
- item_keyword_tag.xml
- item_place.xml
- item_place_filter.xml
- item_searched_subway.xml
- item_subway.xml
- item_subway_line.xml
์ด์ธ์๋ ๋ชจ๋ Constraintlayout์ ์ฌ์ฉํ์์ต๋๋ค.
kotlin collection์ ํ์ฅ ํจ์ ์ฌ์ฉ
-
map
-
filter
-
run
-
also
-
Let
-
forEach
-
first
-
sortedBy
-
getOrElse
custom ํ์ฅ ํจ์ ์ฌ์ฉ
- hasSingleImage()
์ด๋ฏธ์ง ํผ์ปค์์ ์ด๋ฏธ์ง๋ฅผ ๊ฐ์ ธ์ฌ ๋ 1๊ฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋์ ์ฌ๋ฌ๊ฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋ ์ ๊ทผํด์ผ ํ ํ๋๊ฐ ๋ค๋ฆ ๋๋ค.
๊ทธ๋ ๊ธฐ์ ํ์ฅ์ง๋ฆฌ ์ด๋ฏธ์ง๋ฅผ ๊ฐ์ง๊ณ ์๋ intent์ธ์ง, ์ฌ๋ฌ์ฅ์ ๊ฐ์ง๊ณ ์๋ intent์ธ์ง ํ๋จ์ ์ํด ์์ฑํ์ต๋๋ค.
๋ด์ฅ ํ์ฅ ํจ์ map์ ์ฌ์ฉํ๋ฏ๋ก์จ ๋ฐ์ดํฐ ๊ฐ๊ณต์ ๊ฐ๋ ์ฑ์ ๋ํ์ต๋๋ค.
<ImageUriExtractor.kt>
...
fun from(intent: Intent?): List<ImageUri> {
if (intent == null) return emptyList()
if (intent.hasSingleImage()) return getSingleImage(intent)
return getMultipleImages(intent)
}
private fun Intent.hasSingleImage(): Boolean {
return this.data != null
}
...
Name | Gradle |
---|---|
View | androidx.constraintlayout:constraintlayout:1.1.3 |
androidx.legacy:legacy-support-v4:1.0.0 | |
androidx.recyclerview:recyclerview:1.1.0 | |
group: 'de.hdodenhof', name: 'circleimageview', version: '3.1.0' | |
LiveData | androidx.lifecycle:lifecycle-livedata-ktx:2.2.0 |
Material Design | com.google.android.material:material:1.3.0-alpha01 |
glide | com.github.bumptech.glide:glide:4.11.0 |
com.github.bumptech.glide:compiler:4.10.0 | |
Circle Imageview Custom | de.hdodenhof:circleimageview:3.1.0 |
com.android.support:design:29.0.0 | |
Circle Indicator | me.relex:circleindicator:1.2.2 |
Toggle Button Custom | me.rishabhkhanna:CustomToggle:1.0.0 |
Gson | com.google.code.gson:gson:2.8.6 |
retrofit | com.squareup.retrofit2:retrofit:2.7.1 |
com.squareup.retrofit2:converter-gson:2.7.1 | |
com.squareup.retrofit2:retrofit-mock:2.6.2 | |
com.squareup.okhttp3:logging-interceptor:4.2.1 |
- Image ์ถ์ถ ์ ํธ ํด๋์ค
Intent๋ฅผ ๋ฃ๊ธฐ๋งํ๋ฉด Image VO์ List์ ๋ฐํํ๋ ์ ํธ์ฑ ํด๋์ค๋ฅผ ์์ฑํ์ต๋๋ค.
์ ์ ํฉํ ๋ฆฌ๋ฉ์๋ ํจํด์ ์ด์ฉํ์ฌ ์์กด์ฑ๊ณผ ์ ๊ทผ์ฑ์ ๋ฎ์ถ์์ต๋๋ค.
<ImageUriExtractor.kt>
package place.pic.ui.upload
import android.content.Intent
/**
* Created By Hyeok
*/
class ImageUriExtractor {
companion object {
fun from(intent: Intent?): List<ImageUri> {
if (intent == null) return emptyList()
if (intent.hasSingleImage()) return getSingleImage(intent)
return getMultipleImages(intent)
}
private fun Intent.hasSingleImage(): Boolean {
return this.data != null
}
private fun getSingleImage(intent: Intent): List<ImageUri> {
val imageUri = intent.data
?: throw IllegalArgumentException("getSingleImage should not be called when multiple images loaded")
return listOf(ImageUri(imageUri))
}
private fun getMultipleImages(intent: Intent): List<ImageUri> {
val clipData = intent.clipData
?: throw IllegalArgumentException("getMultipleImages should not be called when single image loaded")
return IntRange(0, clipData.itemCount - 1)
.map { clipData.getItemAt(it).uri }
.map { ImageUri(it) }
}
}
}
- Chip
์ฌ๋ฌ ๊ณณ์์ Chip์ ์์ฑํ์ฌ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ChipFactory๋ผ๋ ์ ํธ์ฑ ํด๋์ค๋ฅผ ์์ฑํด์
์นฉ์ด ํ์ํ ์๊ฐ์ ์ธ์ ๋ ์ง ํจ์ ํธ์ถ์ ํตํด Chip์ ์์ฑํ ์ ์๋๋ก ๊ตฌํํ์์ต๋๋ค.
<ChipFactory>
package place.pic.ui.tag
import android.view.LayoutInflater
import com.google.android.material.chip.Chip
import place.pic.R
/**
* Created By kimdahyee
*/
class ChipFactory {
//๊ฐ์ฒด๋ฅผ ์์ฑํ์ง์๊ณ ํจ์ ํธ์ถ๋ง์ผ๋ก๋ ์นฉ์ ์์ฑํ ์ ์๋๋ก ์ ํธ์ฑ method ๋ง๋ค์ด๋ณด๊ธฐ
//factory pattern
//factory.keyword.chip
//newInstance()
companion object {
fun newInstance(layoutInflater: LayoutInflater): Chip {
return layoutInflater.inflate(R.layout.chip_tag, null, false) as Chip
}
fun createSmallChip(layoutInflater: LayoutInflater): Chip {
return layoutInflater.inflate(R.layout.chip_tag_small, null, false) as Chip
}
fun createDetailChip(layoutInflater: LayoutInflater): Chip {
return layoutInflater.inflate(R.layout.chip_detail_tag,null,false) as Chip
}
}
}
์์ ์ฌ์ฉ
- Singleton Shared Preference
Shared Preference๋ฅผ ์ฑ๊ธํด์ผ๋ก ๋ฉํฐ์ฐ๋ ๋ ํ๊ฒฝ์ ๊ณ ๋ คํ์ฌ ํ๋ฒ๋ง ๊ฐ์ฒด๊ฐ ์์ฑ๋๋๋ก ํด๋์ค๋ฅผ ์์ฑํ์์ต๋๋ค.
<PlacepicAuthRepository.kt>
package place.pic.data
import android.app.Activity
import android.content.Context
/**
* Created By Jinsu
*/
class PlacepicAuthRepository private constructor(context: Context) {
private val sharedPreferences = context.getSharedPreferences(context.packageName,Activity.MODE_PRIVATE)
private var editor = sharedPreferences.edit()
val userToken : String?
get() = sharedPreferences.getString(PLACEPIC_AUTH+"token","")
val groupId : Int?
get() = sharedPreferences.getInt(PLACEPIC_AUTH+"groupId",0)
fun saveUserToken(token:String){
editor.putString(PLACEPIC_AUTH+"token",token).apply()
}
fun saveGroupId(groupId: Int) {
editor.putInt(PLACEPIC_AUTH+"groupId",groupId).apply()
}
fun removeUserToken(){
editor.remove(PLACEPIC_AUTH+"token")
}
fun removeGroupId(){
editor.remove(PLACEPIC_AUTH+"groupId")
}
companion object {
private const val PLACEPIC_AUTH = "placepic_auth_"
@Volatile
private var instance: PlacepicAuthRepository? = null
@JvmStatic
fun getInstance(context:Context) = instance ?: synchronized(this) {
instance ?: PlacepicAuthRepository(context).apply {
instance = this
}
}
}
}