kimdahyee / sopt_27th-seminar

:earth_asia: sopt_27th-seminar project repository :earth_asia:

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

๐Ÿ‘จโ€๐Ÿš€ sopt_27th-seminar Android_assignment ๐Ÿ‘ฉโ€๐Ÿš€


1์ฃผ์ฐจ View์™€ ViewGroup (20.10.10)


โœ”๏ธ ํ•„์ˆ˜ ๊ณผ์ œ / ์„ฑ์žฅ ๊ณผ์ œ 1 (20.10.17 ์™„๋ฃŒ)

SignUpActivity ๋งŒ๋“ค๊ธฐ / ํ™”๋ฉด ์ด๋™ + @

  1. ๋กœ๊ทธ์ธ ํ™”๋ฉด์—์„œ "ํšŒ์›๊ฐ€์ž…ํ•˜๋Ÿฌ๊ฐ€๊ธฐ" ํด๋ฆญ ์‹œ SignUpActivity๋กœ ์ด๋™

    tv_signup.setOnClickListener { //ํšŒ์›๊ฐ€์ž…ํ•˜๋Ÿฌ๊ฐ€๊ธฐ  
         val intent = Intent(this, SignUpActivity::class.java)  
         startActivityForResult(intent, REQ_CODE)  
    }

  2. ์ด๋ฆ„ / ์•„์ด๋”” / ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ ํ›„ ํšŒ์›๊ฐ€์ž… ์ง„ํ–‰ ( ํ•˜๋‚˜๋ผ๋„ ์ž…๋ ฅ์ด ์•ˆ๋˜์–ด ์žˆ๋Š” ๊ฒฝ์šฐ ToastMessage ์ถœ๋ ฅ ๐Ÿ’ฌ )

    btn_signup.setOnClickListener {  
         when {  
    	     et_name.text.isNullOrBlank() -> Toast.makeText(this, "์ด๋ฆ„์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.", Toast.LENGTH_SHORT).show()  
    	     et_id.text.isNullOrBlank() -> Toast.makeText(this, "์•„์ด๋””๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.", Toast.LENGTH_SHORT).show()  
    	     et_password.text.isNullOrBlank() -> Toast.makeText(this, "๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.", Toast.LENGTH_SHORT).show()  
    	     else -> {  
    		     Toast.makeText(this, "ํšŒ์›๊ฐ€์ž…์— ์„ฑ๊ณตํ•˜์˜€์Šต๋‹ˆ๋‹ค.", Toast.LENGTH_SHORT).show()  
    		     val intent = Intent(this, LoginActivity::class.java)  
    		     intent.putExtra("id", et_id.text.toString())  
    		     intent.putExtra("password", et_password.text.toString())  
    		     setResult(Activity.RESULT_OK, intent)   
    		     finish()  
    	     }  
         }  
    }

  3. ํšŒ์›๊ฐ€์ž… ์„ฑ๊ณต ์‹œ ์ด์ „ ๋กœ๊ทธ์ธ ํ™”๋ฉด์œผ๋กœ ์ด๋™ ( ํšŒ์›๊ฐ€์ž… ์‹œ ์ž…๋ ฅํ•œ ์•„์ด๋”” / ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ๋œ ์ƒํƒœ )

     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {  
         super.onActivityResult(requestCode, resultCode, data) 
          
         if (requestCode == REQ_CODE && resultCode == Activity.RESULT_OK) {  
    	     et_id_login.setText(data!!.getStringExtra("id"))  
    	     et_password_login.setText(data!!.getStringExtra("password"))   
    	     finish()  
         }  
    }

โœ”๏ธ ์„ฑ์žฅ ๊ณผ์ œ 2 (20.10.19 ์™„๋ฃŒ)

์ž๋™ ๋กœ๊ทธ์ธ

