0

I am using Firebase recyclerview, while it works great, but I am observing a lag in rendering photos everytime user scroll so in order to fix(Recyclerview painfully slow to load cached images form Picasso) I am trying to setHasFixedSize(true) and setItemViewCacheSize(true) but problem is when I set then my recycler view shows nothing when started( completely blank), Please let me know what wrong I am doing.

HomeActivity:

package com.apnabazzar

import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import androidx.appcompat.app.ActionBarDrawerToggle
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.navigation.NavigationView
import androidx.navigation.ui.AppBarConfiguration
import androidx.drawerlayout.widget.DrawerLayout
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.apnabazzar.constant.PNAME_PROPERTY
import com.apnabazzar.constant.PRODUCTS_DB_NAME
import com.apnabazzar.constant.PRODUCT_STATE
import com.apnabazzar.constant.PRODUCT_STATE_ACTIVE
import com.apnabazzar.model.Orders
import com.apnabazzar.model.Products
import com.apnabazzar.prevalent.Prevalent
import com.apnabazzar.viewholder.ProductViewHolder
import com.firebase.ui.database.FirebaseRecyclerAdapter
import com.firebase.ui.database.FirebaseRecyclerOptions
import com.firebase.ui.database.SnapshotParser
import com.google.firebase.database.DatabaseReference
import com.google.firebase.database.FirebaseDatabase
import com.squareup.picasso.Picasso
import de.hdodenhof.circleimageview.CircleImageView
import io.paperdb.Paper

class HomeActivity : AppCompatActivity() {
    private var searchInputText:String = ""
    private lateinit var appBarConfiguration: AppBarConfiguration
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_home)
        Paper.init(this)
        val toolbar: Toolbar = findViewById(R.id.toolbar)
        toolbar.title = "Home"
        setSupportActionBar(toolbar)
        val fab: FloatingActionButton = findViewById(R.id.fab)
        fab.setOnClickListener { view ->
            val intent = Intent(this@HomeActivity, CartActivity::class.java)
            startActivity(intent)
        }

        val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
        val navView: NavigationView = findViewById(R.id.nav_view)
        val toggle = ActionBarDrawerToggle(
            this, drawerLayout, toolbar, 0, 0
        )
        drawerLayout.addDrawerListener(toggle)
        toggle.syncState()
        // Passing each menu ID as a set of Ids because each
        // menu should be considered as top level destinations.
        appBarConfiguration = AppBarConfiguration(
            setOf(
                R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow
            ), drawerLayout
        )
        navView.setNavigationItemSelectedListener {
            when (it.itemId) {
                R.id.nav_cart -> {
                    val intent = Intent(this@HomeActivity, CartActivity::class.java)
                    startActivity(intent)
                    true
                }

                R.id.nav_logout -> {
                    // handle click
                    Paper.book().destroy()
                    val intent = Intent(this@HomeActivity, MainActivity::class.java)
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
                    startActivity(intent)
                    true
                }
                else -> false
            }
        }
        val headerView:View = navView.getHeaderView(0)
        val userNameTextView:TextView = headerView.findViewById(R.id.user_profile_name)
        userNameTextView.text = Prevalent.currentOnlineUser?.name
        val profileImageView:CircleImageView = headerView.findViewById(R.id.user_profile_image)
        if(!Prevalent.currentOnlineUser?.image.isNullOrBlank()){
            Picasso.get().load(Prevalent.currentOnlineUser?.image).placeholder(R.drawable.profile).into(profileImageView)
        }
        val recyclerView:RecyclerView = findViewById(R.id.recycler_menu)
        //recyclerView.setHasFixedSize(true) ----Not working
        //recyclerView.setItemViewCacheSize(10) -- not working
        val layoutManager:RecyclerView.LayoutManager = LinearLayoutManager(this)
        recyclerView.layoutManager = layoutManager
        val searchText: EditText = findViewById(R.id.home_search_product_name)
        val searchProductsBtn: Button = findViewById(R.id.home_search_products_btn)
        searchProductsBtn.setOnClickListener {
            searchInputText = searchText.text.toString()
            onStart()
        }
    }

    override fun onStart() {
        super.onStart()
        var productRef: DatabaseReference = FirebaseDatabase.getInstance().reference.child(PRODUCTS_DB_NAME).child(
            PRODUCT_STATE_ACTIVE)
        var options:FirebaseRecyclerOptions<Products>? = null
        if(searchInputText.isNullOrBlank()){
            options = FirebaseRecyclerOptions.Builder<Products>().setQuery(productRef, Products::class.java).
            setLifecycleOwner(this).build()
        } else {
            options = FirebaseRecyclerOptions.Builder<Products>().setQuery(productRef.orderByChild(PNAME_PROPERTY).
            startAt(searchInputText), Products::class.java).
            setLifecycleOwner(this).build()
        }
        val adapter = object : FirebaseRecyclerAdapter<Products, ProductViewHolder>(options) {
            override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductViewHolder {
                return ProductViewHolder(LayoutInflater.from(parent.context)
                    .inflate(R.layout.product_items_layout, parent, false))
            }

            protected override fun onBindViewHolder(holder: ProductViewHolder, position: Int, model: Products) {
                holder.txtProductName.text = model.pname
                holder.txtProductDescription.text = model.description
                holder.txtProductPrice.text = "Price = ₹ " + model.price.toString()
                Picasso.get().load(model.image).into(holder.imageView)
                holder.itemView.setOnClickListener {
                    val intent:Intent = Intent(this@HomeActivity, ProductDetailsActivity::class.java)
                    intent.putExtra("pid", model.pid)
                    startActivity(intent)
                }
            }
        }
        val recyclerView:RecyclerView = findViewById(R.id.recycler_menu)
        recyclerView.setAdapter(adapter)
        adapter.startListening()
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        // Inflate the menu; this adds items to the action bar if it is present.
        menuInflater.inflate(R.menu.home, menu)
        return true
    }
}

