Skip to content

Commit

Permalink
#6 onboarding/온보딩 화면 : 직장 검색 화면 📰
Browse files Browse the repository at this point in the history
- 리사이클러뷰로 검색결과 구현
- 검색 결과가 없는 경우 이동시킬 버튼 구현
  • Loading branch information
likppi10 committed Jul 30, 2022
1 parent 81a2a83 commit ea9389d
Show file tree
Hide file tree
Showing 16 changed files with 503 additions and 0 deletions.
4 changes: 4 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
android:theme="@style/Theme.Hometerview"
tools:targetApi="31">


<activity
android:name=".ui.onboardingresult.OnboardingResultActivity"
android:exported="false" />
<activity
android:name=".ui.onboarding.OnboardingActivity"
android:exported="false" />
Expand Down
36 changes: 36 additions & 0 deletions app/src/main/java/com/ftw/hometerview/adapter/DiffCallback.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.ftw.hometerview.adapter

import android.annotation.SuppressLint
import androidx.recyclerview.widget.DiffUtil

internal class DiffCallback : DiffUtil.ItemCallback<RecyclerItem>() {

override fun areItemsTheSame(
oldItem: RecyclerItem,
newItem: RecyclerItem
): Boolean {
val oldData = oldItem.data
val newData = newItem.data
// Use appropriate comparator's method if both items implement the interface
// and rely on the plain 'equals' otherwise
return if (oldData is RecyclerItemComparator
&& newData is RecyclerItemComparator
) {
oldData.isSameItem(newData)
} else oldData == newData
}

@SuppressLint("DiffUtilEquals")
override fun areContentsTheSame(
oldItem: RecyclerItem,
newItem: RecyclerItem
): Boolean {
val oldData = oldItem.data
val newData = newItem.data
return if (oldData is RecyclerItemComparator
&& newData is RecyclerItemComparator
) {
oldData.isSameContent(newData)
} else oldData == newData
}
}
59 changes: 59 additions & 0 deletions app/src/main/java/com/ftw/hometerview/adapter/RecyclerAdapter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.ftw.hometerview.adapter

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView

class DataBindingRecyclerAdapter : ListAdapter<RecyclerItem, BindingViewHolder>(
DiffCallback()
) {
override fun getItemViewType(position: Int): Int {
return getItem(position).layoutId
}

override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): BindingViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding: ViewDataBinding = DataBindingUtil.inflate(inflater, viewType, parent, false)
return BindingViewHolder(binding)
}

override fun onBindViewHolder(
holder: BindingViewHolder,
position: Int
) {
holder.run {
getItem(position).bind(binding)
if (binding.hasPendingBindings()) {
binding.executePendingBindings()
}
}
}
}

class BindingViewHolder(
val binding: ViewDataBinding
) : RecyclerView.ViewHolder(binding.root)

private fun RecyclerItem.bind(binding: ViewDataBinding) {
val isVariableFound = binding.setVariable(variableId, data)
if (isVariableFound.not()) {
throw IllegalStateException(
buildErrorMessage(variableId, binding)
)
}
}

