-
Notifications
You must be signed in to change notification settings - Fork 2
Main 홈, 스크롤
yunso edited this page Jan 15, 2021
·
5 revisions
- 해당 디렉토리로 이동하기
-
viewPager2
를 이용해 2개의 프래그먼트를 vertical scroll로 연결하였습니다. -
ScrollFragment
에서 스크롤시viewPager2
가 스크롤되는 것을 막기 위하여NestedScrollableHost
를 이용해 자식 뷰의 스크롤을 우선 인식할 수 있도록 설정했습니다.
fragment_home.xml
<com.example.momo_android.util.ui.NestedScrollableHost
android:id="@+id/nestedScrollableHost"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView_gradient"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never"
tools:listitem="@layout/item_scroll_gradient" />
</com.example.momo_android.util.ui.NestedScrollableHost>
- 현재 시간을 바탕으로 06:00 ~ 18:59 까지는 Day mode,
그 이외의 시간에는 Night mode의 UI를 보여줍니다.
HomeFragment.kt
private fun setDayNightStatus() {
val currentHourDay = Calendar.getInstance().get(Calendar.HOUR_OF_DAY) // 24시간 포맷
if (currentHourDay in 6..18) { // 06:00 ~ 18:59
setDayView()
isDay = true
} else {
setNightView()
isDay = false
}
setLoadingViewBackground()
}
- 사용자가 현재 일자에 업로드한 일기가 없을 경우, 비어있는 홈 화면을 보여주고
현재 일자에 이미 일기를 업로드했을 경우 해당 일기 정보를 보여줍니다.
HomeFragment.kt
private fun setServerDiaryData(diaryList: List<ResponseDiaryList.Data>) {
when (diaryList.size) {
0 -> {
setEmptyView()
DIARY_STATUS = false
}
else -> {
setDiaryView()
DIARY_STATUS = true
diaryId = diaryList[0].id
setEmotionData(diaryList[0].emotionId, isDay)
setDepthData(diaryList[0].depth)
setBookDiaryData(diaryList[0])
}
}
fadeOutLoadingView()
}
- 다이어리 상세 뷰에서 수정을 한 후, 다시 홈 화면으로 돌아왔을 때
IS_EDITED
boolean 값을 이용해 데이터를 다시 로드합니다.
HomeFragment.kt
override fun onResume() {
super.onResume()
if(IS_EDITED) {
updateByServerData()
IS_EDITED = false
}
}
- 총 7개의 깊이 단계에 각기 다른 백그라운드를 적용하기 위해 각 단계를
recyclerView
의 한 아이템으로 구성했습니다.
ScrollFragment.kt
private fun setGradientRecyclerView(year: Int, month: Int) {
viewBinding.recyclerViewGradient.apply {
adapter = ScrollGradientAdapter(year, month)
layoutManager = LinearLayoutManager(requireContext())
addOnScrollListener(scrollListener)
}
}
- 뷰홀더에서 그라디언트 아이템이 바인딩될 때,
viewStub
을 이용하여 동적으로 배경 리소스를 교체하였습니다.
ScrollGradientViewHolder.kt
private fun setDepthViews(position: Int) {
viewBinding.apply {
constraintLayout.removeAllViews()
when (position) {
0 -> {
textViewDepth.text = "2m"
viewStubGradient.layoutResource = R.layout.view_stub_depth_1
}
1 -> {
textViewDepth.text = "30m"
viewStubGradient.layoutResource = R.layout.view_stub_depth_2
}
2 -> {
textViewDepth.text = "100m"
viewStubGradient.layoutResource = R.layout.view_stub_depth_3
}
3 -> {
textViewDepth.text = "300m"
viewStubGradient.layoutResource = R.layout.view_stub_depth_4
}
4 -> {
textViewDepth.text = "700m"
viewStubGradient.layoutResource = R.layout.view_stub_depth_5
}
5 -> {
textViewDepth.text = "1,005m"
viewStubGradient.layoutResource = R.layout.view_stub_depth_6
}
6 -> {
textViewDepth.text = "심해"
viewStubGradient.layoutResource = R.layout.view_stub_depth_7
}
}
constraintLayout.addView(viewBinding.viewStubGradient)
constraintLayout.addView(viewBinding.textViewDepth)
constraintLayout.addView(viewBinding.recyclerViewOval)
viewStubGradient.inflate()
}
}
- 각 깊이 단계에 해당하는 일기 물방울 아이템을 배치하기 위해
recyclerView
를 깊이 아이템 뷰에 사용하였습니다. (2중 리사이클러뷰)
ScrollGradientViewHolder.kt
private fun setOvalRecyclerView(depth: Int, wholeDiaryList: List<ResponseDiaryList.Data>) {
val depthDiaryList = sortServerDiaryData(depth, wholeDiaryList)
viewBinding.recyclerViewOval.adapter = ScrollOvalAdapter(this, depthDiaryList)
}
- 서버에서 넘어오는 0~9의 랜덤값에 따른 x좌표에 물방울 아이템을 배치하였습니다.
ScrollOvalViewHolder.kt
fun onBind(diaryData: ResponseDiaryList.Data) {
getItemAreaWidth()
setOvalXPosition(diaryData.position)
setDiaryData(diaryData)
}
private fun getItemAreaWidth() {
// (디바이스 너비 - 아이템 너비 - 좌우여백) / (한 행당 아이템 최대 개수 - 1)
val deviceWidthPixels = displayMetrics.widthPixels
val itemWidthPixels = ITEM_SIZE * displayMetrics.density
val horizontalMarginPixels = HORIZONTAL_MARGIN * 2 * displayMetrics.density
itemDistance =
(deviceWidthPixels.toFloat() - itemWidthPixels - horizontalMarginPixels) / (ITEM_AMOUNT - 1)
}
private fun setOvalXPosition(xPosition: Int) {
// itemDistance * 아이템 랜덤값 + 좌측 여백 px
val leftMargin = ((itemDistance * xPosition) + (HORIZONTAL_MARGIN * displayMetrics.density))
val layoutParams = viewBinding.imageButtonOval.layoutParams as ConstraintLayout.LayoutParams
layoutParams.marginStart = leftMargin.toInt()
viewBinding.imageButtonOval.layoutParams = layoutParams
}
- 스크롤 중에는 스와이프 아이콘이 보이지 않다가 스크롤을 멈췄을 때 스와이프 아이콘이 보여집니다.
ScrollFragment.kt
private val scrollListener = object : RecyclerView.OnScrollListener() {
@RequiresApi(Build.VERSION_CODES.N)
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val visibleItemPosition = getVisibleItemPosition()
checkHomeButtonStatus(visibleItemPosition)
updateVerticalSeekBar(visibleItemPosition)
}
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
when (newState) {
SCROLL_STATE_IDLE -> fadeInSwipeImage()
SCROLL_STATE_DRAGGING -> fadeOutSwipeImage()
}
}
}
private fun fadeInSwipeUpImage() {
viewBinding.imageViewSwipeUp.apply {
visibility = View.VISIBLE
alpha = 0f
animate()
.alpha(1f)
.setDuration(resources.getInteger(android.R.integer.config_longAnimTime).toLong())
.setListener(null)
}
}
private fun fadeOutSwipeUpImage() {
viewBinding.imageViewSwipeUp.apply {
visibility = View.VISIBLE
alpha = 1f
animate()
.alpha(0f)
.setDuration(resources.getInteger(android.R.integer.config_longAnimTime).toLong())
.setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
viewBinding.viewLoading.visibility = View.INVISIBLE
}
})
}
}
- 스크롤 시, 현재 보여지는
recyclerView
itemPosition에 따라 우측의seekBar
가 이동합니다.
ScrollFragment.kt
@RequiresApi(Build.VERSION_CODES.N)
private fun updateVerticalSeekBar(visibleItemPosition: Int) {
when (visibleItemPosition) {
0 -> {}
else -> {
ObjectAnimator
.ofInt(
viewBinding.verticalSeekBarDepth,
"progress",
(visibleItemPosition - 1) * 80
)
.setDuration(1000)
.start()
}
}
}
- 스크롤 뷰에서 홈 버튼을 눌렀을 경우 아이템 0번째 포지션까지 스크롤이 된 후 홈 화면으로 이동합니다.
ScrollFragment.kt
private val fragmentOnClickListener = View.OnClickListener {
viewBinding.apply {
when (it.id) {
imageButtonMy.id -> setIntentToSettingActivity()
imageButtonCalendar.id -> setIntentToDatePicker()
imageButtonHome.id -> scrollToTop()
imageButtonUpload.id -> setIntentToUploadActivity()
imageButtonList.id -> setIntentToListActivity()
}
}
}
private fun scrollToTop() {
if (getVisibleItemPosition() == 0) {
requireActivity().onBackPressed()
}
viewBinding.recyclerViewGradient.smoothScrollToPosition(0)
isHomeButtonClicked = true
}
private fun checkHomeButtonStatus(visibleItemPosition: Int) {
if (visibleItemPosition == 0 && isHomeButtonClicked) {
IS_FROM_SCROLL = true
requireActivity().onBackPressed()
isHomeButtonClicked = false
}
}
- 일기 수정 후 다시 스크롤 뷰로 돌아왔을 경우 데이터를 다시 로드한 후 해당 깊이를 보여줍니다.
ScrollFragment.kt
override fun onResume() {
super.onResume()
if(IS_EDITED) {
setLoadingViewBackground()
viewBinding.recyclerViewGradient.adapter!!.notifyDataSetChanged()
viewBinding.recyclerViewGradient.scrollToPosition(EDITED_DEPTH + 1)
fadeOutLoadingView()
IS_EDITED = false
}
}