Skip to content

29th-WE-SOPT-Android-Part/Android-Jaehoon

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

40 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Android-Jaehoon

github_์กฐ์žฌํ›ˆ_ver1-27


Week 1

Level 1

  • SignInActivity

    • '๋กœ๊ทธ์ธ ๋ฒ„ํŠผ' ํด๋ฆญ ์‹œ ๋ชจ๋“  EditText๊ฐ€ ์ž…๋ ฅ๋˜์–ด ์žˆ๋Š” ์ง€ ํ™•์ธ

       if(!binding.etId.text.toString().isEmpty() && !binding.etPw.text.toString().isEmpty()) {   
          
          //...
          
       }
    • ๋น„๋ฐ€๋ฒˆํ˜ธ EditText inputType ์†์„ฑ

        android:inputType="textPassword"
    • ๋ชจ๋“  ์ž…๋ ฅ์ด ๋˜์—ˆ์„ ๋•Œ ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ HomeActivity๋กœ ์ด๋™

        val intent = Intent(this, HomeActivity::class.java)
        startActivity(intent)
    • ํšŒ์›๊ฐ€์ž… ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ SignUpActivity๋กœ ์ด๋™

        val intent = Intent(this, SignUpActivity::class.java)
        activityResultLauncher.launch(intent)
  • SignUpActivity

    • 'ํšŒ์›๊ฐ€์ž… ์™„๋ฃŒ' ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ๋ชจ๋“  EditText๊ฐ€ ์ž…๋ ฅ๋˜์–ด ์žˆ๋Š” ์ง€ ํ™•์ธ

        if(!etName.text.toString().isEmpty()
        && !etId.text.toString().isEmpty() && !etPw.text.toString().isEmpty()) {
          
          //...
          
        }
    • ๋น„๋ฐ€๋ฒˆํ˜ธ EditText inputType ์†์„ฑ

        android:inputType="textPassword"

Level 2

  • ํ™”๋ฉด ์ด๋™

    • SignUpActivity

        binding.apply {
                btnSignUp.setOnClickListener {
                    if(!etName.text.toString().isEmpty() && !etId.text.toString().isEmpty() && !etPw.text.toString().isEmpty()) {
                        intent.putExtra("id", etId.text.toString())
                        intent.putExtra("pw", etPw.text.toString())
                        setResult(RESULT_OK, intent)
                        finish()
                    } else {
                        Toast.makeText(this@SignUpActivity, "์ž…๋ ฅ๋˜์ง€ ์•Š์€ ์ •๋ณด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค", Toast.LENGTH_SHORT).show()
                    }
                }
            }
    • SignInActivity

        class SignInActivity : AppCompatActivity() {
          private lateinit var binding : ActivitySignInBinding
          private lateinit var activityResultLauncher : ActivityResultLauncher<Intent>
          override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            binding = ActivitySignInBinding.inflate(layoutInflater)
            setContentView(binding.root)
            activityResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
                if(it.resultCode == RESULT_OK) {
                    binding.etId.setText(it.data?.getStringExtra("id"))
                    binding.etPw.setText(it.data?.getStringExtra("pw"))
                }
            }
            //...
            binding.btnSignUp.setOnClickListener {
                val intent = Intent(this, SignUpActivity::class.java)
                activityResultLauncher.launch(intent)
            }
          }
        }
  • ์ธํ…ํŠธ

    • ๋ช…์‹œ์  ์ธํ…ํŠธ

      • ์ธํ…ํŠธ์— ํด๋ž˜์Šค ๊ฐ์ฒด๋‚˜ ์ปดํฌ๋„ŒํŠธ ์ด๋ฆ„์„ ์ง€์ •ํ•˜์—ฌ ํ˜ธ์ถœํ•  ๋Œ€์ƒ์„ ํ™•์‹คํžˆ ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ
      • ์ฃผ๋กœ ์•ฑ ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉ
      • ํŠน์ • ์ปดํฌ๋„ŒํŠธ๋‚˜ ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ๋ช…ํ™•ํ•˜๊ฒŒ ์‹คํ–‰๋˜์–ด์•ผํ•  ๊ฒฝ์šฐ
    • ์•”์‹œ์  ์ธํ…ํŠธ

      • ์ธํ…ํŠธ์˜ ์•ก์…˜๊ณผ ๋ฐ์ดํ„ฐ๋ฅผ ์ง€์ •ํ•˜๊ธด ํ–ˆ์ง€๋งŒ, ํ˜ธ์ถœํ•  ๋Œ€์ƒ์ด ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ
      • ์•ˆ๋“œ๋กœ์ด๋“œ ์‹œ์Šคํ…œ์ด ์ธํ…ํŠธ๋ฅผ ์ด์šฉํ•ด ์š”์ฒญํ•œ ์ •๋ณด๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์ ์ ˆํ•œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ฐพ์•„ ์‚ฌ์šฉ์ž์—๊ฒŒ ๊ทธ ๋Œ€์ƒ๊ณผ ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์—ฌ์คŒ
      • ํ•ด๋‹น ๊ธฐ๋Šฅ๋“ค์„ ์ง€์›ํ•˜๋Š” ์•ฑ๋“ค์ด ์žˆ๋Š” ๊ฒฝ์šฐ์— ์•”์‹œ์  ์ธํ…ํŠธ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ทธ ์•ฑ๋“ค์„ ์‚ฌ์šฉ
  • HomeActivity ํ™”๋ฉด ๋ ˆ์ด์•„์›ƒ ์ˆ˜์ •

    • nestedScrollView ์‚ฌ์šฉ

        <androidx.core.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fillViewport="true">
      
            <androidx.constraintlayout.widget.ConstraintLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent">
      
                //...
      
            </androidx.constraintlayout.widget.ConstraintLayout>
      
        </androidx.core.widget.NestedScrollView>
    • constraintDimensionRatio ์†์„ฑ ์‚ฌ์šฉ

      • height๋ฅผ 0dp๋กœ ์„ค์ •ํ•˜๊ณ  layout_constraintDimensionRatio์— 1์„ ๋„ฃ์–ด์„œ width์™€ height๋ฅผ 1:1๋น„์œจ๋กœ ์กฐ์ •
        <ImageView
                  android:id="@+id/iv_profile"
                  android:layout_width="200dp"  
                  android:layout_height="0dp" 
      
                  //...
      
                  app:layout_constraintDimensionRatio="1" />