๐Ÿ“• ๊ฐ„๋‹จํ•œ ์„ค์ • ๊ฐ’์ด๋‚˜ ๋ฌธ์ž์—ด ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋Š” _SharedPreferences_๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ด€๋ฆฌ ๋ฐ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

  1. SharedPreferences์™€ SharedPreferences์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•œ Editor ๊ฐ์ฒด ์ƒ์„ฑ

    var pref : SharedPreferences = getSharedPreferences("pref", Context.MODE_PRIVATE)  
    var editor : SharedPreferences.Editor = pref.edit()

  2. Editor๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํŒŒ์ผ์— ์•„์ด๋”” / ๋น„๋ฐ€๋ฒˆํ˜ธ ์ •๋ณด ์ €์žฅ

    editor.putString("id", et_id.text.toString())  
    editor.putString("password", et_password.text.toString())  
    editor.commit()  
    //๋ฐ์ดํ„ฐ ์ €์žฅ ๋ฐ ์‚ญ์ œ ์‹œ commit ํ•„์ˆ˜

  3. SharedPreferences์— ์ €์žฅ๋œ ๊ฐ’ ๊ฐ€์ ธ์™€์„œ ์ž๋™ ๋กœ๊ทธ์ธ ์ง„ํ–‰

    var pref: SharedPreferences = getSharedPreferences("pref", Context.MODE_PRIVATE)  
      
    if (!(pref.getString("id", null).isNullOrBlank() || pref.getString("password", null).isNullOrBlank())) {  
    	     val id = pref.getString("id", null).toString()  
    	     //๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ๋•Œ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ key์™€ default๊ฐ’์„ ๋„ฃ์–ด์ค€๋‹ค  
    	     
    	     if (!id.isNullOrBlank()) {  
    		     Toast.makeText(this, "${id}๋‹˜์ด ์ž๋™๋กœ๊ทธ์ธ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", Toast.LENGTH_SHORT).show();  
    		     val intent = Intent(this, MainActivity::class.java)  
    		     startActivityForResult(intent, REQ_CODE)  
    		 }    
    }



2์ฃผ์ฐจ RecyclerView (20.10.17)


โœ”๏ธ ํ•„์ˆ˜ ๊ณผ์ œ (20.11.08 ์™„๋ฃŒ)

