WIP implementing infinte scroll

This commit is contained in:
Daniele Verducci su MatissePenguin 2021-08-31 21:20:58 +02:00
parent eaff3a4965
commit d470f7e968
8 changed files with 90 additions and 32 deletions

View File

@ -46,6 +46,7 @@ dependencies {
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'com.squareup.picasso:picasso:2.8'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

View File

@ -11,7 +11,8 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.SubitoBeers">
<activity android:name=".MainActivity">
<activity android:name=".MainActivity"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@ -2,19 +2,23 @@ package it.danieleverducci.subitobeers
import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import com.squareup.picasso.Picasso
import it.danieleverducci.subitobeers.databinding.FragmentBeersListitemBinding
import it.danieleverducci.subitobeers.entities.Beer
class BeerRecyclerAdapter : RecyclerView.Adapter<BeerRecyclerAdapter.ViewHolder>() {
var listener: Listener? = null
private val items: ArrayList<Beer> = ArrayList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
// Create viewholder
val vh = ViewHolder(
FragmentBeersListitemBinding.inflate(
LayoutInflater.from(parent.context),
parent,
@ -22,12 +26,37 @@ class BeerRecyclerAdapter : RecyclerView.Adapter<BeerRecyclerAdapter.ViewHolder>
)
)
// Register click listener
vh.root.setOnClickListener(object: View.OnClickListener{
override fun onClick(v: View?) {
if (v != null) {
listener?.OnItemClicked(v.getTag() as Beer)
}
}
})
return vh
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = items[position]
// Attach item to view to retrieve in case of click
holder.root.setTag(item)
// Fill layout with data
holder.name.text = item.name
holder.descr.text = item.tagline
Picasso.get()
.load(item.imageUrl)
.placeholder(R.drawable.ic_launcher_foreground)
.error(R.drawable.ic_launcher_foreground)
.into(holder.pic)
// If we are drawing the last element, notify
if (position == items.size - 1)
listener?.OnLastItemScrolled()
}
fun addItems(ni: List<Beer>) {
@ -38,8 +67,15 @@ class BeerRecyclerAdapter : RecyclerView.Adapter<BeerRecyclerAdapter.ViewHolder>
override fun getItemCount(): Int = items.size
inner class ViewHolder(binding: FragmentBeersListitemBinding) : RecyclerView.ViewHolder(binding.root) {
val root: View = binding.root
val name: TextView = binding.beerItemName
val descr: TextView = binding.beerItemDescr
val pic: ImageView = binding.beerItemPic
}
interface Listener {
fun OnItemClicked(item: Beer)
fun OnLastItemScrolled()
}
}

View File

@ -13,9 +13,11 @@ import it.danieleverducci.subitobeers.entities.Beer
/**
* A fragment representing a list of Items.
*/
class BeersFragment : Fragment(), BeersRepository.Listener {
class BeersFragment : Fragment(), BeersRepository.Listener, BeerRecyclerAdapter.Listener {
private val rvAdapter = BeerRecyclerAdapter()
private val repo = BeersRepository()
private var page = 1
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
@ -30,6 +32,10 @@ class BeersFragment : Fragment(), BeersRepository.Listener {
adapter = rvAdapter
}
}
// Register for recyclerview adapter events
rvAdapter.listener = this
return view
}
@ -37,9 +43,11 @@ class BeersFragment : Fragment(), BeersRepository.Listener {
super.onStart()
// Load beers from network
val repo = BeersRepository()
repo.listener = this
repo.getBeers(1)
// TODO: evitare che ogni volta ricarichi gli stessi elementi
page = 1
repo.getBeers(page)
}
override fun onBeersObtained(beers: List<Beer>) {
@ -50,4 +58,20 @@ class BeersFragment : Fragment(), BeersRepository.Listener {
Toast.makeText(context, R.string.network_error, Toast.LENGTH_SHORT).show()
}
/**
* Called when an item is clicked in the list
*/
override fun OnItemClicked(item: Beer) {
TODO("Not yet implemented")
}
/**
* Called when last list item is displayed.
* Used to fetch more elements
*/
override fun OnLastItemScrolled() {
page++;
repo.getBeers(page)
}
}

View File

@ -29,7 +29,7 @@ class BeersRepository {
override fun onFailure(call: Call<List<Beer>>, t: Throwable) {
if (t.message != null)
Log.e(TAG, "Unable to obtain beers: ${t.message!!}")
Log.e(TAG, "Unable to obtain beers: ${t}")
listener?.onFailure()
}
})

View File

@ -1,27 +1,21 @@
package it.danieleverducci.subitobeers.entities
import com.google.gson.annotations.SerializedName
import org.json.JSONException
import org.json.JSONObject
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import java.util.*
class Beer {
val id: Int
val name: String
val tagline: String
val description: String
val firstBrewed: LocalDate
class Beer// Parse date
@Throws(JSONException::class) constructor() {
val id: Int = 0
val name: String = ""
val tagline: String = ""
val description: String = ""
@SerializedName("image_url")
val imageUrl: String = ""
// @SerializedName("first_brewed")
// val firstBrewed: Date? = null
@Throws(JSONException::class)
constructor(jo: JSONObject) {
this.id = jo.getInt("id")
this.name = jo.getString("name")
this.tagline = jo.getString("tagline")
this.description = jo.getString("description")
// Parse date
val formatter = DateTimeFormatter.ofPattern("mm/yyyy", Locale.ENGLISH)
this.firstBrewed = LocalDate.parse(jo.getString("first_brewed"), formatter)
}
}

View File

@ -1,5 +1,6 @@
package it.danieleverducci.subitobeers.networking
import com.google.gson.FieldNamingPolicy
import com.google.gson.GsonBuilder
import it.danieleverducci.subitobeers.Config
import okhttp3.OkHttpClient

View File

@ -7,9 +7,8 @@
<ImageView
android:id="@+id/beer_item_pic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_width="60dp"
android:layout_height="100dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:src="@mipmap/ic_launcher"
@ -23,13 +22,15 @@
android:id="@+id/beer_item_name"
style="@style/Widget.AppCompat.TextView"
android:layout_width="0dp"
android:layout_height="19dp"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="10dp"
android:ellipsize="end"
android:lines="1"
android:maxLines="1"
android:text="Beer name"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/beer_item_descr"
app:layout_constraintEnd_toEndOf="parent"
@ -39,12 +40,12 @@
<TextView
android:id="@+id/beer_item_descr"
android:layout_width="0dp"
android:layout_height="21dp"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginEnd="10dp"
android:layout_marginBottom="16dp"
android:ellipsize="end"
android:lines="1"
android:maxLines="1"
android:maxLines="3"
android:text="Beer description lorem ipsum dolor sit amet"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"