1์ฃผ์ฐจ
SignIn | Login | Home | SignUp |
---|---|---|---|
1. SignUp
- ์์ด๋, ๋น๋ฐ๋ฒํธ ์ ๋ ฅ์ด ๋ชจ๋ ๋์์ ๋๋ง ๋ก๊ทธ์ธ ๋ฒํผ ๋๋ ์ ๋ HomeActivity๋ก ์ด๋ (ํ ์คํธ๋ฉ์์ง ์ถ๋ ฅ)
val intentHome = Intent(this, HomeActivity::class.java)
binding.apply {
btnLogin.setOnClickListener {
val userId : String = etId.text.toString()
val userPw : String = etPassword.text.toString()
if (userId.isNotEmpty() && userPw.isNotEmpty()) {
startActivity(intentHome)
Toast.makeText(this@SignInActivity, "$userId ๋ ํ์ํฉ๋๋ค", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this@SignInActivity, "๋ก๊ทธ์ธ ์คํจ", Toast.LENGTH_SHORT).show()
}
}
}
- ํ์๊ฐ์ ๋ฒํผ ๋๋ ์ ๋ ํ๋ฉด ์ด๋
val intentSingUp = Intent(this, SignUpActivity::class.java)
btnSignup.setOnClickListener {
startActivity(intentSingUp)
finish()
}
- ํ์๊ฐ์ ์ ์ฑ๊ณตํ ๋ค, ์์ด๋&ํจ์ค์๋ ์๋ ์ ๋ ฅ
if (intent.hasExtra("id") && intent.hasExtra("pw")) {
val id = intent.getStringExtra("id")
val pw = intent.getStringExtra("pw")
etId.setText(id)
etPassword.setText(pw)
}
- EditText์ hint ์์ฑ ๋ฐ ๋น๋ฐ๋ฒํธ์ inputType ์์ฑ
<EditText
android:id="@+id/et_password"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginStart="40dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="40dp"
android:background="@drawable/et_border_pink"
android:ems="10"
android:hint="๋น๋ฐ๋ฒํธ๋ฅผ ์
๋ ฅํด์ฃผ์ธ์"
android:textSize="15dp"
android:paddingStart="20dp"
android:inputType="textPassword"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_password" />
2. Home
- Home ๋ฒํผ ๋๋ฅด๋ฉด ๋์ git ํ์ด์ง๋ก ์ด๋ (์์์ ์ธํ ํธ)
binding.btnGit.setOnClickListener{
var intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/lhb8106"))
startActivity(intent)
}
โ
๋ช
์์ ์ธํ
ํธ์ ์์์ ์ธํ
ํธ์ ์ฐจ์ด์ โ
๋ช
์์ ์ธํ
ํธ๋ ์คํํ๊ณ ์ํ๋ ์ปดํฌ๋ํธ๊ฐ ๋ช
ํํ ๋ ์ฌ์ฉํ๋ ๋ฐฉ์์
๋๋ค. ์ฆ, ํจํค์ง ๋ด๋ถ์ ์กํฐ๋นํฐ๋ฅผ ์คํํ ๋ ์ฌ์ฉ๋ฉ๋๋ค.
์์์ ์ธํ
ํธ๋ ์ด๋ ํ ์ธํ
ํธ๋ฅผ ๋ด์์ ๋ณด๋ด๋ฉด, ์์คํ
์ด ์ ์ ํ ์ปดํฌ๋ํธ๋ฅผ ์ฐพ์์ ์คํํด์ฃผ๋ ๋ฐฉ์์
๋๋ค.
- ์ฌ์ง ๋น์จ ๋ง์ถ๊ธฐ
<ImageView
android:id="@+id/iv_profile"
android:layout_width="180dp"
android:layout_height="0dp"
android:layout_marginTop="20dp"
android:src="@drawable/selca"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
- ์คํฌ๋กค๋ทฐ ์ ์ฉ
<ScrollView
android:id="@+id/scroll"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:fillViewport="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_title">
</ScrollView>
3. SignIn
- ํ์๊ฐ์ ์๋ฃ ๋ฒํผ ๋๋ ์ ๋, ๋น์นธ ํ์ธ ๋ฐ ์์ด๋, ๋น๋ฐ๋ฒํธ ๊ฐ ๋๊ฒจ์ฃผ๊ธฐ
val intent = Intent(this, SignInActivity::class.java)
binding.apply {
btnSingup.setOnClickListener {
val userName : String = etName.text.toString()
val userId : String = etId.text.toString()
val userPw : String = etPassword.text.toString()
if (userName.isNotEmpty() && userId.isNotEmpty() && userPw.trim().isNotEmpty()) {
intent.putExtra("id", userId)
intent.putExtra("pw", userPw)
startActivity(intent)
finish()
} else {
Toast.makeText(this@SignUpActivity, "์
๋ ฅ๋์ง ์์ ์ ๋ณด๊ฐ ์์ต๋๋ค", Toast.LENGTH_SHORT).show()
}
}
}
- hint ์์ฑ ๋ฐ inputType ์์ฑ์ ์์ SignUp๊ณผ ๊ฐ์ต๋๋ค.
๐ค์ด๋ฒ ๊ณผ์ ๋ฅผ ํตํด ๋ฐฐ์ด ๋ด์ฉ & ์ฑ์ฅํ ๋ด์ฉ๐ค
**โconstraintlayout์ ์ ํํ ์ดํดํ์ต๋๋ค!**
์ง๊ธ๊น์ง ์๋๋ก์ด๋ ์คํ๋์ค์์ LinearLayout์ ์ฃผ๋ก ์ฌ์ฉํ๊ณ , ConstraintLayout์ ์ ๋๋ก ํ์ฉํด๋ณธ ๊ฒฝํ์ด ์์๋๋ฐ, ์ด๋ฒ ๊ณผ์ ์ ์ธ๋ฏธ๋๋ฅผ ํตํด ConstraintLayout์ ์ ๋๋ก ์ดํดํ๊ณ ํ์ฉํ ์ ์๋ ๋ฐฉ๋ฒ์ ์๊ฒ ๋์์ต๋๋ค.
๊ทธ ์ธ์๋ layout ๋ด์์ ์ธ ์ ์๋ ๋ค์ํ ์์ฑ์ ์ตํ ์ ์์์ต๋๋ค. (inputType ์์ฑ & constraintDimensionRatio ์์ฑ)
โintent๋ฅผ ์ดํดํ ์ ์๋ ๊ธฐํ๊ฐ ๋์์ต๋๋ค!
์ฒ์์๋ ์ ์ดํด๊ฐ ๋์ง ์์์ง๋ง, ์ฌ๋ฌ๋ฒ ํ๋ฉด์ ์ด๋ํ๋ ์ฐ์ต์ ํ๋ค๋ณด๋, intent๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ๊ณผ ํ์ฉ ๋ฐฉ์์ ๋ํด์ ์ ์ตํ ์ ์๋ ๊ธฐํ๊ฐ ๋์์ต๋๋ค.
ํ์ง๋ง hasExtra๋ ์์ง์..์ ๋ชจ๋ฅด๊ฒ ๋ค์.. ์ผ๋ ๋ฑ๋น ์๋๋๋ก ์์ง์ด๊ธด ํ์ง๋ง ๋ ์ ํํ ์ฝ๋๋ฅผ ์์ฑํ๊ณ ์ถ์ต๋๋ค.
๐git๊ณผ notion
git์ ๋ค๋ฃจ๋ ๋ฐฉ๋ฒ์ ํ๋๋ ๋ชจ๋ฅด๋ ์ฌ๋์ด์๋๋ฐ, ์ด๋ฒ์ ๊ณผ์ ๋ฅผ ์ ์ถํ๊ธฐ ์ํด ์ ํ๋ธ๋ ์ฐพ์๋ณด๊ณ ๋ง์ ์ฌ๋๋ค์๊ฒ ๋ฌผ์ด๋ณด๋ฉฐ ๊ณต๋ถ๋ฅผ ํ์ต๋๋ค..
์์ง ๋ฐฐ์์ผํ ์ ์ด ์ฐ๋๋ฏธ์ด์ง๋ง ์ฐจ๊ทผ์ฐจ๊ทผ ์ฌ๋ผ๊ฐ์ ๋์ค์๋ ๊ผญ ๊น ๋ง์คํฐ๊ฐ ๋๊ฒ ์ต๋๋ค..โ
๊ทธ๋ฆฌ๊ณ notion์ ์ํธ ๋ค์ด์์ ์ฒ์ ์๊ฒ ๋์๋ค๋ณด๋, ์๋ฃ ํ๋ ์ฐพ๋๋ฐ์๋ ๊ฝค ๋ง์ ์๊ฐ์ ์๋ชจํ์ต๋๋ค.
ํ์ง๋ง ์์ฃผ ๋ค์ด๊ฐ์ ์ ๋ณด๋ฅผ ํ์ธํ๋ค๋ณด๋ notion์ด ์ ์ ์ด๋ ต์ง ์๊ฒ ๋๊ปด์ง๋๋ค!
๋
ธ์
๊ณผ ๊น.. ์ฐจ์ฐจ ์นํด์ง๊ฒ ์ต๋๋ค๐
2์ฃผ์ฐจ
Home |
---|
๐FollowerRecyclerView์ RepositoryRecyclerView์ ์ฝ๋๊ฐ ์ ์ฌํ๋ฏ๋ก FollowerRecyclerView ๊ตฌํ์ฝ๋๋ง ์์ฑํ๊ฒ ์ต๋๋ค.๐
1. FollowerRecyclerView
+) LinearLayoutManager ์ฌ์ฉ ( GridLayoutManage๊ด๋ จ ์ฝ๋๋ ํ๋จ์ ์ฒจ๋ถํ๊ฒ ์ต๋๋ค. )
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_follower"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
tools:itemCount="4"
tools:listitem="@layout/item_follower_list" />
2. FolloweData
data class FollowerData(
val name: String,
val introduction: String
)
3. FollowrAdapter
class FollowerAdapter : RecyclerView.Adapter<FollowerAdapter.FollowerViewHolder>() {
val userList = mutableListOf<FollowerData>()
class FollowerViewHolder(private val binding : ItemFollowerListBinding) : RecyclerView.ViewHolder(binding.root){
fun onBind(data: FollowerData) {
binding.tvName.text = data.name
binding.tvIntroduction.text = data.introduction
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FollowerViewHolder {
val binding = ItemFollowerListBinding.inflate(LayoutInflater.from(parent.context),parent,false)
return FollowerViewHolder(binding)
}
override fun onBindViewHolder(holder: FollowerViewHolder, position: Int) {
holder.onBind(userList[position])
}
override fun getItemCount(): Int = userList.size
}
FollowerFragment
class FollowerFragment : Fragment() {
private lateinit var follwerAdapter: FollowerAdapter
private var _binding: FollowerFragmentBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FollowerFragmentBinding.inflate(layoutInflater, container,false)
follwerAdapter = FollowerAdapter()
binding.rvFollower.adapter = follwerAdapter
follwerAdapter.userList.addAll(
listOf(
FollowerData("์ดํ๋น1", "์๋
ํ์ธ์"),
FollowerData("์ดํ๋น2", "์๋
ํ์ธ์"),
FollowerData("์ดํ๋น3", "์๋
ํ์ธ์"),
FollowerData("์ดํ๋น4", "์๋
ํ์ธ์")
)
)
follwerAdapter.notifyDataSetChanged()
return binding.root
}
override fun onDestroy() {
super.onDestroy()
_binding = null
}
}
HomeActivity
class HomeActivity : AppCompatActivity() {
private var postion = FIRST_POSITION
private lateinit var binding : ActivityHomeBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHomeBinding.inflate(layoutInflater)
setContentView(binding.root)
initTransactionEvent()
}
fun initTransactionEvent() {
val followerFragment = FollowerFragment()
val repositstoryFragment = RepositoryFragment()
supportFragmentManager.beginTransaction().add(R.id.container_rv, followerFragment).commit()
binding.btnFollower.setOnClickListener {
supportFragmentManager.beginTransaction().replace(R.id.container_rv, followerFragment) .commit()
}
binding.btnRepository.setOnClickListener {
supportFragmentManager.beginTransaction().replace(R.id.container_rv, repositstoryFragment) .commit()
}
}
companion object {
const val FIRST_POSITION = 1
}
}
xml ellipsize ์์ฑ
<TextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textColor="@color/black"
android:textSize="12sp"
android:ellipsize="end"
android:maxLines="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_title"
tools:text="๋ด์ฉ" />
RepositoryRecyclerView ์์ฑ
+)GridlayoutManager ์ฌ์ฉ
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_repository"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:itemCount="4"
android:layout_marginLeft="55dp"
android:layout_marginRight="30dp"
android:layout_marginTop="10dp"
tools:listitem="@layout/item_repository_list" />
์ํ๊ธฐ๊ฐ์ด๋ผ.. 2-2๋ง ๊ตฌํํ์ต๋๋ค.. ๋ค๋ฅธ ๋ถ๋ถ์ ์ฐจ์ฐจ ํด๋ณด๊ฒ ์ต๋๋ค...
Decoration
class Decoration(val colorString: String, val left: Int, val right: Int, val height: Int, val bottom:Int) : RecyclerView.ItemDecoration() {
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
super.onDraw(c, parent, state)
val paint = Paint().apply {
color = Color.parseColor(colorString)
}
for (i in 0 until parent.childCount) {
val child = parent.getChildAt(i)
if (i != parent.childCount - 1) {
c.drawRect(child.left.toFloat(), child.bottom.toFloat(), child.right.toFloat(), child.bottom.toFloat() + height, paint)
}
}
}
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
outRect.right = right
outRect.left = left
outRect.top = height
outRect.bottom = height
}
}
FollowerFragment
binding.rvFollower.addItemDecoration(Decoration("#F658A6", 50,50,25,25))
๐ค์ด๋ฒ ๊ณผ์ ๋ฅผ ํตํด ๋ฐฐ์ด ๋ด์ฉ & ์ฑ์ฅํ ๋ด์ฉ๐ค
โFragment์ ๋ํด ์ดํดํ์ต๋๋ค.
์ค์ต ์์ ๋ง ๋ฐ๋ผํ๊ณ ์ง์ ์ํ๋ ์์๋ฅผ ์ด๋ ค์ ๊ตฌํ์ ํด๋ณธ ์ ์ด ์์๋๋ฐ, ์ด๋ฒ ๊ธฐํ๋ฅผ ํตํด ์ง์ ์ฝ๋๋ฅผ ์์ฑํ๋ฉด์ ์ ๋๋ก ์ดํดํ ์ ์๋ ์๊ฐ์ ๊ฐ์ก์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ fragment์์ binding์ ์ฌ์ฉํ๋ ๋ฒ ๋ํ ์ตํ ์ ์์์ต๋๋ค. ์๋ช
์ฃผ๊ธฐ๋ฅผ ์ ๋๋ก ์ดํดํ์ง ๋ชปํ๋๋ฐ ์ด๋ฒ ๊ธฐํ๋ฅผ ํตํด ์ ๋๋ก ์ดํดํ ์ ์๊ฒ ๋์์ต๋๋ค.
โItemDecoration์ ์๊ฒ ๋์์ต๋๋ค.
์๋ xml์์ margin๊ฐ์ ๋ชจ๋ ์คฌ๋๋ฐ, ์ด๋ฒ ๊ณผ์ ๋ฅผ ํตํด ItemDecoration์ ์ฒ์ ์๊ฒ๋์์ต๋๋ค.
๊ทธ๋์ ์ ๊ฐ๋ด๋ ์์ง ๋ถ์กฑํ๊ณ .. ์ด์ํ.. ์ฝ๋์ง๋ง ๋ ์ด์ฌํ ๊ณต๋ถํด์.. ๋ค์์ฃผ์ ๋ ๋์ ์ฝ๋๋ก ๋ฐ๊ฟ์ค๊ฒ ์ต๋๋ค
3์ฃผ์ฐจ
SignUp | SignIn | Profile | Home | ViewPager |
---|---|---|---|---|
1-1. EditText์ selector ํ์ฉํ๊ธฐ
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/et_border_pink" android:state_focused="true"/>
<item android:drawable="@drawable/et_fill_gray" android:state_focused="false"/>
</selector>
1-2. ๋ฒํผ ๋ฑ๋ฑ Drawable๋ก ์ง์ ๋ง๋ค๊ธฐ
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/pink"/>
<corners
android:bottomRightRadius="5dp"
android:bottomLeftRadius="5dp"
android:topLeftRadius="5dp"
android:topRightRadius="5dp"/>
โButton์ selector ํ์ฉํ๊ธฐ๋ ์์ EditText์ selector ํ์ฉํ๊ธฐ์ ๊ฐ์ ๋ฐฉ์์ผ๋ก ์ฝ๋๋ฅผ ์์ฑํ์ต๋๋ค!
2-1. ์ด๋ฏธ์ง Glide์ CircleCrop ๊ธฐ๋ฅ์ ํ์ฉํด์ ๋ฃ์ด์ฃผ๊ธฐ
Glide.with(this)
.load("https://mblogthumb-phinf.pstatic.net/MjAxOTA0MjNfMjcy/MDAxNTU2MDIwNjg0ODMw.KwUiIDMhdpKzsuNX83GpdFljS1HjgNhCBNcXv2QXfxkg.ksHQVjDUTn8AMV4XVSfETLX-tZ1LTz9-bOmO0o7AtI8g.JPEG.ndh7782/%EC%B9%98%EC%A6%8801.JPG?type=w800")
.apply(RequestOptions.circleCropTransform())
.into(binding.ivProfile)
2-2. ์์ด์ฝ ์ด๋ฏธ์ง exportํด์ ์ฌ์ฉ
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/menu_profile"
android:icon="@drawable/ic_person_gray"
android:title="ํ๋กํ" />
<item
android:id="@+id/menu_home"
android:icon="@drawable/ic_home_gray"
android:title="ํ" />
<item
android:id="@+id/menu_camera"
android:icon="@drawable/ic_camera_gray"
android:title="์นด๋ฉ๋ผ" />
</menu>
2-3. ํ๋จ์ BottomNavigation ๋ฃ์ด์ฃผ๊ธฐ
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavigationView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
app:menu="@menu/menu_bottom"
app:itemIconTint="@drawable/selector_icon"
app:itemTextColor="@drawable/selector_icon"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
xml ellipsize ์์ฑ
<TextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textColor="@color/black"
android:textSize="12sp"
android:ellipsize="end"
android:maxLines="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_title"
tools:text="๋ด์ฉ" />
3-1 TabLayout + ViewPager2
<com.google.android.material.tabs.TabLayout
android:id="@+id/tl_follow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="200dp"
android:fontFamily="@font/noto_sans_kr_regular"
android:textFontWeight="500"
android:textSize="16sp"
app:tabIndicatorColor="@color/pink"
app:tabIndicatorHeight="3dp"
app:tabSelectedTextColor="@color/pink"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</com.google.android.material.tabs.TabLayout>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/vp_follow"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="13dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tl_follow" />
FollowerAdapter
Glide.with(itemView.context).load(data.photo)
.apply(RequestOptions.circleCropTransform())
.into(binding.ivProfile)
FollowerData
data class FollowerData(
val name: String,
val introduction: String,
val photo : String
)
FollowerFragment
follwerAdapter.userList.addAll(
listOf(
FollowerData("์คํฐ์ง๋ฐฅ", "์๋
ํ์ธ์", "https://ww.namu.la/s/bd52223e4d1f11fcc4c7f6506bf3321b26579bf118db6c1ca20492b9af4228a414edd25f1006baace220e4ca771288e0f38d6cbf253ae4e9d39aaf4b881600b0d65e518e7d94891837ee9a0c6a723aac0f4d2b7bf4a65b36bd1fe636aa49c632"),
FollowerData("๋ฑ์ด", "์๋
ํ์ธ์", "https://img.insight.co.kr/static/2020/08/12/700/fyzvinle3b068ce501hq.jpg"),
FollowerData("์ง๊ฒ์ฌ์ฅ", "์๋
ํ์ธ์", "https://pbs.twimg.com/media/D8RITHlV4AAb1iG.jpg")
)
)
๐ค์ด๋ฒ ๊ณผ์ ๋ฅผ ํตํด ๋ฐฐ์ด ๋ด์ฉ & ์ฑ์ฅํ ๋ด์ฉ๐ค
โ๋์์ธ์ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ์ตํ์ต๋๋ค
์ด๊ธฐ์ ์ง์ ๋ ์ด์์์ ์งฐ์ ๋์ ๋์์ด๋๋ถ๊ป์ ๋์์ธํด์ฃผ๋ ๊ฒ์ ๋ณด๋ฉฐ ์์ ์ ํด๋๊ฐ๋ฉด์ ๋์ค๋ ๊ฒฐ๊ณผ๋ฌผ์ ์ฐจ์ด๋ฅผ ๋ณด๊ณ ๋์์ด๋์ ์ค์์ฑ๊ณผ ํ์
์ ์ค์์ฑ์ ๊นจ๋ฌ์ ์ ์๋ ๊ณ๊ฐ๊ธฐ ๋์์ต๋๋ค.
๋ํ, ํผ๊ทธ๋ง๋ฅผ ์ ๋๋ก ๋ค๋ค๋ณธ ์ ์ด ํ๋ฒ๋ ์์๋๋ฐ, ํ์
์ ์์ด์ ํผ๊ทธ๋ง๋ฅผ ์ฐ๋ ๋ฒ์ ์ตํ ์ ์์์ต๋๋ค.
โViewPager2๋ฅผ ์ดํดํ์ต๋๋ค
์ดํ์ ์ฌ์ฉํ๋ฉด์ ๊ฐ์ฅ ๋ง์ด ๋ดค๋ ๊ธฐ๋ฅ ์ค ํ๋์๋๋ฐ, ์ด๋ฒ ์ธ๋ฏธ๋๋ฅผ ํตํด ViewPager2๋ฅผ ์ ์ ์์์ต๋๋ค.
๋ํ, TabLayout๋ฑ๋ฑ๋ ํจ๊ป ๋ฐฐ์ธ ์ ์์ด์ ๋ป๊น์์ต๋๋ค.
4์ฃผ์ฐจ
# 4๏ธโฃ Fourth WeekLogIn |
---|
PostMan ํ ์คํธ ์ด๋ฏธ์ง ์ฒจ๋ถ
POST | GET |
---|---|
RequestData
data class RequestLoginData(
@SerializedName("email")
val email: String,
val password: String
)
data class RequestSignUpData(
@SerializedName("email")
val email: String,
val name: String,
val password: String
)
ResponseData
data class ResponseLoginData(
val status: Int,
val success: Boolean,
val message: String,
val data: Data
) {
data class Data(
val id: Int,
val name: String,
val email: String
)
}
data class ResponseSignUpData(
val status: Int,
val success: Boolean,
val message: String,
val data: Data
) {
data class Data(
val id: Int,
val name: String,
val email: String
)
}
SampleService
interface SampleService {
@Headers("Content-Type: application/json")
@POST("user/login")
fun postLogin(
@Body requestLoginData: RequestLoginData
) : Call<ResponseLoginData>
}
interface SignUpService {
@Headers("Content-Type: application/json")
@POST("user/signup")
fun postSignUp(
@Body requestSignUpData: RequestSignUpData
) : Call<ResponseSignUpData>
}
ServiceCreator
<menu xmlns:android="http://schemas.android.com/apk/res/android">
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 sampleService : SampleService = retrofit.create(SampleService::class.java)
}
object SignUpCreator {
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 signUpService : SignUpService = retrofit.create(SignUpService::class.java)
}
SignInActivity
private fun initNetwork() {
val requestLoginData = RequestLoginData(
binding.etId.text.toString(),
binding.etPassword.text.toString()
)
val call : Call<ResponseLoginData> = ServiceCreator.sampleService.postLogin(requestLoginData)
call.enqueue(object : Callback<ResponseLoginData> {
override fun onResponse(
call: Call<ResponseLoginData>,
response: Response<ResponseLoginData>
) {
if(response.isSuccessful) {
Toast.makeText(this@SignInActivity, "${response.body()?.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
private fun initNetwork() {
val requestSignUpData = RequestSignUpData(
binding.etName.text.toString(),
binding.etId.text.toString(),
binding.etPassword.text.toString()
)
val call : Call<ResponseSignUpData> = SignUpCreator.signUpService.postSignUp(requestSignUpData)
call.enqueue(object : Callback<ResponseSignUpData> {
override fun onResponse(
call: Call<ResponseSignUpData>,
response: Response<ResponseSignUpData>
) {
if (response.isSuccessful) {
Toast.makeText(this@SignUpActivity, response.body()?.message, Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this@SignUpActivity, "ํ์๊ฐ์
์คํจ", Toast.LENGTH_SHORT).show()
}
}
override fun onFailure(call: Call<ResponseSignUpData>, t: Throwable) {
Log.e("NetworkTest", "error: $t")
}
})
}
๐ค์ด๋ฒ ๊ณผ์ ๋ฅผ ํตํด ๋ฐฐ์ด ๋ด์ฉ & ์ฑ์ฅํ ๋ด์ฉ๐ค
โ์๋ฒ๋ฅผ ์ตํ์ต๋๋ค
์๋ฒ๊ฐ ๋ญ์ง ์ ๋ง ํ๋๋ ๋ชจ๋ฅด๊ณ ๋ค ์ฒ์ ๋ณด๋ ์ฝ๋์ ๊ฐ๋
ํฌ์ฑ์ด์ด์ ์กฐ๊ธ์ ์ด๋ ต๊ฒ ๋๊ปด์ก์์ต๋๋ค ๐ฅ
๊ทธ๋๋! ์ค์ต๊ณผ ๊ณผ์ ๋ฅผ ํตํด... ์์ฃผ ์กฐ๊ธ์.. ์ดํด๋ฅผ ํ ๊ฒ ๊ฐ๋ค๋ ์๊ฐ์ด ๋ญ๋๋ค!
๋ ๋ณต์ตํ๊ณ ๊ณต๋ถํ๋ฉด์ ์ ๋๋ก ์ดํดํ๊ณ ๋์ด๊ฐ๊ฒ ์ต๋๋ค!
7์ฃผ์ฐจ
onBoarding | autoLogin | autoLogin cancel | backstack |
---|---|---|---|
๐คview๋ notion์ ์ฐธ๊ณ ํ์ฌ ์ ์ํ์ต๋๋ค๐ค
1-1. ์จ๋ณด๋ฉ ํ๋ฉด ๋ง๋ค๊ธฐ
nav_onboarding.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_onboarding"
app:startDestination="@id/onboardingFragment1">
<fragment
android:id="@+id/onboardingFragment1"
android:name="com.example.myapplication.view.onboarding.OnboardingFragment1"
android:label="fragment_onboarding1"
tools:layout="@layout/fragment_onboarding1" >
<action
android:id="@+id/action_onboardingFragment1_to_onboardingFragment2"
app:destination="@id/onboardingFragment2" />
</fragment>
<fragment
android:id="@+id/onboardingFragment2"
android:name="com.example.myapplication.view.onboarding.OnboardingFragment2"
android:label="OnboardingFragment2" >
<action
android:id="@+id/action_onboardingFragment2_to_onboardingFragment3"
app:destination="@id/onboardingFragment3" />
</fragment>
<fragment
android:id="@+id/onboardingFragment3"
android:name="com.example.myapplication.view.onboarding.OnboardingFragment3"
android:label="OnboardingFragment3">
<action
android:id="@+id/action_onboardingFragment3_to_signInActivity"
app:destination="@id/signInActivity" />
</fragment>
<activity
android:id="@+id/signInActivity"
android:name="com.example.myapplication.view.login.SignInActivity"
android:label="activity_main"
tools:layout="@layout/activity_signin" />
</navigation>
OnboardingFragment1
๊ฐ ํ๋ ๊ทธ๋จผํธ๋ณ๋ก buttonEvent๋ฅผ ์์ฑํ์ต๋๋ค
fun initBtnEvent() {
binding.btnNext.setOnClickListener {
findNavController().navigate(R.id.action_onboardingFragment1_to_onboardingFragment2)
}
}
1-2. SharedPreferences ํ์ฉํด์ ์๋๋ก๊ทธ์ธ/์๋๋ก๊ทธ์ธ ํด์
activity_cancel_auto_login
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".view.profile.CancelAutoLoginActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:background="@color/pink"
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/tv_setting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="30dp"
android:layout_marginBottom="30dp"
android:fontFamily="@font/noto_sans_kr_regular"
android:text="ํ๊ฒฝ์ค์ "
android:textColor="@color/white"
android:textFontWeight="800"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/tv_off_auto_login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
android:fontFamily="@font/noto_sans_kr_regular"
android:text="์๋๋ก๊ทธ์ธ ํด์ "
android:textColor="@color/maingray"
android:textFontWeight="400"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/constraintLayout" />
<LinearLayout
android:id="@+id/ll_line"
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_marginTop="20dp"
android:background="@color/lightgray"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_off_auto_login" />
</androidx.constraintlayout.widget.ConstraintLayout>
SiginInActivity
private fun initClickEvent() {
binding.ivAutoLogin.setOnClickListener {
binding.ivAutoLogin.isSelected = !binding.ivAutoLogin.isSelected
SOPTSharedPreferences.setAutoLogin(this, binding.ivAutoLogin.isSelected)
}
}
private fun isAutoLogin() {
if(SOPTSharedPreferences.getAutoLogin(this)) {
shortToast("์๋๋ก๊ทธ์ธ ์๋ฃ")
startActivity(Intent(this, HomeActivity::class.java))
finish()
}
}
CancelAutoLoginActivity
private fun initClickEvent() {
binding.tvOffAutoLogin.setOnClickListener {
val settings: SharedPreferences = getSharedPreferences("USER_AUTH", MODE_PRIVATE)
val editor: SharedPreferences.Editor = settings.edit()
editor.remove("int")
editor.clear()
editor.commit()
}
}
1-3 ๋ณธ์ธ์ด ์ฌ์ฉํ๋ Util ํด๋์ค ์ฝ๋ ๋ฐ ํจํค์ง ๋ฐฉ์
util์์๋ ์ธ๋ฏธ๋ ์๊ฐ์ ๋ค๋ฃฌ shortToast๋ฅผ ์ ์ํด๋ ์ํ์
๋๋ค.
์์ฃผ ์ฌ์ฉํ๋ ํ ์คํธ ๋ฉ์์ง ๊ธฐ๋ฅ์ ํด๋์ค๋ก ๋ง๋ค์ด ์ฌ๋ฌ ๊ณณ์์ ์ฌ์ฉํ ์ ์์์ต๋๋ค.
fun Context.shortToast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
- ํจํค์ง ๋ฐฉ์
-์์ฃผ ์์ฑ๋๋ ํด๋์ค์ธ adapter, api, data, util, view๋ฅผ ์ค์ฌ์ผ๋ก ํ์ฌ ํจํค์ง๋ฅผ ๋๋ด์ต๋๋ค.
โฃ ๐adapter
โฃ ๐api
โฃ ๐data
โฃ ๐util
โ ๐view
โฃ ๐camera
โฃ ๐home
โฃ ๐login
โฃ ๐onboarding
โ ๐profile
2-1. NavigationComponent์์ BackStack๊ด๋ฆฌ
<fragment
android:id="@+id/onboardingFragment2"
android:name="com.example.myapplication.view.onboarding.OnboardingFragment2"
android:label="OnboardingFragment2" >
<action
android:id="@+id/action_onboardingFragment2_to_onboardingFragment3"
app:destination="@id/onboardingFragment3"
app:popUpTo="@id/onboardingFragment2"
app:popUpToInclusive="true"/>
</fragment>
๐ค์ด๋ฒ ๊ณผ์ ๋ฅผ ํตํด ๋ฐฐ์ด ๋ด์ฉ & ์ฑ์ฅํ ๋ด์ฉ๐ค
โnavigation
๋ณดํต ์ดํ์ ์คํํ์๋ ์์ฃผ ์ ํ๋ ์จ๋ณด๋ฉ ํ๋ฉด์ ๋ง๋ค ์ ์๋ ๊ธฐํ๋ผ์ ๋ป๊น๊ณ ์์ผ๋ก ๋ง์ด ํ์ฉ๋ ๊ฒ ๊ฐ์ ๊ต์ฅํ ๋ง์ด ๋ฐฐ์ด ๊ฒ ๊ฐ์ต๋๋ค.
โutil์ ๋ํด ์ดํดํ์ต๋๋ค
ํญ์ ์์ฃผ ์ฌ์ฉํ๋ ๋ถ๋ถ์์ ๊ผญ ์ ๋ ๊ฒ ์จ์ผํ๋๋ผ๋ ๊ฒ์ ๋ง์ด ์๊ฐํ๋๋ฐ, ์ด๋ฒ ์ธ๋ฏธ๋์ ๊ณผ์ ๋ฅผ ํตํด util์ ๋ฐฐ์ฐ๊ณ , ๋ฐ๋ณต๋๋ ์ฝ๋๋ฅผ ๋จ์ํ ํ ์ ์๋ ๊ธฐํ๊ฐ ๋์์ต๋๋ค.
๐package๊ธฐ๋ฅ์ ๋ํด ์ดํดํ์ต๋๋ค
ํด๋์ค๊ฐ ๋๋ฌด ๋ง์์ ์ด์ ๋ญ๊ฐ ๋ญ์ง ํท๊ฐ๋ฆฌ๊ธฐ ์์ํ๋๋ฐ, ์ด๋ ๋ฑ ํจํค์ง์ ๋ํด์ ๋ฐฐ์ฐ๊ณ ํ์ฉํด๋ณด๋ ๊ธฐํ๊ฐ ๋์์ต๋๋ค! ์์ผ๋ก ์ ๊ฐ ์ ์ํ ํจํค์ง ๋ฐฉ์์ ๋ฐ๋ผ ์ฌ์ฉํ๋ฉด ๋์ค์๋ ๋ณด๋ค ํ๋ก์ ํธ๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ข์ ๊ฒ ๊ฐ๋ค๋ ์๊ฐ์ด ๋ค์์ต๋๋ค.