ํฌํŠธํด๋ฆฌ์˜ค Recyclerview ๋งŒ๋“ค๊ธฐ

  1. ๋ฐ˜๋ณต๋  item view ์ƒ์„ฑ - item_portpolio.xml

  2. ๋ฐฐ์น˜๋ฐฉํ–ฅ ์„ค์ • - activity_portfolio.xml / PortfolioActivity.kt

  3. data ํ˜•ํƒœ ๊ฒฐ์ • - PortfolioData.kt

  4. viewHolder ์ƒ์„ฑ - PortfolioViewHolder.kt

    ๐Ÿ“• viewHolder๋Š” ๋ฐ˜๋ณต๋  item view ํ•˜๋‚˜์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ์„ ์œ„์น˜ ์ •๋ณด

  5. adapter ์ƒ์„ฑ - PortfolioAdapter.kt

  6. recyclerView์— adapter ์ ์šฉ - PortfolioActivity.kt

    portfolioAdapter = PortfolioAdapter(this)  
    rcv_portfolio.adapter = portfolioAdapte
  7. item ํด๋ฆญ ์‹œ ์ƒ์„ธ ํ™”๋ฉด์œผ๋กœ ์ด๋™ํ•˜๊ธฐ ์œ„ํ•œ RecyclerView click event ์ฒ˜๋ฆฌ

    ( click event๋ฅผ adapter์—์„œ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๊ณ , activity (๋˜๋Š” fragment)์—์„œ ์ฒ˜๋ฆฌํ•˜๊ณ ์ž ํ•  ๋•Œ์˜ ๋ฐฉ๋ฒ• )

    i ) adapter ๋‚ด์— custom click listener interface ์ •์˜

    interface ItemClickListener {  
       fun onItemClick(view: View, position: Int)  
    }

    ii ) listener ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•˜๋Š” method์™€ ์ „๋‹ฌ๋œ ๊ฐ์ฒด๋ฅผ ์ €์žฅํ•  ๋ณ€์ˆ˜ ์ถ”๊ฐ€

    //listener ๊ฐ์ฒด ์ฐธ์กฐ๋ฅผ ์ €์žฅํ•˜๋Š” ๋ณ€์ˆ˜  
    private lateinit var itemClickListener: ItemClickListener  
      
    //ItemClickListener ๊ฐ์ฒด ์ฐธ์กฐ๋ฅผ adapter์— ์ „๋‹ฌํ•˜๋Š” method  
    fun setItemClickListener(itemClickListener: ItemClickListener) {  
        this.itemClickListener = itemClickListener  
    }

    iii ) item view์™€ click listener๋ฅผ ์—ฐ๊ฒฐ

    override fun onBindViewHolder(holder: PortfolioViewHolder, position: Int) {  
        holder.onBind(data[position])  
        holder.itemView.setOnClickListener{  
    	    itemClickListener.onItemClick(it, position)  
        }  
    }

    iv ) activity (๋˜๋Š” fragment)์—์„œ custom listener ๊ฐ์ฒด ์ƒ์„ฑ ๋ฐ ์ „๋‹ฌ

    portfolioAdapter.setItemClickListener(object : PortfolioAdapter.ItemClickListener {  
        override fun onItemClick(view: View, position: Int) {  
    	    val clickedPortfolioIntent = Intent(view.context, PortfolioDetailActivity::class.java)  
    	    startActivity(clickedPortfolioIntent)  
    })

    ๐ŸŽƒ data๊ฐ€ ์žˆ์œผ๋ฉด adapter๊ฐ€ data list ์ค‘ ํ•˜๋‚˜๋ฅผ viewHolder์—๊ฒŒ ์ „๋‹ฌํ•˜๊ณ  viewHolder๋Š” ์ „๋‹ฌ ๋ฐ›์€ data๋ฅผ view์— ๋ฟŒ๋ ค์ค€๋‹ค



โœ”๏ธ ์„ฑ์žฅ ๊ณผ์ œ 1 (20.11.16 ์™„๋ฃŒ)

GridLayout ๋งŒ๋“ค๊ธฐ

๐Ÿ“‹ menu์—์„œ LinearLayout ํด๋ฆญ ์‹œ ๋ฐฐ์น˜ ๋ฐฉํ–ฅ -> Linear / GridLayout ํด๋ฆญ ์‹œ ๋ฐฐ์น˜ ๋ฐฉํ–ฅ -> Grid


  1. ๋ฐฐ์น˜ ๋ฐฉํ–ฅ ์„ ํƒ์„ ์œ„ํ•œ menu ์ƒ์„ฑ

    i ) res ํด๋” ์•ˆ์— menu directory ์ƒ์„ฑ ํ›„ menu directory ์•ˆ์— xml ํŒŒ์ผ ์ƒ์„ฑํ•ด์„œ menu item ์„ค์ •

    ii ) option menu ์ง€์ •์„ ์œ„ํ•ด onCreateOptionsMenu() ์žฌ์ •์˜

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {  
        val inflater: MenuInflater = menuInflater  
        inflater.inflate(R.menu.layout, menu)  
        return true  
    }   
  2. Grid ๋ฐฉํ–ฅ์„ ์œ„ํ•œ ์ƒˆ๋กœ์šด layout ์ž‘์„ฑ

  3. adapter์—์„œ viewType์— ๋”ฐ๋ผ inflate๋˜๋Š” view๋ฅผ ์ง€์ •

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PortfolioViewHolder {  
    	var view = when (viewType) {  
    	     1 -> {  
    		     LayoutInflater.from(context).inflate(R.layout.item_portfolio, parent, false)  
    	     }  
    	     else -> {  
    		     LayoutInflater.from(context).inflate(R.layout.item_portfolio_grid, parent, false)  
    	     }  
    	 }  
    	 return PortfolioViewHolder(view)  
    }  
          
    override fun getItemViewType(position: Int): Int {  
         return viewType  
    }
  4. activity์—์„œ ์„ ํƒ๋˜๋Š” menu item์— ๋”ฐ๋ผ viewType๊ณผ LayoutManager๋ฅผ ์ง€์ •

    override fun onOptionsItemSelected(item: MenuItem): Boolean {  
         when (item?.itemId) {  
    	     R.id.menu_linear -> {  
    		     portfolioAdapter.viewType = 1  
    		     rcv_portfolio.layoutManager = LinearLayoutManager(this)  
    	     }  
    	     R.id.menu_grid -> {  
    		     portfolioAdapter.viewType = 2  
    		     rcv_portfolio.layoutManager = GridLayoutManager(this, 3, RecyclerView.VERTICAL, false)  
    	     }  
    	 }  
         return super.onOptionsItemSelected(item)  
    }