private fun buildErrorMessage(
variableId: Int,
binding: ViewDataBinding
): String {
val variableName = DataBindingUtil.convertBrIdToString(variableId)
val className = binding::class.simpleName
return "Failed to find variable='$variableName' in the following databinding layout: $className"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.ftw.hometerview.adapter

interface RecyclerItemComparator {
fun isSameItem(other: Any): Boolean
fun isSameContent(other: Any): Boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.ftw.hometerview.adapter

import androidx.databinding.BindingAdapter
import androidx.recyclerview.widget.RecyclerView

@BindingAdapter("items")
fun setRecyclerViewItems(
recyclerView: RecyclerView,
items: List<RecyclerItem>?
) {
var adapter = (recyclerView.adapter as? DataBindingRecyclerAdapter)
if (adapter == null) {
adapter = DataBindingRecyclerAdapter()
recyclerView.adapter = adapter
}

adapter.submitList(
items.orEmpty()
)
}
9 changes: 9 additions & 0 deletions app/src/main/java/com/ftw/hometerview/adapter/RecyclweItem.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.ftw.hometerview.adapter

import androidx.annotation.LayoutRes

data class RecyclerItem(
val data: Any,
@LayoutRes val layoutId: Int,
val variableId: Int
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.ftw.hometerview.di.ui

import com.ftw.domain.usecase.login.LoginUseCase
import com.ftw.hometerview.dispatcher.Dispatcher
import com.ftw.hometerview.ui.onboardingresult.OnboardingResultViewModel
import com.ftw.hometerview.ui.splash.SplashViewModel
import dagger.Module
import dagger.Provides
Expand All @@ -20,4 +21,11 @@ class ActivityViewModelModule {
): SplashViewModel {
return SplashViewModel(dispatcher, loginUseCase)
}
@Provides
@ActivityScoped
fun provideOnboardResultViewModel(
dispatcher: Dispatcher
): OnboardingResultViewModel {
return OnboardingResultViewModel(dispatcher)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.ftw.hometerview.ui.onboardingresult

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.lifecycle.lifecycleScope
import com.ftw.hometerview.databinding.ActivityOnboardingResultBinding
import com.ftw.hometerview.ui.login.TAG
import com.ftw.hometerview.ui.onboardingnonresult.OnboardingNonResultActivity
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import javax.inject.Inject

@AndroidEntryPoint
class OnboardingResultActivity : AppCompatActivity() {

@Inject
lateinit var viewModel: OnboardingResultViewModel
private lateinit var binding: ActivityOnboardingResultBinding

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

binding = ActivityOnboardingResultBinding.inflate(layoutInflater)
setContentView(binding.root)

binding.searchButton.setText(intent.getStringExtra("search_word"))

binding.let {
it.lifecycleOwner = this
it.searchResultViewModel = this.viewModel
}
observe()
}

private fun observe() {
lifecycleScope.launch {
viewModel.clickState.collect { state ->
when (state) {
OnboardingResultViewModel.State.OnClickEmpty -> noResult()
else -> {}
}
}
}
}

private fun noResult() {
Log.d(TAG, "카카오계정공")
viewModel.noResultClicked()
val intent = Intent(this, OnboardingNonResultActivity::class.java)
startActivity(intent) // TODO: HometerviewActivity로 이동
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.ftw.hometerview.ui.onboardingresult

import android.util.Log
import com.ftw.hometerview.BR
import androidx.lifecycle.ViewModel
import com.ftw.domain.entitiy.DEMO_RESULTS
import com.ftw.domain.entitiy.SearchResult
import com.ftw.hometerview.R
import com.ftw.hometerview.adapter.RecyclerItem
import com.ftw.hometerview.dispatcher.Dispatcher
import com.ftw.hometerview.ui.login.TAG
import com.ftw.hometerview.ui.onboardingresult.recyclemodel.SearchResultRecycleModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch

class OnboardingResultViewModel(
private val dispatcher: Dispatcher
) : ViewModel() {

// private val _recyclerItems = MutableLiveData<List<RecyclerItem>>()
// val recyclerItems: LiveData<List<RecyclerItem>> = _recyclerItems
private val _recyclerItems: MutableStateFlow<List<RecyclerItem>> = MutableStateFlow(listOf())
val recyclerItems: StateFlow<List<RecyclerItem>> get() = _recyclerItems.asStateFlow()
private val _clickState: MutableStateFlow<State> = MutableStateFlow(State.Nothing)
val clickState: StateFlow<State> get() = _clickState.asStateFlow()

sealed class State {
object OnClickEmpty : State()
object Failure : State()
object Loading : State()
object Nothing : State()
}

init {
_recyclerItems.value = loadDemoDatas()
.map { createSearchResultRecycleModel(it) }
.map { it.toRecyclerItem() }

}

private fun createSearchResultRecycleModel(searchResult: SearchResult): SearchResultRecycleModel {
return SearchResultRecycleModel(searchResult).apply {
itemClickHandler = { searchResult -> showClickMessage(searchResult) }
}
}

private fun showClickMessage(searchResult: SearchResult) {
//통신으로 받아온 정보를 State와 조합해서 사용하면 될 듯 함

}

// 1. startActivity와 BindingAdapter
// 2. success가 안 바뀌어....
fun noResultRecycleModel() {
Log.d(TAG, "카카오계정공123 ${_clickState.value}")
CoroutineScope(dispatcher.ui()).launch {
_clickState.value = State.Loading
// api 호출??
_clickState.value = State.OnClickEmpty

}

}

fun noResultClicked() {
CoroutineScope(dispatcher.ui()).launch {
_clickState.value = State.Nothing

}
}

private fun loadDemoDatas(): List<SearchResult> = DEMO_RESULTS

}

private fun SearchResultRecycleModel.toRecyclerItem() = RecyclerItem(
data = this, // SearchResultRecycleModel: searchResult(company, ..), onClickListener
layoutId = R.layout.list_item_search_result,
variableId = BR.searchResult
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.ftw.hometerview.ui.onboardingresult.recyclemodel

import com.ftw.domain.entitiy.SearchResult

class SearchResultRecycleModel(val result: SearchResult) {

lateinit var itemClickHandler: (result: SearchResult) -> Unit

fun onItemClick() {
itemClickHandler(result)
}

}
5 changes: 5 additions & 0 deletions app/src/main/res/drawable/ic_baseline_navigate_before_24.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M15.41,7.41L14,6l-6,6 6,6 1.41,-1.41L10.83,12z"/>
</vector>
5 changes: 5 additions & 0 deletions app/src/main/res/drawable/ic_baseline_navigate_next_24.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z"/>
</vector>
5 changes: 5 additions & 0 deletions app/src/main/res/drawable/ic_baseline_search_24.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
</vector>
Loading

0 comments on commit ea9389d

Please sign in to comment.