Skip to content

๐Ÿฅ 2020 ์†ํŠธ APP-JAM : "์–ผ๋ฆฌ๋ฒ„๋””-๋‚˜๋งŒ์˜ ๋ฐฐ์ฐจ์•Œ๋ฆผ"

Notifications You must be signed in to change notification settings

devAon/EARLY-BUDDY-ANDROID

ย 
ย 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿฅ we are EARLY-BUDDY-ANDROID

img

์–ผ๋ฆฌ๋ฒ„๋”” - ์•ฝ์†์‹œ๊ฐ„์„ ์œ„ํ•œ ๋‚˜๋งŒ์˜ ๋Œ€์ค‘๊ตํ†ต ๋ฐฐ์ฐจ ์•Œ๋ฆฌ๋ฏธ

SOPT 25๊ธฐ Appjam '์–ผ๋ฆฌ๋ฒ„๋””'

ํ”„๋กœ์ ํŠธ ๊ธฐ๊ฐ„ 2019.12 ~ ์ง„ํ–‰์ค‘

๊น€์˜ˆ์ง„ ๊น€์ฐฌ์˜ ์ตœ์˜ˆ์› ์–‘์‹œ์—ฐ

Workflow

img

์ ์šฉ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

//๋ฆฌ์‚ฌ์ดํด๋Ÿฌ๋ทฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
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

1. data

  • calendar : ๋‹ฌ๋ ฅ
  • db : sharedPreference
  • place : ์ฃผ์†Œ
  • route : ๊ฒฝ๋กœ
  • schedule : ์ผ์ •
  • user : ํšŒ์›

2. feature

  • calendar : ๋‹ฌ๋ ฅ
  • home : ํ™ˆ ํ™”๋ฉด
  • initial_join : ์ตœ์ดˆ๊ฐ€์ž…
  • intercepter : header ์ถ”๊ฐ€ intercepter
  • place : ์žฅ์†Œ
    • search : ์žฅ์†Œ ๊ฒ€์ƒ‰
    • select : ์ž์ฃผ ๊ฐ€๋Š” ์žฅ์†Œ ์„ ํƒ
  • route : ์„ธ๋กœ ๊ฒฝ๋กœ
  • schedule : ์ผ์ •
  • user : ์œ ์ €(๋กœ๊ทธ์ธ,ํšŒ์›๊ฐ€์ž…)

3. network : ํ†ต์‹ 

4. util : ์• ๋‹ˆ๋ฉ”์ด์…˜

์ฃผ์š” ๊ธฐ๋Šฅ (ํ˜„์žฌ๊นŒ์ง€ ์ง„ํ–‰ํ•œ ์‚ฌํ•ญ)

0. ์Šคํ”Œ๋ž˜์‰ฌ

  • 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"/>
    

1. ์ตœ์ดˆ๊ฐ€์ž…(๋กœ๊ทธ์ธ,ํšŒ์›๊ฐ€์ž…,๋‹‰๋„ค์ž„, ์ž์ฃผ๊ฐ€๋Š” ์žฅ์†Œ ๋“ฑ๋ก)

  • 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 ๋ฉ”์‹œ์ง€ ๋„์šฐ๊ธฐ

2. ์„ธ๋กœ ๊ฒฝ๋กœ

  • ์ถœ๋ฐœ์ง€์—์„œ ๋ชฉ์ ์ง€๋กœ ๊ฐ€๋Š” ์„ธ๋กœ ๊ฒฝ๋กœ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๋ทฐ ์ œ์ž‘. ๊ฒฝ์œ  ์ •๋ฅ˜์žฅ ํ‘œ์‹œํ•˜๊ธฐ ์œ„ํ•ด ๋ฆฌ์‚ฌ์ดํด๋Ÿฌ๋ทฐ ์•ˆ์— ๋ฆฌ์‚ฌ์ดํด๋Ÿฌ๋ทฐ ์ œ์ž‘

  • ์ง€ํ•˜์ฒ  ๊ฒฝ๋กœ(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()
    }

3. ๊ฐ€๋กœ๊ฒฝ๋กœ

์†Œ์š”์‹œ๊ฐ„์„ bar๋กœ ๋ณด์—ฌ์ฃผ๋Š” ๊ฐ€๋กœ๊ฒฝ๋กœ ๋ทฐ ๊ทธ๋ฆฌ๊ธฐ

<ํƒ€์•ผํ•˜๋Š” ๊ตํ†ต ์ˆ˜๋‹จ์˜ ๊ฐฏ์ˆ˜์™€ ์†Œ์š” ์‹œ๊ฐ„์— ๋”ฐ๋ผ ๋™์ ์œผ๋กœ ๋‹ฌ๋ผ์ง€๋Š” ๊ธฐ๋Šฅ>

  • 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>

4. ์• ๋‹ˆ๋ฉ”์ด์…˜

  • ์ˆซ์ž ์˜ฌ๋ผ๊ฐ€๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜ 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
                    }
                }
            }
        }
    }

5. ์ผ์ •ํ‘œ

  • ์ปค์Šคํ…€ ๋‹ฌ๋ ฅ์„ ํ†ตํ•œ ์ผ์ • ์กฐํšŒ ๋ฐ ๋“ฑ๋ก

  • ์˜ค๋Š˜ ๋‚ ์งœ์— ํŒŒ๋ž€ ๋งˆ์ปค ๋””ํดํŠธ

  • ์ปค์Šคํ…€ ๋‹ฌ๋ ฅ -> ๋ทฐํŽ˜์ด์ €์™€ ๋ฆฌ์‚ฌ์ดํด๋Ÿฌ๋ทฐ ์ด์šฉ

6. ๋งต ๋„์šฐ๊ธฐ

  • google map api ์ ์šฉ

  • ๊ฒฝ๋„ ์œ„๋„ ์ขŒํ‘œ๋ฅผ ๋ฐ›์•„ ๊ตฌ๊ธ€๋งต์— 16F zoom ์œผ๋กœ ํ™”๋ฉด ํ‘œ์‹œ

7. ํ™ˆ ํ™”๋ฉด

  • ๊ฐ€์žฅ ์ตœ๊ทผ์— ์žˆ๋Š” ์ผ์ •์— ๋งž์ถฐ ํ™”๋ฉด ์ ์šฉ

  • ์ผ์ •์— ๋Œ€ํ•œ ๋Œ€์ค‘๊ตํ†ต ์•Œ๋ฆผ์ด ์‹œ์ž‘๋˜๋ฉด 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
    }

8. ์ผ์ • ๋“ฑ๋ก

  • 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()
        }
    }
  • ์žฅ์†Œ ๊ฒ€์ƒ‰์„ ํ†ตํ•ด ๋ฐ›์€ ์ถœ๋ฐœ์ง€์™€ ๋„์ฐฉ์ง€ ์ขŒํ‘œ๋กœ ๊ฐ€๋กœ๊ฒฝ๋กœ ํ‘œ์‹œ

About

๐Ÿฅ 2020 ์†ํŠธ APP-JAM : "์–ผ๋ฆฌ๋ฒ„๋””-๋‚˜๋งŒ์˜ ๋ฐฐ์ฐจ์•Œ๋ฆผ"

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Kotlin 100.0%