SOPT 25๊ธฐ Appjam '์ผ๋ฆฌ๋ฒ๋'
ํ๋ก์ ํธ ๊ธฐ๊ฐ 2019.12 ~ ์งํ์ค
๊น์์ง ๊น์ฐฌ์ ์ต์์ ์์์ฐ
//๋ฆฌ์ฌ์ดํด๋ฌ๋ทฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
implementation 'androidx.recyclerview:recyclerview:1.1.0-alpha06'
//Retrofit ๋ผ์ด๋ธ๋ฌ๋ฆฌ : https://github.com/square/retrofit
implementation 'com.squareup.retrofit2:retrofit:2.6.2'
//Retrofit ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์๋ต์ผ๋ก ๊ฐ์ง ๊ฐ์ฒด๋ฅผ ๋ง๋ค๊ธฐ ์ํด
implementation 'com.squareup.retrofit2:retrofit-mock:2.6.2'
//๊ฐ์ฒด ์๋ฆฌ์ผ๋ผ์ด์ฆ๋ฅผ ์ํ Gson ๋ผ์ด๋ธ๋ฌ๋ฆฌ : https://github.com/google/gson
implementation 'com.google.code.gson:gson:2.8.6'
//Retrofit ์์ Gson ์ ์ฌ์ฉํ๊ธฐ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
implementation 'com.squareup.retrofit2:converter-gson:2.6.2'
//์ด๋ฏธ์ง ๋ก๋๋ฅผ ์ํด glide ๋ผ์ด๋ธ๋ฌ๋ฆฌ : https://github.com/bumptech/glide
implementation 'com.github.bumptech.glide:glide:4.10.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0'
//constraint Layout ์ฌ์ฉ์ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
//Lottie Library
implementation 'com.airbnb.android:lottie:3.2.2'
//google map
implementation 'com.google.android.gms:play-services-maps:17.0.0'
implementation 'com.google.android.gms:play-services-location:17.0.0'
// fcm - firebase๋ฅผ ์ด์ฉํด ์๋ฆผ ๊ตฌํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
implementation 'com.google.firebase:firebase-core:16.0.6' // ์ ๋๋ฆฌํฑ์ค(๊ธฐ๋ณธ)
implementation 'com.google.firebase:firebase-messaging:17.3.4' // ํด๋ผ์ฐ๋ ๋ฉ์์ง
data,feature,network,util
- calendar : ๋ฌ๋ ฅ
- db : sharedPreference
- place : ์ฃผ์
- route : ๊ฒฝ๋ก
- schedule : ์ผ์
- user : ํ์
- calendar : ๋ฌ๋ ฅ
- home : ํ ํ๋ฉด
- initial_join : ์ต์ด๊ฐ์
- intercepter : header ์ถ๊ฐ intercepter
- place : ์ฅ์
- search : ์ฅ์ ๊ฒ์
- select : ์์ฃผ ๊ฐ๋ ์ฅ์ ์ ํ
- route : ์ธ๋ก ๊ฒฝ๋ก
- schedule : ์ผ์
- user : ์ ์ (๋ก๊ทธ์ธ,ํ์๊ฐ์ )
-
Lottie ์ ๋๋ฉ์ด์ ์ ์ฉ
- activity_splash.xml
<com.airbnb.lottie.LottieAnimationView android:id="@+id/act_splash_av" android:layout_width="0dp" android:layout_height="0dp" android:scaleType="fitXY" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:lottie_autoPlay="true" app:lottie_fileName="splash.json"/>
-
TextWatcher ์ฌ์ฉํด์ ์์ธ์ฒ๋ฆฌ ๋ฐ ๋ฒํผํ์ฑํ. ex) ์ค๋ณตํ์ธ, ํน์ ๋ฌธ์ ์ ํ, ๊ธ์์ ์ ํ, ํ์ฑํ ๋นํ์ฑํ ๋ฒํผ์์ ๋ณ๊ฒฝ
- PlaceSearchActivity.kt
act_place_search_et_search.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(p0: Editable?) { } override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { } override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { //ํต์ getPlaceSearch() Log.d("testtest", "onTextChanged") } })
-
ํ์๊ฐ์
- SignupActivity
private fun passwordCheck() { act_signup_et_pw.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(p0: Editable?) { if ((p0!!.length < 6) || !(pwdPattern.matcher(act_signup_et_pw.text.toString()).matches())) { act_signup_tv_pw_ment.showOrInvisible(true) act_signup_cl_pw.setBackgroundResource(R.drawable.act_signup_round_rect_red) act_signup_et_pw.setTextColor( ContextCompat.getColor( this@SignupActivity, R.color.black ) ) act_signup_cl_join.setBackgroundResource(R.drawable.act_place_round_rect_gray_full) pwFlag = false } else { act_signup_tv_pw_ment.showOrInvisible(false) act_signup_cl_pw.setBackgroundResource(R.drawable.act_signup_round_rect_blue) act_signup_et_pw.setTextColor( ContextCompat.getColor( this@SignupActivity, R.color.black ) ) if(!act_signup_et_pw.text.toString().equals(act_signup_et_pw_check.text.toString())) { act_signup_tv_pw_check_ment.showOrInvisible(true) act_signup_cl_pw_check.setBackgroundResource(R.drawable.act_signup_round_rect_red) act_signup_cl_join.setBackgroundResource(R.drawable.act_place_round_rect_gray_full) pwCheckFlag = false } pwFlag = true } } override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { } override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { } }) }
-
ReCyclerView๋ก ์ฅ์ ๊ฒ์ ํต์ ์ ๋๋ฏธ๋ฐ์ดํฐ
-
Custom dialog๋ฅผ ์ง์ ๊ตฌํํ์ฌ ์ฌ์ฉ์๊ฐ ์ ํํ ์ฌํญ์ ๋ทฐ์ ๋ฐ์
-
ConstraintLayout์์ TextView, EditText ๊ตฌํ
-
kotlin extension์ ์ด์ฉํ์ฌ layout ํ์ผ์ ๊ฐ์ฒด์ ์ฐธ์กฐ ์ป์ด์ค๊ธฐ
-
kotlin์ ๋๋ค์์ ์ฌ์ฉํ click ์ด๋ฒคํธ ๊ตฌํ
-
ํ๋๋ผ๋ ์ ๋ ฅํ์ง ์์ ํญ๋ชฉ์ด ์์ ๊ฒฝ์ฐ Toast ๋ฉ์์ง ๋์ฐ๊ธฐ
-
์ถ๋ฐ์ง์์ ๋ชฉ์ ์ง๋ก ๊ฐ๋ ์ธ๋ก ๊ฒฝ๋ก๋ฅผ ๋ณด์ฌ์ฃผ๋ ๋ทฐ ์ ์. ๊ฒฝ์ ์ ๋ฅ์ฅ ํ์ํ๊ธฐ ์ํด ๋ฆฌ์ฌ์ดํด๋ฌ๋ทฐ ์์ ๋ฆฌ์ฌ์ดํด๋ฌ๋ทฐ ์ ์
-
์งํ์ฒ ๊ฒฝ๋ก(18๊ฐ) ์ ๋ฒ์ค ๊ฒฝ๋ก(10๊ฐ) ์ ๋ค์ํ ๊ฒฝ์ฐ์ ์๋ก ์ธํ internal constructor ์์ฑ
-
internal constructor ๋ก ์ธํ ์ฝ๋ ์ค์
-
์ธ๋ก ๊ฒฝ๋ก ๋ทฐ ์๋ฒ์์ retroifit ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํด ํต์ ์๋ฃ
-
๋๊ฐ์ data class ๋ฅผ ๋ฐ๋ ๋๊ฐ์ adapter,viewHolder ์์ฑ
-
when ์ ํตํ ๋๋ค์ ์ ์ฉ
when (holder.itemViewType) {
//์งํ์ฒ
1 -> {
holder.direction.text = String.format("%s ๋ฐฉ๋ฉด", routeList[position].way)
holder.startingText.text =
String.format("%s์ญ", routeList[position].startName)
holder.endText.text = String.format("%s์ญ", routeList[position].endName)
}
//๋ฒ์ค
2 -> {
holder.ridingNumber.text =
String.format("%s", routeList[position].lane.busNo)
holder.startingText.text =
String.format("%s", routeList[position].startName)
holder.endText.text = String.format("%s", routeList[position].endName)
holder.direction.text = "๋ฐฉํฅ์ ์ฃผ์ํ๊ณ ํ์นํ์ธ์"
}
}
- with ๋๋ค์ ์ฌ์ฉ
//๊ฒฝ๋ก๋ฐ์ดํฐ ๋ฃ๊ธฐ
fun setRouteItem(newRouteList: ArrayList<SubPath>) {
with(routeList) {
clear()
addAll(newRouteList)
}
notifyDataSetChanged()
}
<ํ์ผํ๋ ๊ตํต ์๋จ์ ๊ฐฏ์์ ์์ ์๊ฐ์ ๋ฐ๋ผ ๋์ ์ผ๋ก ๋ฌ๋ผ์ง๋ ๊ธฐ๋ฅ>
-
nine-patch๋ฅผ ์ด์ฉํด ๊ฒฝ๋ก ์ด๋ฏธ์ง์์ ๋๋ฆฌ๊ณ ์ถ์ ๋ถ๋ถ๋ง ์ ํํ์ฌ ์กฐ์ ๊ฐ๋ฅ
-
LinearLayout์ horizental๋ก ๊ฒฝ๋ก ๋ฐ๋ฅผ ๋ฐฐ์นํ๊ณ weight๋ก ๊ธธ์ด ์กฐ์
-
backgroundTint๋ก ๊ฐ ๊ฒฝ๋ก ๋ฐ์ ์๊น์ ํด๋น ํธ์ ์ ์๊น๋ก ๋ณ๊ฒฝ
-
๊ฐ๋ก ๊ฒฝ๋ก ๋ทฐ ์๋ฒ์์ retroifit ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํด ํต์ ์๋ฃ
- Item_list_place_search_route.xml
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp">
<RelativeLayout
android:id="@+id/act_schedule_route_rl_gray"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/img_gray_line"/>
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="18dp"
android:orientation="horizontal">
<RelativeLayout
android:id="@+id/act_schedule_route_rl_walk_1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
>
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</RelativeLayout>
.
.
.
<RelativeLayout
android:id="@+id/act_schedule_route_rl_walk_4"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</RelativeLayout>
</LinearLayout>
</RelativeLayout>
- ์ซ์ ์ฌ๋ผ๊ฐ๋ ์ ๋๋ฉ์ด์ kotlin extension ์ ์ด์ฉํ์ฌ ์์ฑ
private fun TextView.setAnimInt(value: Int) {
startAnimation(TextViewIntAnimation(this, to = value))
}
- ์๊ฐ์ด ์ค์ด๋๋ ์ ๋๋ฉ์ด์ kotlin extension ์ ์ด์ฉํ์ฌ ์์ฑ
private fun TextView.start(token: Int) { //ํ์ด๋จธ ์คํํธ
var a = this
if (token == 0) {
timerTask = timer(period = 10) {
// period = 10 0.01์ด , period = 1000 ๋ฉด 1์ด
time--
val min = (time / 6000) % 60 // 1๋ถ
runOnUiThread {
// Ui ๋ฅผ ๊ฐฑ์ ์ํด.
if (min < 10) { // ๋ถ
minmin = "$min"
} else {
minmin = "$min"
}
a.text = String.format("%s", minmin)
if (Integer.valueOf(minmin) <= 3) {
act_home_tv_minute_number.visibility = View.INVISIBLE
act_home_tv_before_minute.visibility = View.INVISIBLE
act_home_tv_soon.visibility = View.VISIBLE
}
}
}
}
}
-
์ปค์คํ ๋ฌ๋ ฅ์ ํตํ ์ผ์ ์กฐํ ๋ฐ ๋ฑ๋ก
-
์ค๋ ๋ ์ง์ ํ๋ ๋ง์ปค ๋ํดํธ
-
์ปค์คํ ๋ฌ๋ ฅ -> ๋ทฐํ์ด์ ์ ๋ฆฌ์ฌ์ดํด๋ฌ๋ทฐ ์ด์ฉ
-
google map api ์ ์ฉ
-
๊ฒฝ๋ ์๋ ์ขํ๋ฅผ ๋ฐ์ ๊ตฌ๊ธ๋งต์ 16F zoom ์ผ๋ก ํ๋ฉด ํ์
-
๊ฐ์ฅ ์ต๊ทผ์ ์๋ ์ผ์ ์ ๋ง์ถฐ ํ๋ฉด ์ ์ฉ
-
์ผ์ ์ ๋ํ ๋์ค๊ตํต ์๋ฆผ์ด ์์๋๋ฉด 1๋,2๋,3๋ ์ด๋์ค ์ ๋ฐ๋ผ ํ๋ฉด ๋ณํ
-
๋์ค๊ตํต ๋์ฐฉ์ด ์๋ฐ(3๋ถ ์ด๋ด)๊ฐ ๋๋ฉด ๊ณง ๋์ฐฉ ์ด๋ผ๋ ๋ฌธ๊ตฌ ํ์
-
๋ค์ ๋์ฐฉ ๋์ค๊ตํต ์ ๋ณด๊ฐ ์์ผ๋ฉด ๊ทธ ์ ๋ณด๋ฅผ ์ง์ฐ๊ณ ๋ฐ์ผ๋ก ๋ถ์ฌ์ผํ๋๋ฐ ์ฌ๊ธฐ์ ConstraintLayout์ ์๋ก ์ฐ๊ฒฐ๋๋ ์์ฑ์ ์ฌ์ฉํด์ ์ ํด๊ฒฐํจ.
var bottomParams = act_home_cl_middle_bar.layoutParams as? ConstraintLayout.LayoutParams
bottomParams?.topMargin = 90
act_home_cl_middle_bar.layoutParams = bottomParams
viewGone()
private fun viewGone() {
act_home_tv_bus_number.visibility = View.GONE
act_home_tv_bus_current_location.visibility = View.GONE
act_home_tv_next_bus.visibility = View.GONE
act_home_tv_next_bus_var.visibility = View.GONE
act_homme_iv_reboot.visibility = View.GONE
}
- datePicker์ timePicker๋ก ๋ ์ง์ ์๊ฐ ์ ํ
act_schedule_tv_date_click.setOnClickListener {
DatePickerDialog(this@ScheduleActivity, R.style.MyDatePickerDialogTheme,
DatePickerDialog.OnDateSetListener{ datePicker, year, monthOfYear, dayOfMonth ->
cal.set(Calendar.YEAR, year)
cal.set(Calendar.MONTH, monthOfYear)
cal.set(Calendar.DAY_OF_MONTH, dayOfMonth)
act_schedule_tv_date_click.text = SimpleDateFormat("yyyy.MM.dd").format(cal.time)
},cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)).show()
}
}
- ์ฅ์ ๊ฒ์์ ํตํด ๋ฐ์ ์ถ๋ฐ์ง์ ๋์ฐฉ์ง ์ขํ๋ก ๊ฐ๋ก๊ฒฝ๋ก ํ์