3์ฃผ์ฐจ Fragment, ViewPager, BottomNavigation, TabLayout (20.10.31)


โœ”๏ธ ํ•„์ˆ˜ ๊ณผ์ œ (20.11.30 ์™„๋ฃŒ)

BottomNavigation + ViewPager + TabLayout ์‚ฌ์šฉํ•ด์„œ ํ”„๋กœํ•„ view ๊ตฌํ˜„ํ•˜๊ธฐ



BottomNavigation + ViewPager

  1. res ํด๋” ์•ˆ์— menu directory ์ƒ์„ฑ ํ›„ menu directory ์•ˆ์— xml ํŒŒ์ผ ์ƒ์„ฑํ•ด์„œ menu item ์„ค์ • - navigation.xml

  2. res ํด๋” ์•ˆ์— color directory ์ƒ์„ฑ ํ›„ color directory ์•ˆ์— xml ํŒŒ์ผ ์ƒ์„ฑํ•ด์„œ icon selector ์„ค์ • - selector_nav.xml

  3. ๋ฐฐ์น˜ ์„ค์ • - activity_main.xml

  4. viewPager adapter ์ƒ์„ฑ - MainPagerAdapter.kt

    class MainPagerAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT){
    
        override fun getItem(position: Int): Fragment {
            return when(position) {
                0 -> ProfileFragment()  //index๊ฐ€ 0์ผ๋•Œ
                1 -> LinearFragment()
                else -> GridFragment()
            }
        }
    
        override fun getCount() = 3
    }
  5. viewPager์— adapter ์ ์šฉ - MainActivity.kt

    viewPager_main.adapter = MainPagerAdapter(supportFragmentManager)
  6. viewPager์˜ ํ™”๋ฉด์ „ํ™˜์„ ๊ฐ์ง€ํ•˜๋Š” listener ์„ค์ • - MainActivity.kt

    viewPager_main.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
                // viewPager์˜ ์ด๋™ -> ํ•˜๋‹จ ํƒญ์˜ ์ฒดํฌ ์ƒํƒœ ๋ณ€ํ™”
                override fun onPageScrollStateChanged(state: Int) {
                }
    
                override fun onPageScrolled(
                    position: Int,
                    positionOffset: Float,
                    positionOffsetPixels: Int
                ) {
                }
    
                override fun onPageSelected(position: Int) {
                    // navigation ๋ฉ”๋‰ด ์•„์ดํ…œ ์ฒดํฌ
                    main_bottomNavigation.menu.getItem(position).isChecked = true
                }
    })
  7. bottomNavigation item ํด๋ฆญ ์‹œ ํด๋ฆญ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  listener ์„ค์ • - MainActivity.kt

    main_bottomNavigation.setOnNavigationItemSelectedListener {
                // ํ•˜๋‹จ ํƒญ์˜ ์ฒดํฌ ์ด๋ฒคํŠธ -> ํ•ด๋‹นํ•˜๋Š” ํŽ˜์ด์ง€๋กœ ์ด๋™
                var index by Delegates.notNull<Int>()
                when (it.itemId) {
                    R.id.nav_profile -> index = 0
                    R.id.nav_linear -> index = 1
                    R.id.nav_grid -> index = 2
                }
                viewPager_main.currentItem = index
                true
    }



