BY 최호준, 김유나, 양혜선
implementation 'com.google.android.material:material:1.0.0'
implementation 'com.android.support:design:29.0.0'
//리사이클러뷰
implementation 'androidx.recyclerview:recyclerview:1.0.0'
//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'
//이미지 로딩
implementation 'com.github.bumptech.glide:glide:4.10.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0'
//동그란 이미지 커스텀 뷰 라이브러리 : https://github.com/hdodenhof/CircleImageView
implementation 'de.hdodenhof:circleimageview:3.0.1'
//indecator 효과
implementation 'com.tbuonomo.andrui:viewpagerdotsindicator:4.1.2'
//multi radiobutton
implementation 'com.yuxingxin.multiradiogroup:library:1.0.0'
//Picaso 라이브러리 (이미지)
implementation 'com.squareup.picasso:picasso:2.4.0'
//페이스북 연동
implementation 'com.facebook.android:facebook-login:[5,6)'
//socket.io 라이브러리
implementation('io.socket:socket.io-client:1.0.0') {
exclude group: 'org.json', module: 'json'
}
프로그램 구조는 크게 data, network, ui로 패키징하여 진행하였다.
network는 다음과 위와 같이 BASE_URL을 가진 싱글톤과, 해당 부분 구현시 이용할 interface,
Kotlin Extension Function을 이용한 enqueue 메소드가 있다.
ui도 나름대로 패키징을 하였으나, 아직 뷰 구현이 진행중이라 구조가 명확하지는 않다. 하지만 로그인,회원가입에 대한 부분과 하단 탭바를 눌렀을 때 전환되는 5가지 프래그먼트가 있고, 해당 프래그먼트에서 연동되는 액티비티같은 경우 그 패키지 속에 분류를 하여 진행하고있다.
어플 실행시 배경의 변화, 로그인 / 회원가입 시 EditText변화 등의 애니매이션을 적용하였다. ObjectAnimator의 ALPHA, TRANSLATE 속성 등을 이용하였다.
kotlin extension을 이용하여 작성된 enqueue메소드와 sendToast라는 메소드를 적용하였다. 위 이미지에서는 이메일 중복 체크, 로그인 요청에 enqueue 함수를 이용하였고, 이메일 중복체크 결과를 띄우는 토스트 또한 Toast의 함수를 확장시켜 sendToast를 이용하였다. (기존 코드의 반복되는 요소들을 줄일 수 있었다.)
ex 1) 토스트를 만들어 사용하는 경우 val toast = Toast.makeText(this,"멘트",Toast.LENTH_SHORT) toast.show()
를 kotlin extension 을 이용하여 생성한 함수 sendToast()를 이용하여 sendToast("멘트") 를 작성해주기만 하면 된다.
ex 2) enqueue 메소드
ex 3) textview 가격을 1000원 -> 1,000원 으로 바꾸어주었다.
ex 4) editText 가격 입력시 자동으로 ,(콤마) 넣어주었다.
가독성을 높이기 위해 위와 같이 람다식을 활용하였다.
implementation 'com.tbuonomo.andrui:viewpagerdotsindicator:4.1.2' 을 이용해 추가한 Indicator를 뷰페이져와 연동시켜 전환 효과를 적용시켰다.
또한 매거진의 세로 스크롤 기능 또한 Vertical ViewPager 검색을 통해 기존의 뷰페이저를 변형한 커스텀을 적용시켰다.
위에 사용된 리사이클러뷰는 세미나때 배운 내용을 바탕으로 적용되었다.
-
홈화면의 상단 뷰페이져를 제외한 목록들
-
마이페이지의 최근 본 상품, 좋아요/팔로우 리스트
https://re-build.tistory.com/11 해당 사이트 및 facebook developer 사이트를 참고하여 다음과 같이 페이스북 로그인 연동을 하였다. 이와 더불어 SharedPreferences를 활용해 자동로그인 및 로그아웃에 대한 이벤트 처리를 구현하였다. 아래는 해당 화면이다.
구글 머티리얼 디자인 가이드와 구글링을 통해 recyclerview의 아이템 클릭시 액티비티로 전환될 때 애니매이션을 구현하였다. 아래는 해당 화면과 코드이다.
옷을 선택할 때는 RadioButton을 이용하였다. 이 과정에서 RadioGroup은 기본으로 제공되는 상태로는 두 줄로 나열 할 수 없었기에 implementation 'com.yuxingxin.multiradiogroup:library:1.0.0' 을 통해 이를 가능하게 해 주었다. Bottom sheet Behavior 속성 중 hideable="true"를 통해 드래그해서 필터 뷰를 내릴 수 있도록 구현해주었다.
아래 사진을 보면 3개정도 이상의 아이템이 있는 경우 +3과 같은 요약 표시를 해줘야 할 필요가 있었다. 해당 기능에 대해 kotlin의 collection 기능을 이용하였고, 이에 대한 코드는 다음과 같다.
경매가 끝나는 시간을 서버에 받아온 뒤 경매를 진행 중인 시간을 계산하여 해당 경매 아이템 뷰에 흘러가는 시간을 표시했다. 해당 기능에 대해 kotlin에 존재하는 CountDownTimer 객체를 이용하여 1초마다 시간이 줄어가는 것을 표시했다. 이에 대한 코드는 다음과 같다.
fun countDownTimer(long: Long)
{
var countDownTimer = object : CountDownTimer(long*1000, 1000)
{
override fun onFinish() {
tv_auction_detail_extra_time.text = "경매가 마감되었습니다."
tv_auction_detail_extra_text.text = ""
// sendToast("경매가 마감되었습니다.")
}
override fun onTick(p0: Long) {
val hours = p0.div(3600000)
var temp = p0/1000 - hours*3600
val min = p0.div(60000).toInt() - p0.div(3600000)*60
val seconds = temp - min*60
if ( hours < 10)
{
tv_auction_detail_extra_time.text = "0" + hours.toString()+ " : "+ min.toString() + " : " + seconds.toString()
if( min < 10)
{
tv_auction_detail_extra_time.text = "0" + hours.toString()+ " : "+ "0" + min.toString() + " : " + seconds.toString()
}
}
else
{
if(min < 10)
{
tv_auction_detail_extra_time.text = hours.toString()+ " : "+ "0" + min.toString() + " : " + seconds.toString()
}
tv_auction_detail_extra_time.text = hours.toString()+ " : "+ min.toString() + " : " + seconds.toString()
}
}
}
countDownTimer.start()
}
상품을 장바구니에 담으면 상품이 장바구니에 담기게 되고, 장바구니 뷰에서 상품을 삭제하면 장바구니에서 상품이 삭제되는 기능을 구현했다. 또한 장바구니에서 전체선택 체크박스를 통해 상품들을 모두 다 선택 하기를 원할 시 전체 선택이 되고, 만약 하나라도 상품의 체크박스가 체크가 풀릴 시 전체선택이 풀리게 구현하였다.
//액티비티 내의 코드
cb_cart_check_all.setOnClickListener{
if(cb_cart_check_all.isChecked){
cartGoodsAdapter.isAllSelected = true
cartGoodsAdapter.notifyDataSetChanged()
}else{
cartGoodsAdapter.isAllSelected = false
cartGoodsAdapter.notifyDataSetChanged()
}
}
//체크박스 들어있는 RecyclerView의 뷰홀더에 해당하는 부분
if(isAllCheck){
cb_cart_goods.isChecked = true
}else{
cb_cart_goods.isChecked = false
}
cb_cart_goods.setOnCheckedChangeListener{
buttonView, isChecked ->
if(isChecked){
ctx.count++
ctx.checkItem()
ctx.selected_cart_list!!.add(goodsData)
}else{
ctx.count--
ctx.checkItem()
ctx.selected_cart_list!!.remove(goodsData)
ctx.cb_cart_check_all.isChecked = false
}
}
회원가입 성공 후, 각각의 유저에 취향에 따른 상품을 추천해주기 위해, 유저가 마음에 드는 사진을 3개 이상 선택하게 한다. 유저의 취향이 서버를 통해 분석이 되어지면, 그에 맞는 키워드를 가진 셀러(플럽)을 추천해주고, 취향에 맞는 스타일의 옷들이 홈화면에 나오게 된다.
유저 취향에 따른 옷들이 최신순으로 둘러보기 화면에 나오게 되고, 필터 아이콘을 선택하여 옷의 종류, 색깔, 스타일, 사이즈,등 을 선택하게 되면 상품 DB에 존재하는 모든 옷들 중 유저가 고른 필터에 따른 옷들만 화면에 존재하게 된다.
유저가 마음에 드는 사진을 클릭시 customToast를 통해 하트 이미지가 화면 중앙에 뜨게 되고 해당 상품의 하단 하트 버튼이 채워지게 된다.
class MySharedPreferences(context: Context) {
val PREFS_FILENAME = "prefs"
val PREF_KEY_MY_LOCAL_LOGIN_TOKEN = "local_login"
val PREF_KEY_MY_LOCAL_LOGIN_ID = "local_login_id"
val prefs: SharedPreferences = context.getSharedPreferences(PREFS_FILENAME,0)
val editor = prefs.edit()
/* 파일 이름과 EditText를 저장할 Key 값을 만들고 prefs 인스턴스 초기화 */
/* get/set 함수 임의 설정. get 실행 시 저장된 값을 반환하며 default 값은 false
* set(value) 실행 시 value로 값을 대체한 후 저장 */
var local_login_token : String?
get() = prefs.getString(PREF_KEY_MY_LOCAL_LOGIN_TOKEN,null)
set(value) = editor.putString(PREF_KEY_MY_LOCAL_LOGIN_TOKEN,value).apply()
}
IO.Option에서 query옵션: url에 직접 쿼리 매개 변수를 제공한다.
bid 이벤트를 통해 서버와 통신한다.