https://github.com/luisguzmanms/apiapp
Aplicación móvil con arquitectura MVVM que permite realizar una conexión API REST
https://github.com/luisguzmanms/apiapp
android api kotlin mvvm rest-api
Last synced: 26 days ago
JSON representation
Aplicación móvil con arquitectura MVVM que permite realizar una conexión API REST
- Host: GitHub
- URL: https://github.com/luisguzmanms/apiapp
- Owner: luisguzmanms
- License: mit
- Created: 2023-02-13T05:36:17.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2023-02-13T06:28:56.000Z (over 3 years ago)
- Last Synced: 2024-04-20T12:07:09.599Z (about 2 years ago)
- Topics: android, api, kotlin, mvvm, rest-api
- Language: Kotlin
- Homepage:
- Size: 5.13 MB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Documentación App API REST con MVVM "Clientes"
> Documentación y desarrollo móvil realizado por Luis A. Mesa ([luis4mesa@gmail.com](mailto:luisguzman014.m@gmail.com))
Introducción
============
El siguiente documento describe el desarrollo de una aplicación móvil con arquitectura **MVVM** que permite realizar una llamada a un **API REST** en la URL [https://jsonplaceholder.typicode.com/users](https://jsonplaceholder.typicode.com/users) para obtener información sobre usuarios denominados como clientes para este caso.
Los datos son luego cargados en un Recycler View, y se pueden filtrar para una mejor visualización y accesibilidad.
Tecnologías utilizadas
======================
Para el desarrollo de la aplicación se han utilizado las siguientes tecnologías:
* **Android Studio**: Es el entorno para desarrollo de aplicaciones para Android.
* Lenguaje de programación: **Kotlin**.
* **Coroutines** de **Kotlin** para procesar tareas asíncronas.
* Arquitectura **MVVM** para la implementación de la base de datos.
* **DataBinding** para enlazar los datos del ViewModel con la interfaz.
* **MaterialDesign3** para un diseño agradable.
Codigo fuente, Video e Instalador APK
=====================================
En esta sección se incluirá el código fuente completo de la aplicación, un video demostrativo para ver su funcionamiento y un instalador para descargar e instalar la aplicación.
* Código Fuente: [Github](https://rebrand.ly/APIappGIT)
* Demostración: [Video](https://rebrand.ly/APIvideo)
* Instalador APK: [Descargar](https://rebrand.ly/APIappAPK)
Detalles del desarrollo
=======================
MVVM
----
se utiliza en este proyecto para separar la lógica de negocios de la lógica de la interfaz de usuario.
LiveData
--------
manejo de cambios en los datos y la actualización automática de la vista
Databinding
-----------
Vincular los datos directamente a la vista

Uso de MVVM
===========
* [**CustomerAdapter**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/adapter/CustomerAdapter.kt): Se encarga de mostrar la información de cada cliente en la lista de clientes.
* [**Customer**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/res/layout/customer_item.xml): Contiene las propiedades y métodos necesarios para representar a un cliente.
* [**CustomerApiClient**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/data/network/api/CustomerApiClient.kt): archivo se encarga de establecer la conexión con la API que proporciona los datos de los clientes.
* [**CustomerService**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/data/network/CustomerService.kt): Contiene funciones específicas para obtener los clientes.
* [**RetrofitHelper**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/data/network/RetrofitHelper.kt)**:** facilita la creación de Retrofit.
* [**CustomerRepository**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/data/repository/CustomerRepository.kt)[:](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/data/repository/CustomerRepository.kt) encargado de gestionar el acceso a los datos de los clientes desde la API.
* [**CustomerViewHolder**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/ui/view/CustomerViewHolder.kt): Contiene la información que se mostrará para cada cliente.
* [**MainActivity**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/ui/view/MainActivity.kt): actividad principal de la aplicación.
* [**CustomerViewModel**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/ui/viewmodel/CustomerViewModel.kt): Obtiene los datos a través del repositorio y proporcionarlos a la actividad principal.
* [**GetCustomersUseCase**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/usecase/GetCustomersUseCase.kt): caso de uso que proporciona la manera de obtener los clientes de la aplicación.
* [**Util**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/util/Util.kt): contiene funciones útiles como la validación de conexión a internet.

Funcionamiento de la aplicación
===============================
Se hace una llamada API REST a [https://jsonplaceholder.typicode.com/users](https://jsonplaceholder.typicode.com/users) para obtener un listado de usuarios denominados "**clientes**", la información recibida se muestra en una vista de RecyclerView y se puede filtrar mediante una barra de búsqueda.
Implementación API REST:
------------------------
Para ello, se creó una clase llamada [`CustomerService`](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/data/network/CustomerService.kt) [](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/data/network/CustomerService.kt)que contiene la configuración de Retrofit para realizar la solicitud. haciendo uso del metodo "**GET**" [`CustomerApiClient`](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/data/network/api/CustomerApiClient.kt) [](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/data/network/api/CustomerApiClient.kt)para la consulta en la interfaz, con uso de **Courutines (**[Dispatchers.IO](http://Dispatchers.IO)**)** para la operaciones de datos en segundo plano.
```kotlin
class CustomerService {
private val retrofit = RetrofitHelper.getRetrofit("https://jsonplaceholder.typicode.com/")
suspend fun getCustomers(): List {
return withContext(Dispatchers.IO) {
val response = retrofit.create(CustomerApiClient::class.java).getAllCustomers()
response.body() ?: emptyList()
}
}
}
interface CustomerApiClient {
@GET("users")
suspend fun getAllCustomers(): Response>
}
```
Luego, se creó un **Repository**, que es el encargado de obtener los datos de la **API** y proporcionarlos a la **vista**. En este caso, se utilizaron **Coroutines** para manejar de manera asíncrona la solicitud y evitar bloqueos en la UI. [CustomerRepository](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/data/repository/CustomerRepository.kt)
[**CustomerRepository**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/data/repository/CustomerRepository.kt)
```kotlin
class CustomerRepository {
private val api = CustomerService()
suspend fun getCustomers(): List {
return api.getCustomers()
}
}
```
Por último, en el **ViewModel** se hizo la llamada al **Repository** mediante un **caso de uso** (**GetCustomersUseCase()**) para obtener los datos y exponerlos en la vista.
[CustomerViewModel](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/ui/viewmodel/CustomerViewModel.kt)
```kotlin
class CustomerViewModel : ViewModel() {
private val _customers = MutableLiveData>()
val getCustomersUseCase = GetCustomersUseCase()
val customers: LiveData>
get() = _customers
fun getCustomers() {
viewModelScope.launch {
val result = getCustomersUseCase()
_customers.postValue(result)
}
}
}
```
Implementación RecyclerView:
----------------------------
[**Customer**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/data/model/Customer.kt)
Se crea la clase **Customer** que el modelo de cliente que se utilizará en la aplicación. Contiene las propiedades y métodos necesarios para representar al cliente.
```kotlin
package com.lamesa.apiapp.data.model
data class Customer(
val id: Int,
val name: String,
val username: String,
val email: String,
val address: Address,
val phone: String,
val website: String,
val company: Company
)
data class Address(
val street: String,
val suite: String,
val city: String,
val zipcode: String,
val geo: Geo
)
/
data class Geo(
val lat: String,
val lng: String
)
data class Company(
val name: String,
val catchPhrase: String,
val bs: String
)
```
**RecyclerView**
Se implementa la vista en el XML activity\_main.xml
```xml
```
[**CustomerAdapter**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/adapter/CustomerAdapter.kt)
Se crea el **CustomerAdapter** que se encargará de mostrar la lista de clientes en el recyclerview El método `updateList` actualiza la lista de clientes y llama a `notifyDataSetChanged` para notificar al adaptador de que la lista ha cambiado. El método `filterList` filtra la lista de clientes y actualiza la lista con los clientes filtrados.
```kotlin
package com.lamesa.apiapp.adapter
class CustomerAdapter : RecyclerView.Adapter() {
private var listCustomer = ArrayList()
private var lastPosition = -1
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomerViewHolder {
val view =
LayoutInflater.from(parent.context).inflate(R.layout.customer_item, parent, false)
return CustomerViewHolder(view)
}
override fun onBindViewHolder(holder: CustomerViewHolder, position: Int) {
val customer = listCustomer[position]
holder.bind(customer)
setAnimation(holder, position)
}
override fun getItemCount(): Int {
return listCustomer.size
}
internal fun updateList(customers: List) {
listCustomer.clear()
listCustomer.addAll(customers)
this.notifyDataSetChanged()
}
internal fun filterList(filteredCustomers: List) {
this.updateList(filteredCustomers)
this.notifyDataSetChanged()
}
private fun setAnimation(holder: CustomerViewHolder, position: Int) {
if (position > lastPosition) {
val animation = AlphaAnimation(0f, 1f)
animation.duration = 500
animation.startOffset = 100 * position.toLong()
holder.itemView.startAnimation(animation)
lastPosition = position
}
}
}
```
[**CustomerViewHolder**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/ui/view/CustomerViewHolder.kt)
Se inicializa las vistas que representan los detalles de un cliente, como su ID, nombre, correo electrónico, nombre de usuario, sitio web, teléfono, nombre de la compañía y ciudad de residencia. y se enlaza los datos traidos de la api.

```kotlin
package com.lamesa.apiapp.ui.view
class CustomerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val cardCustomer = itemView.findViewById(R.id.card_customer)
private val id = itemView.findViewById(R.id.identification)
private val name = itemView.findViewById(R.id.fullname)
private val username = itemView.findViewById(R.id.username)
private val email = itemView.findViewById(R.id.email)
private val website = itemView.findViewById(R.id.website)
private val phone = itemView.findViewById(R.id.phone)
private val company = itemView.findViewById(R.id.company)
private val address = itemView.findViewById(R.id.address)
@SuppressLint("SetTextI18n")
fun bind(customer: Customer) {
id.text = "ID: ${customer.id}"
name.text = customer.name
username.text = customer.username
email.text = customer.email
phone.text = customer.phone
website.text = customer.website
company.text = customer.company.name
address.text = customer.address.city
cardCustomer.setOnClickListener() {
Toast.makeText(cardCustomer.context, customer.toString(), Toast.LENGTH_SHORT).show()
}
}
}
```
RecyclerView en MainActivity
Se inicializa el **RecyclerView** desde **MainActivity**
```kotlin
private fun initRecyclerView() {
val customerRecyclerView = findViewById(R.id.customer_recycler_view)
customerRecyclerView.layoutManager = LinearLayoutManager(this)
customerAdapter = CustomerAdapter()
customerRecyclerView.adapter = customerAdapter
}
```
Implementación de la barra de búsqueda.
---------------------------------------
Se crea un **EditText** en el _activity\_main.xml_ que mediante un **_TextWatcher_** _se detecta el texto digitado y es apsado a la función_ **_`filterCustomers(searchtext)`_**
```kotlin
// Detectar cambios en etSearch para el filtrado de clientes.
binding.etSearch.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {
val searchText = s.toString().trim().lowercase()
// Filtrar los clientes.
filterCustomers(searchText)
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(text: CharSequence, start: Int, before: Int, count: Int) {
if (text.isEmpty()) {
customerAdapter.updateList(allCustomers)
}
}
})
private fun filterCustomers(searchText: String) {
val filteredCustomers = allCustomers.filter {
it.id.toString().contains(searchText, ignoreCase = true)
|| it.name.contains(searchText, ignoreCase = true)
|| it.email.contains(searchText, ignoreCase = true)
|| it.phone.contains(searchText, ignoreCase = true)
|| it.website.contains(searchText, ignoreCase = true)
|| it.address.city.contains(searchText, ignoreCase = true)
|| it.company.name.contains(searchText, ignoreCase = true)
}
// Actualizar lista de RecyclerView.
customerAdapter.filterList(filteredCustomers)
// Indicar que no se encuentra ningún resultado.
if (filteredCustomers.isEmpty()) {
binding.cnEmpty.visibility = View.VISIBLE
Toast.makeText(
this@MainActivity,
"No se encontraron resultados.",
Toast.LENGTH_LONG
).show()
} else {
binding.cnEmpty.visibility = View.GONE
}
}
```
En el adaptador del **RecyclerView**, se usa la función `filterList ()` para actualizar la vista del recycler view:
```kotlin
internal fun filterList(filteredCustomers: List) {
this.updateList(filteredCustomers)
this.notifyDataSetChanged()
}
```
===
Conclusión
==========
En esta documentación se presentó la implementación de una aplicación en Android Studio utilizando Kotlin y las siguientes tecnologías: Retrofit2, Coroutines, DataBinding y MaterialDesign3. con arquitectura MVVM, de Retrofit y Coroutines donde se crearon las clases correspondientes para el Repository, el ViewModel y la Vista.
La aplicación realiza una consulta a un API ([https://jsonplaceholder.typicode.com/users](https://jsonplaceholder.typicode.com/users)) para obtener una lista de usuarios, que luego se muestran en un RecyclerView.
Adicional, se implementó una barra de búsqueda permitiendo al usuario filtrar la lista de acuerdo a la información proporcionada.