Level 3

  • ViewBinding & DataBinding

    • ViewBinding๊ณผ DataBinding์˜ ๊ด€๊ณ„ image

    • ๊ณตํ†ต์ 

      • findViewById์— ๋น„ํ•ด ์ƒ๋Œ€์ ์œผ๋กœ ๊ฐ„๋‹จํ•˜๋ฉฐ ํผํฌ๋จผ์Šค ํšจ์œจ์ด ์ข‹๊ณ  ์šฉ๋Ÿ‰ ์ ˆ์•ฝ ๊ฐ€๋Šฅ
      • ๋ทฐ์˜ ์ง์ ‘ ์ฐธ์กฐ๋ฅผ ์ƒ์„ฑํ•˜๋ฏ€๋กœ ์œ ํšจํ•˜์ง€ ์•Š์€ ๋ทฐ ID๋กœ ์ธํ•œ NPE๋กœ๋ถ€ํ„ฐ ์•ˆ์ „
    • ViewBinding์˜ ์žฅ์ 

      • ๋น ๋ฅธ ์ปดํŒŒ์ผ ์†๋„์™€ ๋”ฐ๋กœ xml ํŒŒ์ผ์— ํƒœ๊ทธ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š๊ณ  ์ž๋™์œผ๋กœ ์ ์šฉ๋˜๋ฏ€๋กœ ์‚ฌ์šฉ ํŽธ๋ฆฌ
    • DataBinding์˜ ์žฅ์ 

      • ๋ฐ์ดํ„ฐ์™€ ๋ทฐ๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ์ž‘์—…์„ ๋ ˆ์ด์•„์›ƒ์—์„œ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
      • ๋™์  UI ์ฝ˜ํ…์ธ  ์„ ์–ธ ๋ฐ ์–‘๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ๊ฒฐํ•ฉ ์ง€์›
  • ์ฝ”ํ‹€๋ฆฐ์—์„  setOnClickListener๋ฅผ ๋žŒ๋‹ค์‹์œผ๋กœ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ์ด์œ 

    • ์ฝ”ํ‹€๋ฆฐ์ด ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ
      ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ๋Š” ํ•จ์ˆ˜๋ฅผ ๊ฐ’์ฒ˜๋Ÿผ ๋‹ค๋ฃจ๋Š” ์ ‘๊ทผ ๋ฐฉ์‹์„ ํƒํ•จ์œผ๋กœ์จ,
      ๊ธฐ์กด์ฒ˜๋Ÿผ ํด๋ž˜์Šค๋ฅผ ์„ ์–ธํ•˜๊ณ  ๊ทธ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ํ•จ์ˆ˜์— ๋„˜๊ธฐ๋Š” ๋Œ€์‹ ,
      ํ•จ์ˆ˜๋ฅผ ์ง์ ‘ ๋‹ค๋ฅธ ํ•จ์ˆ˜์— ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Œ.
    

    ์ž๋ฐ”

      button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
          //TODO
        }
      });

    ์ฝ”ํ‹€๋ฆฐ

      button.setOnClickListener { 
        //TODO
      }