TabLayout

  1. ๋ฐฐ์น˜ ์„ค์ • - fragment_profile.xml

  2. viewPager adapter ์ƒ์„ฑ - ProfilePagerAdapter.kt

  3. viewPager์— adapter ์ ์šฉ - ProfileFragment.kt

  4. tabLayout์— ViewPager ์—ฐ๋™ - ProfileFragment.kt

    tabLayout_profile.setupWithViewPager(viewPager_profile)
  5. tab title ์„ค์ • - ProfileFragment.kt

    ๐Ÿ“• ๋ฐ˜๋“œ์‹œ viewPager์™€ ์—ฐ๋™ ํ›„ ์ž‘์„ฑํ•ด์•ผํ•œ๋‹ค

    tabLayout_profile.apply {
                getTabAt(0)?.text = "INFO"
                getTabAt(1)?.text = "OTHER"
    }



6์ฃผ์ฐจ Server (20.11.21)


โœ”๏ธ ํ•„์ˆ˜ ๊ณผ์ œ (20.12.03 ์™„๋ฃŒ)

๋กœ๊ทธ์ธ/ํšŒ์›๊ฐ€์ž… ์„œ๋ฒ„ ํ†ต์‹  ๊ตฌํ˜„ํ•˜๊ธฐ



  1. Request/Response ๊ฐ์ฒด ์ƒ์„ฑ

    • SignUpRequest.kt

      data class SignUpRequest (
          val email: String,
          val password: String,
          val userName: String
      )
    • SignUpResponse.kt

      data class SignUpResponse(
          val data: Data,
          val status: Int,
          val success: Boolean,
          val message: String
      ) {
          data class Data(
              val email: String,
              val password: String,
              val userName: String
          )
      }
    • SignInRequest.kt

      data class SignInRequest(
          val email: String,
          val password: String
      )
    • SignInResponse.kt

      data class SignInResponse(
          val data: Data,
          val status: Int,
          val success: Boolean,
          val message: String
      ) {
          data class Data(
              val email: String,
              val password: String,
              val userName: String
          )
      }

  2. retrofit interface ์ƒ์„ฑ - SoptService.kt

    interface SoptService {
    
        @Headers("Content-Type:application/json")
        @POST("/users/signup")
        fun signup(
            @Body body: SignUpRequest
        ): Call<SignUpResponse>
    
        @Headers("Content-Type:application/json")
        @POST("/users/signin")
        fun signin(
            @Body body: SignInRequest
        ): Call<SignInResponse>
    }

  1. ๊ตฌํ˜„์ฒด ์ƒ์„ฑ - SoptServiceImpl.kr

    object SoptServiceImpl {
        // ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๋งŒ๋“œ๋Š” ์‹ค์ œ ๊ตฌํ˜„์ฒด
        // -> ๊ฐ์ฒด๋Š” ํ•˜๋‚˜๋งŒ ์ƒ์„ฑํ•˜๊ณ  ํ”„๋กœ์ ํŠธ ์–ด๋””์„œ๋‚˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋Š” ๋””์ž์ธ ํŒจํ„ด
        // object: ์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด object๋กœ ์„ ์–ธ
    
        private const val BASE_URL = "http://15.164.83.210:3000"
    
        private val retrofit: Retrofit = Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
                //gson ์—ฐ๋™: retrofit์—์„œ ๋ฐ›์•„์˜ค๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃจ๊ธฐ ์‰ฝ๊ฒŒ ๋ณ€ํ™˜
            .build()
        // Retrofit ๊ฐ์ฒด ์ƒ์„ฑ
    
        val service: SoptService = retrofit.create(SoptService::class.java)
        // Interface ๊ฐ์ฒด๋ฅผ ๋„˜๊ฒจ ์‹ค์ œ ๊ตฌํ˜„์ฒด ์ƒ์„ฑ
    }

About

:earth_asia: sopt_27th-seminar project repository :earth_asia:


Languages

Language:Kotlin 100.0%