Kotlin with MVVM application PART 4(Last Part)
Hi everyone we are now at the last chapter. We create an all about Food section. First, we need an API, I used this API(click it).
Get a JSON file and use the Kotlin data class file from JSON on Android Studio and paste JSON data like this and the model class will be created automatically.
DATA(MODEL&NETWORK)
Model(It Created if you use JSON generate automatically)
Let’s create Retrofit for Networking create network folder and Api + Service files.
API Interface
interface FoodApi {
@GET("food-database/v2/parser?app_id=a00**58e&app_key=e7d27a4853f***44181583be6dd1f7e2") <- My KEY and ID for API fun getFoods(@Query("ingr") searchText: String) <- For search term Single<FoodApiModel>
}
point: Single on RxJava: A Single is something like an Observable, but instead of emitting a series of values — anywhere from none at all to an infinite number — it always either emits one value or an error notification.
Service
private val BASE_URL = "https://api.edamam.com/api/"
private val api = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
.create(FoodApi::class.java)
fun getData(searchText:String): Single<FoodApiModel> {
return api.getFoods(searchText) <- Get for Search Term
}
UI (ADAPTER&VİEWMODEL&FRAGMENT)
We need Recycler View, ProgressBar for when I refresh the page, TextView for networking Errors, and EditText for Food search.
Here it is my food_item, one of the items in my Recycler View.
Adapter
We created one adapter on PART 3. Let’s create again.
...... Create like in PART 3 Adapter(click for detail)...fun updateFoodWhenRefresh(newFoodList: List<FoodApiModel>) {
foodsList.clear() <- When refresh I clear my list
foodsList.addAll(newFoodList) <- After add new food
}onBindViewHolder : holder.binding.tvFoodLabel.text=foodsList[position].hints.firstOrNull()?.food?.label.toString()
...
... <- Other Items
...
Picasso.get().load(foodsList[position].hints.firstOrNull()?.food?.image).into(holder.binding.ivFood) <- For Image I used Picasso Library
I want to go Food Details When I click one Item on Recycler View Let’s create this function in onBindViewHolder
holder.itemView.setOnClickListener {
val intent = Intent(holder.itemView.context,FoodDetails::class.java) <- Which page I want to go
intent.apply {
putExtra("Label", currentItem.hints.firstOrNull()?.food?.label) <- Remember KEY / VALUE in => PART 2
...
...
...
holder.itemView.context.startActivity(intent)
}
VİEWMODEL
private val foodApiService = FoodApiService() <-Call my service
private val disposable = CompositeDisposable() **description is belowRemember my UI -> (RecyclerView get list data, error(TextView) and loading(ProgressBar) cases
val foodsData = MutableLiveData<List<FoodApiModel>>()
val foodsError = MutableLiveData<String>()
val foodsLoading = MutableLiveData<Boolean>()
point: Disposable is a stream or a link between an Observable and an Observer. A quick check of the documentation. Click for details
I need to get data from API now let’s create this function.
private fun getDataFromAPI(searchText: String) { foodsLoading.value = true disposable.add(
foodApiService.getData(searchText) <- I called My function in my ServiceClick for details for 1/2/3 1 .subscribeOn(Schedulers.newThread())
2 .observeOn(AndroidSchedulers.mainThread())
3 .subscribeWith(object : DisposableSingleObserver<FoodApiModel>() {
override fun onSuccess(t: FoodApiModel) {
foodsData.value = listOf(t)
foodsError.value = ""
foodsLoading.value = false
}
override fun onError(e: Throwable) {
foodsError.value = e.message
foodsLoading.value = false
}
})
)
}
and
fun getData(searchText: String) {
getDataFromAPI(searchText)
}
Fragment
Global variables
lateinit var foodAdapter: FoodAdapter <- Call my adapter
private val _viewModel by inject<FoodViewModel>() <- KOIN
private lateinit var binding: FragmentRecipesBinding <-View BindingFirst time I use Shared Preferences in this Project
private lateinit var getPreferences: SharedPreferences
private lateinit var setPreferences: SharedPreferences.Editor
Initialize Shared Preferences
getPreferences = requireActivity().getSharedPreferences("pref", MODE_PRIVATE) <- Get Shared Pref initializesetPreferences = getPreferences.edit()<- Set Shared Pref initialize
I need to observe my data.
private fun observeLiveData() {
If I got Error
_viewModel.foodsError.observe(viewLifecycleOwner, Observer { errorMessage ->
...Create for yourself
}If I got Refresh
_viewModel.foodsLoading.observe(viewLifecycleOwner, Observer { loading ->
... Create for yourselfIf I got live data
_viewModel.foodsData.observe(viewLifecycleOwner, Observer { foods ->
foods?.let {
binding.recyclerView.visibility = View.VISIBLE
foodAdapter.updateFoodWhenRefresh(foods) <- In my adapter function
}
})
Now when I search term and click search I need a function
fun searchFood(){
... <- Binding UI
val foodName = binding.edSearchFood.text.toString()
setPreferences.putString("foodName", foodName)
setPreferences.apply()
_viewModel.getData(foodName)
observeLiveData()
}
onCreateView
xval foodName = getPreferences.getString("foodName", "")?.lowercase(Locale.getDefault()) <- Use preferences and default value EMPTY STRING binding.edSearchFood.setText(foodName) <- Search Term is foodName value _viewModel.getData(foodName) <- Use getDataFromAPI function on viewModel observeLiveData() <- Above function
searchFood() <- Above functionnote : The rest is the same as the previous parts
**extra point
If I want to add food and show it on the daily menu
If click to add food you use this way var totalCalories = 0
var addCount = 0if click add
addCount++
or click remove
addCount--click to add button, get total calories maybe like this
addCount * get calorie food in model + total calorieclick to daily food menu
use putExtra
Let’s watch the whole application
This article is 4 and last part of my MVVM app :)
Github link is: https://github.com/basaransuleyman/MustfitMVVMJetpack Linked is: https://www.linkedin.com/in/basaransuleyman/