View Holder:

class ProductViewHolder(itemView: View): RecyclerView.ViewHolder(itemView), View.OnClickListener{
    val txtProductName: TextView = itemView.findViewById(R.id.product_name)
    val txtProductDescription: TextView = itemView.findViewById(R.id.product_description)
    val imageView: ImageView = itemView.findViewById(R.id.product_image)
    val txtProductPrice: TextView = itemView.findViewById(R.id.product_price)
    override fun onClick(v: View?) {

    }

}

content_home

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:showIn="@layout/app_bar_home">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_menu"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scrollbars="vertical">

    </androidx.recyclerview.widget.RecyclerView>
</RelativeLayout>

build.gradle

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"

    defaultConfig {
        applicationId "com.apnabazzar"
        minSdkVersion 23
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

// To inline the bytecode built with JVM target 1.8 into
// bytecode that is being built with JVM target 1.6. (e.g. navArgs)


    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = "1.8"
    }

}
repositories {
    maven { url 'https://jitpack.io' }
}
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.core:core-ktx:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'com.google.firebase:firebase-analytics:17.2.2'
    implementation 'com.google.firebase:firebase-database:19.3.0'
    implementation 'com.google.firebase:firebase-storage:19.1.1'
    implementation 'com.firebaseui:firebase-ui-database:6.2.1'
    implementation 'com.squareup.picasso:picasso:2.71828'
    implementation 'com.github.rey5137:material:1.3.0'
    implementation 'io.paperdb:paperdb:2.7.1'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    implementation 'com.google.android.material:material:1.1.0'
    implementation 'androidx.navigation:navigation-fragment:2.2.2'
    implementation 'androidx.navigation:navigation-ui:2.2.2'
    implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
    implementation 'androidx.navigation:navigation-fragment-ktx:2.2.2'
    implementation 'androidx.navigation:navigation-ui-ktx:2.2.2'
    implementation 'de.hdodenhof:circleimageview:3.1.0'
    implementation 'com.theartofdev.edmodo:android-image-cropper:2.8.+'
    implementation 'com.cepheuen.elegant-number-button:lib:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

apply plugin: 'com.google.gms.google-services'
Rajesh
  • 2,934
  • 8
  • 42
  • 71
  • If you encounter problems, it's best to create a [MCVE](https://stackoverflow.com/help/mcve) when posting a question. You posted **more than 250 lines of code** for this issue. That's a lot for people to parse and try to debug online. Please edit your question and isolate the problem, in that way you increase your chances of being helped. – Alex Mamo Jun 07 '20 at 10:25

1 Answers1

0

I was able to solve, issue was with Picasso. By default picasso will try to load all image everytime we scroll, rather it should cache image locally and should reload if image changes at server. Below was very helpful

How do I use disk caching in Picasso?

Rajesh
  • 2,934
  • 8
  • 42
  • 71