Week 2

Level 1

  • HomeActivity.kt

    • ๊ฐ Fragment๋กœ ์ด๋™ํ•˜๋Š” Button๊ณผ FragmentContainerView ์ถ”๊ฐ€

        <Button
          android:id="@+id/btn_follower_list"
            ...
          />
      
        <Button
          android:id="@+id/btn_repository_list"
            ...
          />
        
        <androidx.fragment.app.FragmentContainerView
          android:id="@+id/fc_home_list"
            ...
          />
    • ๊ฐ ๋ฒ„ํŠผ์„ ํด๋ฆญ ์‹œ Fragment ์ด๋™

        private fun initBtn() {
          binding.btnGit.setOnClickListener {
              val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/jaehoon-jo"))
              startActivity(intent)
          }
      
          binding.btnFollowerList.setOnClickListener {
              transFragment(FOLLOWER_BTN)
          }
      
          binding.btnRepositoryList.setOnClickListener {
              transFragment(REPOSITORY_BTN)
          }
        }
        
        private fun transFragment(btn : Int) {
          val transaction = supportFragmentManager.beginTransaction()
      
          when(btn) {
              FOLLOWER_BTN -> {
                  val followerFragment = FollowerFragment()
                  transaction.replace(R.id.fc_home_list, followerFragment).commit()
      
              }
              REPOSITORY_BTN -> {
                  val repositoryFragment = RepositoryFragment()
                  transaction.replace(R.id.fc_home_list, repositoryFragment).commit()
              }
          }
        }
  • FollowerFragment, FollowerAdapter, Follower ์ƒ์„ฑ

    • FollowerFragment์— ๋ฆฌ์‚ฌ์ดํด๋Ÿฌ๋ทฐ ์ƒ์„ฑ

        <androidx.recyclerview.widget.RecyclerView
          android:id="@+id/rv_follower"
          app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            ...
          />
  • RepositoryFragment, RepositoryAdapter, Repository ์ƒ์„ฑ

    • RepositoryFragment์— ๋ฆฌ์‚ฌ์ดํด๋Ÿฌ๋ทฐ ์ƒ์„ฑ, GridLayoutManager

        <androidx.recyclerview.widget.RecyclerView
          android:id="@+id/rv_repository"
          app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
            ...
          />

Week 3

Level 1

  • SignInActivity.kt & SignUpActivity.kt

    • selector๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ EditText๊ฐ€ focus ์—ฌ๋ถ€์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๋””์ž์ธ ์ถœ๋ ฅ

        <?xml version="1.0" encoding="utf-8"?>
        <selector xmlns:android="http://schemas.android.com/apk/res/android">
            <item android:state_focused="true" android:drawable="@drawable/edit_text_selected"/>
            <item android:state_focused="false" android:drawable="@drawable/edit_text_unselected"/>
        </selector>
    • ๋กœ๊ทธ์ธ, ํšŒ์›๊ฐ€์ž… ๋ฒ„ํŠผ์— shape๋ฅผ ํ†ตํ•ด round ์†์„ฑ ์ถ”๊ฐ€

        <?xml version="1.0" encoding="utf-8"?>
        <shape xmlns:android="http://schemas.android.com/apk/res/android"
            android:shape="rectangle" android:tint="@color/sopt_pink2">
            <corners android:radius="5dp"/>
        </shape>
  • ProfileFragment.kt

    • Button์— selector ํ™œ์šฉ

        <selector xmlns:android="http://schemas.android.com/apk/res/android">
            <item
                android:state_checked="true"
                android:drawable="@drawable/shape_profile_btn_selected"
                />
            <item
                android:state_checked="false"
                android:drawable="@drawable/shape_profile_btn_unselected"
                />
        </selector>
    • ์ด๋ฏธ์ง€ Glide์˜ CircleCrop๊ธฐ๋Šฅ ํ™œ์šฉ

        Glide.with(this)
              .load("https://www.riotgames.com/darkroom/2880/656220f9ab667529111a78aae0e6ab9f:d1a7c6d0384f2edf9672d9369a8e9083/01-logo.png")
              .circleCrop()
              .into(binding.ivProfile)
  • HomeFragment.kt

    • TabLayout + ViewPager2 ์ถ”๊ฐ€

            <com.google.android.material.tabs.TabLayout
                android:id="@+id/tl_home_fragment"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:tabIndicatorHeight="3dp"
                app:tabIndicatorColor="@color/sopt_pink2"
                app:layout_constraintBottom_toTopOf="@+id/vp_home_fragment" />
      
            <androidx.viewpager2.widget.ViewPager2
                android:id="@+id/vp_home_fragment"
                android:layout_width="match_parent"
                android:layout_height="338dp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toEndOf="parent" />

