1️⃣Week
2️⃣Week
3️⃣Week
4️⃣Week
7️⃣Week
SignInActivity , SignUpActivity, HomeActivity
binding.btnSign.setOnClickListener{
startActivity(intent)
}
binding.btnLogin.setOnClickListener{
if (binding.etId.text.toString().equals(" " )|| binding.etId2.text.toString().equals(" " )) {
Toast .makeText(this , " 로그인 실패" , Toast .LENGTH_LONG ).show()
}
else {
Toast .makeText(this , binding.etId.text.toString() + " 님 환영합니다." , Toast .LENGTH_SHORT )
.show()
startActivity(intent2)
}
}
setContentView(binding.root)
}
}
공백임을 equals()를 통해 구현 (empty여부로 구현도 가능)
로그인 시 아이디 혹은 비밀번호 칸 중 하나라도 입력되지 않았으면 toast.message로 "로그인 실패" 알림
로그인 성공하면 아이디 edittext 값을 string으로 가져와 "아이디+ 환영합니다"로 알림
로그인 성공 -> 클릭하면 HomeActivity 자기소개 화면으로 화면 이동
❗ binding을 scope으로 묶지 않았지만 다음 과제부터 진행해볼 것!!
binding= ActivitySignUpBinding .inflate(layoutInflater)
binding.btnsignup.setOnClickListener{
if (binding.etId.text.toString().equals(" " )|| binding.etId2.text.toString().equals(" " )|| binding.etName.text.toString().equals(" " )) {
Toast .makeText(this , " 입력되지 않은 정보가 있습니다." , Toast .LENGTH_LONG ).show()
}
else
finish()
}
setContentView(binding.root)
}
공백임을 equals()를 통해 구현 (empty여부로 구현도 가능)
아이디, 비밀번호, 이름 중 하나라도 입력되지 않았으면 toast.message로 "입력되지 않은 정보 존재" 알림
intent가 아닌 finish()로 스택에 화면이 쌓이지 않도록 구현
❗ binding을 scope으로 묶지 않았지만 다음 과제부터 진행해볼 것
❗ 변수를 만들어 if조건문 간결하게 구현해볼 것
로그인-홈
회원가입
로딩이 오래걸려 자판이 보이지 않는 점 양해부탁드려오😥
* constraintlayout의 특징을 알게되며 다른 layout보다 자유자재로 item이나 textview등을 배치할 수 있는 장점을 알게됨.
```
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="2dp"
android:color="#FF00CC" />
<corners android:radius="4dp"/>
<solid
android:color="#FFFFFF" />
</shape>
```
1. binding scope으로 구현하여 간결하게 코드짜기
3. 앞으로 level 2, level 3 과제 하기
Home화면
* 목록을 나타내기 위해서 전에 개발할 때는 recyclerview활용하지 않았는데 이번 과제를 통해 재생산이 되는 이 뷰 장점을 알게 됨.
* Fragment를 활용하면 한 화면에 다양한 동작을 갖는 뷰를 구현할 수 있다는 점을 알게 됨.
* Adapter : viewHolder를 생성하고 ItemLayout을 ViewHolder에 넘겨준다.
* onBind() 함수는 ViewHolder가 가진 View에 Adapter로 부터 전달받은 데이터를 붙여준다.
* 시험기간이라 역시나,,,LV.1 과제만 해서 아쉽,,, 셤끝나고 LV2,LV3 도전 ❗
1. Adatper와 Fragment 그리고 layout에 대해 복습할 것
2. level 2, level 3 과제 하기
< selector xmlns: android= " http://schemas.android.com/apk/res/android" >
< item android: color= " @color/black" android: state_pressed= " false" / >
< item android: color= " @color/white" android: state_pressed= " true" / >
< / selector>
< selector xmlns: android= " http://schemas.android.com/apk/res/android" >
< item android: drawable= " @drawable/button_fill_gray" android: state_pressed= " false" / >
< item android: drawable= " @drawable/button_fill_color" android: state_pressed= " true" / >
< / selector>
< selector xmlns: android= " http://schemas.android.com/apk/res/android" >
< item android: drawable= " @drawable/rectangle_fill_gray" android: state_focused= " false" / >
< item android: drawable= " @drawable/rectangle_border_pink" android: state_focused= " true" / >
< / selector>
private fun initAdapter (){
val fragmentList= listOf (HomeFollowerFragment (),HomeFollowingFragment ())
homeFollowViewPagerAdapter= HomeFollowViewPagerAdapter (this )
homeFollowViewPagerAdapter.fragments.addAll(fragmentList)
binding.vpHomefragment.adapter= homeFollowViewPagerAdapter
}
private fun initTabLayout (){
val tabLabel= listOf (" 팔로잉" ," 팔로워" )
// tablayout이랑 viepager2 연결
TabLayoutMediator (binding.homeTablayout,binding.vpHomefragment){
tab,position->
tab.text= tabLabel[position]
}.attach()
}
중첩스크롤 문제 해결 -> NestedScrollableHost
Follower__각각 아이템에 이미지 url 활용하여 다른 이미지 보여주기
FollowerAdapter, UserData, FollowerFragment
(1).먼저 UserData에 "val Img : String" 변수 추가해주기
(2).FollowerAdatper : Glide.circleCrop()으로 사진 둥글게 나타내기 , into()로 팔로우 프로필과
class FollowerViewHolder (private val binding : ItemSampleListBinding ) :
RecyclerView .ViewHolder (binding.root) {
fun onBind (data : UserData ) {
binding.tvName.text = data.name
binding.tvIntroduce.text = data.introduction
Glide .with (binding.root)
.load(data.Img )
.circleCrop()
.into(binding.ivProfile)
}
}
private fun initAdapter () {
// adapter 초기화
followerAdapter = FollowerAdapter ()
// adatper와 recyclerview 연동
binding.rvFollower.adapter = followerAdapter
val img1 = " http://image.cine21.com/resize/cine21/still/2019/0624/16_07_21__5d1076a9eca7f[W578-].JPG"
val img2 = " http://image.cine21.com/resize/cine21/still/2019/0624/16_15_30__5d1078925c7c5[W578-].jpg"
val img3 = " https://search.pstatic.net/common/?src=http%3A%2F%2Fshop1.phinf.naver.net%2F20210118_180%2F161094482719298FEG_JPEG%2FB01MU764R0_9.jpg&type=sc960_832"
val img4 = " https://search.pstatic.net/common/?src=http%3A%2F%2Fblogfiles.naver.net%2F20160902_16%2Fdynamote_1472795070315jifGn_JPEG%2F1.jpeg&type=sc960_832"
val img5 = " img url(길어서 생략)"
val img6 = " img url(길어서 생략)"
followerAdapter.userList.addAll(
listOf (
UserData (img1," 김수빈" , " 안드로이드" ),
UserData (img2," 문다빈" , " 안드로이드 파트장" ),
UserData (img3," 김현아" , " 기획 파트장" ),
UserData (img4," 이성현" , " 디자인 파트장" ),
UserData (img5," 김우영" , " 서버 파트장" ),
UserData (img6," 김의진" , " 웹 파트장" )
)
)
followerAdapter.notifyDataSetChanged()
}
profile화면에서 repository, follower화면 확인 + home화면에서 팔로잉, 팔로워 확인. 각각 아이템에 이미지 url첨부 + 중첩스크롤 해결
week3실행화면
📌 이거..왜 안바뀔까요오???ㅜㅜㅜㅜ 😂😢😂😢
button selected 여부로 코드 구현했는데 실행하면 에뮬레이터에서 적용이 안됩니다요.... 어디가 틀렸는지 알수가없숩니다...알려주쎄여... 이부분만 2시간동안 째려봐써여👀
클릭할 때만 바뀝니다...유지가 안된다고할까..??
Button색상 안바뀜
```kotlin
private fun initTransactionEvent(){
val followerFragment = FollowerFragment()
val repositoryFragment = RepositoryFragment()
childFragmentManager.beginTransaction().add(R.id.container_view, followerFragment).commit()
binding.btnFollower.isSelected = true //처음 화면 보여질 시에
binding.btnFollower.setOnClickListener{
childFragmentManager.beginTransaction()
.replace(R.id.container_view, followerFragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.commit()
binding.btnFollower.isSelected=true
binding.btnRepo.isSelected=false
}
binding.btnRepo.setOnClickListener {
childFragmentManager.beginTransaction()
.replace(R.id.container_view,repositoryFragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.commit()
binding.btnRepo.isSelected=true
binding.btnFollower.isSelected=false
}
}
```
* TapLayout의 기능과 ViewPager2의 특징을 알게됨 (특히 ViewPager와 ViewPager2의 차이)
* ViewPager2는 리사이클러뷰 기반으로 동작하고 notifyDataChanged()통해 UI 업데이터 가능함. 그리고 수평, 수직 스크롤 모두 지원!
* Fragment안에 Fragment 구현시에 코드가 달라진다. (아래코드첨부)
* childFragmentManager.beginTransaction().add(R.id.container_view, followerFragment).commit() --> ProfileFragment에서 구현시에 사용.
* 디자인 font첨부와 이미지 url 그리고 navigation 하단바 - 구현할 때는 조금...귀찮거 힘든데 막상 만들어 보면 앱다운 앱같다 ㅎㅎ
* 이제 DataBinding을 공부해서 더 간결하고 육안으로 보기 편한 코드를 짜봐야겠다!!!!!
* 버튼 클릭시 색 변환이... 코드 로직은 틀린 거 없어 보이는데 왜 적용이 안될까.... 이 문제에서만 2시간 붙잡고 있었는데 해결못했따...또르륵😥
1. ViewPager2, TapLayout 정리하기
2. level 3 과제 하기 +DataBinding으로 코드 refactor하기
SignUp - Retrofit Interface
서버와의 의사소통 방식, 일종의 상호작용 방법을 정의
interface SignUpService {
@Headers(" Content-Type: application/json" )
@POST(" user/signup" )
fun postSignup (
@Body body : RequestSignUpData
) :Call <ResponseSignUpData >
}
SignUp - request/response 객체 설계
data class RequestSignUpData (
@SerializedName(" email" )
val id : String ,
val name : String ,
val password : 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
)
}
SignUp - Callback 등록하여 통신 요청
Call 객체의 비동기 작업 이후 작업이 끝날때 할 행동을 Callback 객체로 표현
private fun initNetwork () {
val requestSignupData = RequestSignUpData (
id = binding.etId.text.toString(),
name = binding.etName.text.toString(),
password = binding.etId2.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?.email} 님 회원가입이 완료되었습니다!" , Toast .LENGTH_SHORT ).show()
} else
Toast .makeText(this @SignUpActivity, " 회원가입에 실패하셨습니다" , Toast .LENGTH_SHORT ).show()
}
override fun onFailure (call : Call <ResponseSignUpData >, t : Throwable ) {
Log .e(" NetworkText" , " error:$t " )
}
})
}
SignIn - Callback 등록하여 통신 요청
회원가입과 data, interface 유사하므로 skip
call 객체에 enqueue를 호출하여 실제 서버통신을 비동기적으로 요청, 만약 body()에 값이 없을 경우 or response.isSuccessful이 false인 경우 서버통신 실패
private fun initNetwork () {
val requestSignInData = RequestSignInData (
id = binding.etId.text.toString(),
password = binding.etId2.text.toString()
)
val call: Call <ResponseSignInData > = ServiceCreator .signinService.postLogin(requestSignInData)
call.enqueue(object : Callback <ResponseSignInData > {
override fun onResponse (
call : Call <ResponseSignInData >,
response : Response <ResponseSignInData >
) {
if (response.isSuccessful) {
val data = response.body()?.data
Toast .makeText(this @SignInActivity, " ${data?.email} 님 반갑습니다!" , Toast .LENGTH_LONG ).show()
startActivity(Intent (this @SignInActivity, HomeActivity ::class .java))
} else
Toast .makeText(this @SignInActivity, " 로그인에 실패하셨습니다" , Toast .LENGTH_LONG ).show()
}
override fun onFailure (call : Call <ResponseSignInData >, t : Throwable ) {
Log .e(" NetworkTest" , " error:$t " )
}
})
}
week4실행화면
* retrofit interface, resoponse/request 객체 구현
* Callback 등록하여 통신 요청하는 방법 / 비동기 작업
NavigationComponent 활용하여 온보딩 화면 만들기
fragment1,2,3으로 SOPT Hub 로그인 화면 전까지 온보딩 구현
각 fragment 전환은 버튼을 눌러 이동
< 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_sample"
app: startDestination= " @id/firstFragment" >
< fragment
android: id= " @+id/firstFragment"
android: name= " com.example.androidsopt.view.onBoarding.FirstFragment"
android: label= " fragment_first"
tools: layout= " @layout/fragment_first" >
< action
android: id= " @+id/action_firstFragment_to_secondFragment"
app: destination= " @id/secondFragment" / >
< / fragment>
< fragment
android: id= " @+id/thirdFragment"
android: name= " com.example.androidsopt.view.onBoarding.ThirdFragment"
android: label= " fragment_third"
tools: layout= " @layout/fragment_third" >
< action
android: id= " @+id/action_thirdFragment_to_signInActivity"
app: destination= " @id/signInActivity" / >
< / fragment>
< fragment
android: id= " @+id/secondFragment"
android: name= " com.example.androidsopt.view.onBoarding.SecondFragment"
android: label= " fragment_second"
tools: layout= " @layout/fragment_second" >
< action
android: id= " @+id/action_secondFragment_to_thirdFragment"
app: destination= " @id/thirdFragment" / >
< / fragment>
< activity
android: id= " @+id/signInActivity"
android: name= " com.example.androidsopt.view.Login.SignInActivity"
android: label= " activity_sign_in"
tools: layout= " @layout/activity_sign_in" / >
< / navigation>
SharedPreferences 활용하여 자동로그인
object SharedPreferences {
private const val STORAGE_KEY = " USER_AUTH"
private const val AUTO_LOGIN = " AUTO_LOGIN"
fun getAutoLogin (context : Context ): Boolean {
val preferences = context.getSharedPreferences(" USER_AUTH" ,Context .MODE_PRIVATE )
return preferences.getBoolean(AUTO_LOGIN , false )
}
fun setAutoLogin (context : Context , value : Boolean ) {
val preferences = context.getSharedPreferences(" USER_AUTH" ,Context .MODE_PRIVATE )
preferences.edit()
.putBoolean(AUTO_LOGIN , value)
.apply ()
}
SettingActivity - 자동로그인 해제(tvCancel) 클릭 시 remove, clear() 자동로그인 해제
private fun initClickEvent () {
binding.tvCancel.setOnClickListener {
val sp : SharedPreferences = getSharedPreferences(" USER_AUTH" , MODE_PRIVATE )
val editor : SharedPreferences .Editor = sp.edit()
editor.remove(" USER_AUTH" )
editor.clear()
editor.commit()
}
}
직관적으로 쉽게 알아볼 수 있도록 data/network/util/view 로 패키지를 생성
이때 view폴더에 각 구현한 Activity/Fragment 단위로 하위 패키지를 생성
┣ 📂adapter
┣ 📂util
┣ 📂network : 서버통신관련
┣ 📂data
┣ 📂view
┣ 📂Home
┣ 📂Profile
┣ 📂Login
┣ 📂Camera
┗ 📂onBoarding
week7 온보딩
자동로그인
자동로그인 알림
자동로그인 해제
* 온보딩 정의 및 NavigationComponent활용
* 자동로그인 로직 , SharedPreferences
* 패키징의 중요성 --> 한눈에 잘보여 파일 찾을 때 매우매우 편리함!!!