Omet navegació

3.2 - Exemple complet

Una vegada hem vist les classes que ens fan falta per a accedir per mig de ROOM, anem a fer les modificacions oportunes a l'exemple CoffeeShops_Fragments.

Recordem que aquest exemple mostra en un primer fragment (FirstFragment) una sèrie de cafeteries (amb una imatge, nom, adreça i puntuació). En punxar en una cafeteria, anem un segon fragment (SecondFragment) on es veuen el comentaris que s'han fet a aquesta cafeteria. Haurem de modificar, per tant, aquestos fitxers, així com adaptar CoffeeAdapter i alguna cosa més.

  • Copieu-vos el projecte CoffeeShops_Fragments en un altre projecte anomenat CoffeeShops_Fragments_ROOM, per a poder conservar l'original.
  • Modifiqueu el nom del projecte, nom del paquet, nom de l'aplicació, ... Ho farem amb 2 accions:
    • Des del menú Edit -> Find -> Repalce in path... (si no està aquesta opció, doncs Edit -> Find -> Repalce in Files...) canvieu CoffeeShops_Fragments per CoffeeShops_Fragments_ROOM. Aneu amb compte vigilant que la diferenciació de majúscules i minúscules estiga activada (ha d'estar en blau). Es diu Match case, i és un simbolet així: Aa
    • Feu el mateix amb per a canviar coffesShops_fragments en coffesShops_fragments_room (en minúscules), cuidant que la diferenciació entre majúscules i minúscules continue activa.
    • Sobre app -> java -> com.example.coffeeshops_fragments fer un Refactor -> Renamecoffeeshops_fragments_room i triar després l'opció Rename package. Això hauria d'actualitzar el paquet en totes les classes definides, i també en el AndroidManifest.xml
  • Executeu-lo per veure que continua funcionant, ara ja amb el nou nom
  • Modifiqueu el build.gradle de l'aplicació per a que continga la llibreria Room, afegint les línies següents i després sincronitzant:
plugins {
  ...
  id 'kotlin-kapt'
}

dependencies {
...
  def room_version = "2.2.5"

  implementation "androidx.room:room-runtime:$room_version"
  kapt "androidx.room:room-compiler:$room_version"
}
  • Copieu les classes de l'apartat anterior: Coffee, Comment, CoffeeWithComments, CoffeeShopsDao i CoffeeShopsDatabase. Recordeu que Coffee i Comment substituiran a les anteriors
  • Ara és el moment de modificar els programes fets en el mòdul de DI, pera a adaptar-lo a l'accés a la Base de Dades a través de Room. Anirem mirant cadascun d'ells
    • CoffeeAdapter

      Igual que vam fer en el punt 2.2.1, ara no agafem les imatges del res, sinó que ens arriba la mateixa imatge. També aprofitem per a mostrar la puntuació de la Base de Dades. Posem tota la funció bindCards

      fun bindCards(t: Coffee, onClick: (View) -> Unit) {
      //image.setImageResource(t.image)
      val img = t.image
      if (img != null) {
      val imgBmp = BitmapFactory.decodeByteArray(img, 0, img.size)
      image.setImageBitmap(imgBmp)
      }
      text.text = t.title
      text1.text = t.subtitle
      if (t.points!=null)
      barstars.rating = t.points.toFloat()
      barstars.onRatingBarChangeListener = RatingBar.OnRatingBarChangeListener { ratingBar: RatingBar, fl: Float, b: Boolean ->
      points.text = fl.toString()
      }
      itemView.setOnClickListener{ onClick(itemView) }
      }
    • CommentsAdapter

      No cal modificar res en aquest. Només en tot cas arreglar que la propietat de Comment es diu comm, no comm1

    • FirstFragment

      En aquest sí que tenim feina. Per una banda hem d'agafar les dades de la Base de Dades a través de Room. Per una altra banda hem de passar la informació correcta al SecondFragment
      Per a la primera qüestió, recordeu que no podem accedir des del Thread principal, per tant ens muntem un altre per a fer tot l'accés a Room
      Per a la segona qüestió, voldrem passar al SecondFragment no únicament el nom del cafè, sinó tots els comentaris també. Aleshores passarem un objecte CoffeWithComments, perquè ahí està tot. Per a poder passar-lo en el bundle, ha d'heretar de Serializable.
      També aprofitarem per a inicialitzar la puntuació de les cafeteries, ja que tenim guardada en la Base de Dades aquesta puntuació.
      Ací teniu tot el FirstFragment

      import android.os.Bundle
      import android.view.LayoutInflater
      import android.view.View
      import android.view.ViewGroup
      import androidx.core.os.bundleOf
      import androidx.fragment.app.Fragment
      import androidx.navigation.fragment.findNavController
      import androidx.recyclerview.widget.LinearLayoutManager
      import androidx.recyclerview.widget.RecyclerView
      import androidx.room.Room

      /**
      * A simple [Fragment] subclass as the default destination in the navigation.
      */
      class FirstFragment : Fragment() {

      private var items: ArrayList<Coffee> = ArrayList()
      private var itemsWithComments: ArrayList<CoffeeWithComments> = ArrayList()

      private var roomThread: Thread = object : Thread() {
      override fun run() {
      val db = Room.databaseBuilder(
      context!!,
      CoffeeShopsDatabase::class.java, "CoffeeShops_Com.sqlite"
      ).createFromAsset("CoffeeShops_Com.sqlite").build()

      items = ArrayList(db.coffeeshopsDao().getCoffees())
      itemsWithComments = ArrayList(db.coffeeshopsDao().getCoffeesWithComments())
      }
      }

      override fun onCreateView(
      inflater: LayoutInflater, container: ViewGroup?,
      savedInstanceState: Bundle?
      ): View? {

      val root = inflater.inflate(R.layout.fragment_first, container, false)

      if (items.size==0) { // per a no executar-lo una altra vegada quan tornem del SecondFragment
      roomThread.start()
      roomThread.join()
      }

      val recView: RecyclerView = root.findViewById(R.id.recView)
      recView.setHasFixedSize(true)
      val adaptador = CoffeeAdapter(items)

      recView.adapter = adaptador
      recView.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)

      adaptador.onClick = {
      val c = itemsWithComments[recView.getChildAdapterPosition(it)]
      val bundle = bundleOf("coffee" to c)
      findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment, bundle)
      }
      return root
      }

      override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
      super.onViewCreated(view, savedInstanceState)

      }
      }

    • SecondFragment

      Només haurem d'agafar la informació que ens arriba del bundle, tenint en compte que és un objecte CoffeeWithComments. Ara ja no inicialitzem els comentaris de forma directa sinó que els agafem de l'anterior.
      Ací teniu tot el SecondFragment

      import android.os.Bundle
      import android.view.LayoutInflater
      import android.view.View
      import android.view.ViewGroup
      import android.widget.Button
      import android.widget.TextView
      import android.widget.Toast
      import androidx.fragment.app.Fragment
      import androidx.navigation.fragment.findNavController
      import androidx.recyclerview.widget.GridLayoutManager
      import androidx.recyclerview.widget.LinearLayoutManager
      import androidx.recyclerview.widget.RecyclerView

      /**
      * A simple [Fragment] subclass as the second destination in the navigation.
      */
      class SecondFragment : Fragment() {

      private lateinit var Comments: ArrayList<Comment>


      override fun onCreateView(
      inflater: LayoutInflater, container: ViewGroup?,
      savedInstanceState: Bundle?
      ): View? {
      // Inflate the layout for this fragment
      val root = inflater.inflate(R.layout.fragment_second, container, false)
      val texto: TextView = root.findViewById(R.id.textview_second)

      val coffee = arguments?.get("coffee") as CoffeeWithComments
      texto.text = coffee.coffee.title
      Comments=ArrayList(coffee.coms)

      val recView: RecyclerView = root.findViewById(R.id.recyclerview_coment)
      recView.setHasFixedSize(true)
      val adaptador = CommentsAdapter(Comments)

      recView.adapter = adaptador
      recView.layoutManager = GridLayoutManager(context, 2)

      return root
      }

      override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
      super.onViewCreated(view, savedInstanceState)

      /* view.findViewById<Button>(R.id.button_second).setOnClickListener {
      findNavController().navigate(R.id.action_SecondFragment_to_FirstFragment)
      }*/
      }
      }
    • Creeu-vos la carpeta de assets des del menú: File -> New -> Folder -> Assets Folder, i copieu en ella la Base de Dades CoffeeShops_Com.sqlite