Week 4

Level 1

  • POSTMAN ํ…Œ์ŠคํŠธ - ํšŒ์›๊ฐ€์ž… ์™„๋ฃŒ & ๋กœ๊ทธ์ธ ์™„๋ฃŒ

    • ํšŒ์›๊ฐ€์ž… แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2021-11-12 แ„‹แ…ฉแ„’แ…ฎ 8 38 28

    • ๋กœ๊ทธ์ธ แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2021-11-12 แ„‹แ…ฉแ„’แ…ฎ 8 38 34

  • retrofit

    • retrofit interface

      • LoginService.kt
          interface LoginService {
              @Headers("Content-Type:application/json")
              @POST("user/login")
              fun postLogin(
                  @Body body: RequestLoginData
              ) : Call<ResponseLoginData>
        
              @Headers("Content-Type:application/json")
              @POST("user/signup")
              fun postSignUp(
                  @Body body: RequestSignUpData
              ) : Call<ResponseSignUpData>
          }
    • retrofit ๊ตฌํ˜„์ฒด

      • ServiceCreator.kt
          object ServiceCreator {
              private const val BASE_URL = "https://asia-northeast3-we-sopt-29.cloudfunctions.net/api/"
        
              private val retrofit: Retrofit = Retrofit
                  .Builder()
                  .baseUrl(BASE_URL)
                  .addConverterFactory(GsonConverterFactory.create())
                  .build()
        
              val loginService : LoginService = retrofit.create(LoginService::class.java)
              val signUpService : LoginService = retrofit.create(LoginService::class.java)
          }
    • Request/Response ๊ฐ์ฒด

      • SignInActivity.kt

          private fun initNetwork(){
              val requestLoginData = RequestLoginData(
                  email = binding.etId.text.toString(),
                  password = binding.etPw.text.toString()
              )
        
              val call: Call<ResponseLoginData> = ServiceCreator.loginService.postLogin(requestLoginData)
        
              call.enqueue(object : Callback<ResponseLoginData> {
                  override fun onResponse(
                      call: Call<ResponseLoginData>,
                      response: Response<ResponseLoginData>
                  ) {
                      if(response.isSuccessful){
                          val data=response.body()?.data
        
                          Toast.makeText(this@SignInActivity,"${data?.name}๋‹˜ ๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹ค!", Toast.LENGTH_SHORT).show()
                          startActivity(Intent(this@SignInActivity, HomeActivity::class.java))
                      }else{
                          Toast.makeText(this@SignInActivity,"๋กœ๊ทธ์ธ์— ์‹คํŒจํ•˜์…จ์Šต๋‹ˆ๋‹ค", Toast.LENGTH_SHORT).show()
                      }
                  }
        
                  override fun onFailure(call: Call<ResponseLoginData>, t: Throwable) {
                      Log.e("NetworkTest","error:$t")
                  }
              })
          }
      • SignUpActivity.kt

          private fun initNetwork(){
              val requestSignUpData = RequestSignUpData(
                  email = binding.etId.text.toString(),
                  name = binding.etName.text.toString(),
                  password=binding.etPw.text.toString()
              )
        
              val call: Call<ResponseSignUpData> = ServiceCreator.signUpService.postSignUp(requestSignUpData)
        
              call.enqueue(object : Callback<ResponseSignUpData> {
                  override fun onResponse(
                      call: Call<ResponseSignUpData>,
                      response: Response<ResponseSignUpData>
                  ) {
                      if(response.isSuccessful){
                          val data=response.body()?.data
        
                          Toast.makeText(this@SignUpActivity,"${data?.name}๋‹˜ ํšŒ์›๊ฐ€์ž… ์™„๋ฃŒ", Toast.LENGTH_SHORT).show()
        
                          val intent = Intent(this@SignUpActivity, SignInActivity::class.java)
        
                          intent
                          .putExtra("id", binding.etId.text.toString())
                          .putExtra("pw", binding.etPw.text.toString())
        
                          setResult(RESULT_OK, intent)
                          finish()
                      }else{
                          Toast.makeText(this@SignUpActivity,"ํšŒ์›๊ฐ€์ž… ์‹คํŒจ", Toast.LENGTH_SHORT).show()
                      }
                  }
        
                  override fun onFailure(call: Call<ResponseSignUpData>, t: Throwable) {
                      Log.e("NetworkTest","error:$t")
                  }
              })
          }

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages