Sid
Sid

Reputation: 2862

Lazy Loading not working properly

I am showing a list of contacts in a recyclerview. I am showing contact profile images, first I am loading these images from server and then storing these images in an external memory.

Then Loading the images from external storage. I can see the images loaded, but as I scroll I can see some images for a second or two then they get disappear and I can see the default image icon.

public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactHolder> {


    private List<Contact> contactList;
    File myDir1;
    private Activity mContext;

    private Boolean fileExists;
    private File file;
    private static final int MY_PERMISSIONS_REQUEST_CALL= 20;

    public ContactAdapter(Activity context, List<Contact> contactList) {
        this.contactList = contactList;
        this.mContext = context;
    }

    @Override
    public ContactHolder onCreateViewHolder(ViewGroup viewGroup, int i) {

        View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_layout,null);
        ContactHolder mh = new ContactHolder(v);

        return mh;
    }

    @Override
    public void onBindViewHolder(final ContactHolder contactHolder, int i) {

        final Contact contact = contactList.get(i);
        //  Log.e("Imagename",""+"http://xesoftwares.co.in/contactsapi/profile_images/85368a5bbd6cffba8a3aa202a80563a2.jpg");//+feedItem.getThumbnail());

        Target target = new Target() {

            @Override
            public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {

                // your code here ...

                bitmap = Bitmap.createScaledBitmap(bitmap,(int)(bitmap.getWidth()*0.8), (int)(bitmap.getHeight()*0.8), true);

                contactHolder.thumbnail.setImageBitmap(bitmap);

                Log.e("ProfileImage", contact.getmProfileImage());

                SaveImages(bitmap, contact.getmProfileImage());

            }

            @Override
            public void onBitmapFailed(Drawable errorDrawable) {
                contactHolder.thumbnail.setImageDrawable(errorDrawable);
                // do error handling as required
            }

            @Override
            public void onPrepareLoad(Drawable placeHolderDrawable) {
                contactHolder.thumbnail.setImageDrawable(placeHolderDrawable);
            }
        };

        contactHolder.thumbnail.setTag(target);

        String url = ServiceUrl.getBaseUrl() + ServiceUrl.getImageUserUrl() + contact.getmProfileImage();

        Log.e("url",url);

        if(contact.getmProfileImage().equals(""))

        {

            file = new File("");

            fileExists = file.exists();

            contactHolder.thumbnail.setImageDrawable(ContextCompat.getDrawable(mContext, R.drawable.ic_account_circle_black_48dp));
        }
        else {

            file = new File(Environment.getExternalStorageDirectory() + "/ContactProfileImages/" + contact.getmProfileImage());

            fileExists = file.exists();
        }

        if(fileExists)
        {

            Log.e("fileExists",file.getAbsolutePath());

            BitmapFactory.Options bmOptions = new BitmapFactory.Options();
            Bitmap bitmap = BitmapFactory.decodeFile(file.getPath(), bmOptions);

            contactHolder.thumbnail.setImageBitmap(bitmap);

        }

        else {

            Log.e("Picasso",file.getAbsolutePath());

            Picasso.with(mContext).load(url)
                    .error(R.drawable.ic_account_circle_black_24dp)
                    .placeholder(R.drawable.ic_account_circle_black_24dp)
                    .into(target);
        }

        contactHolder.title.setText(contact.getmUserName());
        //feedListRowHolder.genre.setText(Html.fromHtml(feedItem.getGenre()));

    }

    @Override
    public int getItemCount() {
        return (null != contactList ? contactList.size() : 0);
    }

    public void SaveImages(Bitmap bitmap,String profileName)
    {

        try {
            String root = Environment.getExternalStorageDirectory().getPath();
            File myDir = new File(root +"/ContactProfileImages");

            if (!myDir.exists()) {
                myDir.mkdirs();
            }

            // String name = new Date().toString();=
            String name = profileName;
            File myDir1 = new File(myDir, name);
            if(!myDir1.exists()) {
                FileOutputStream out = new FileOutputStream(myDir1);
                bitmap.compress(Bitmap.CompressFormat.PNG,100,out);

                out.flush();
                out.close();
            }


        } catch(Exception e){
            // some action
        }

        //myDir1= imageFilePath1.getprofile();

    }


    public class ContactHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        protected CircleImageView thumbnail;
        protected TextView title;

        public ContactHolder(View view) {
            super(view);
            this.thumbnail = (CircleImageView) view.findViewById(R.id.thumbnail);
            this.title = (TextView) view.findViewById(R.id.title);
            view.setOnClickListener(this);

        }

        @Override
        public void onClick(View v) {

            final Contact contact = contactList.get(getAdapterPosition());

            final Dialog dialog = new Dialog(mContext);
            dialog.setCanceledOnTouchOutside(true);
            dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
            dialog.setContentView(R.layout.custom);
            final Window window = dialog.getWindow();

            WindowManager.LayoutParams wlp =window.getAttributes();
            wlp.gravity = Gravity.CENTER_HORIZONTAL|Gravity.TOP;
            wlp.y=320;
            window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
            window.setAttributes(wlp);

            // set the custom dialog components - text, image and button
            TextView txtusername = (TextView) dialog.findViewById(R.id.txtusername);
            TextView txtmobile = (TextView) dialog.findViewById(R.id.txtmobile);
            TextView txtemail = (TextView) dialog.findViewById(R.id.txtemail);

            txtusername.setText(contact.getmUserName());
            txtemail.setText(contact.getmEmailId());
            txtmobile.setText(contact.getmMobileNo());

            SquareImageView image = (SquareImageView) dialog.findViewById(R.id.image);
            ImageView image1 = (ImageView) dialog.findViewById(R.id.image1);
            ImageView image2 = (ImageView) dialog.findViewById(R.id.image2);
            ImageView image3 = (ImageView) dialog.findViewById(R.id.image3);

            if(contact.getmProfileImage().equals(""))

            {
                image.setImageDrawable(ContextCompat.getDrawable(mContext,R.drawable.profile_icon));
            }
            else {
                File file = new File(Environment.getExternalStorageDirectory() + "/ContactProfileImages/" + contact.getmProfileImage());
                BitmapFactory.Options bmOptions = new BitmapFactory.Options();
                Bitmap bitmap = BitmapFactory.decodeFile(file.getPath(), bmOptions);

                image.setImageBitmap(bitmap);
            }

            image1.setImageResource(R.drawable.ic_call_black_24dp);
            image2.setImageResource(R.drawable.ic_textsms_black_24dp);
            image3.setImageResource(R.drawable.ic_email_black_24dp);

            image2.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {

                    Uri sms_uri = Uri.parse("smsto:" + contact.getmMobileNo());
                    Intent sms_intent = new Intent(Intent.ACTION_SENDTO, sms_uri);
                    mContext.startActivity(sms_intent);
                    dialog.dismiss();

                }
            });

            image1.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {

                        Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + contact.getmMobileNo()));
                        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
                        mContext.startActivity(intent);
                        dialog.dismiss();
                }
            });

            image3.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Intent email = new Intent(Intent.ACTION_SEND);
                    email.putExtra(Intent.EXTRA_EMAIL, new String[]{contact.getmEmailId()});
                    email.setType("message/rfc822");
                    mContext.startActivity(Intent.createChooser(email, "Choose an Email client :"));

                }
            });

            Button dialogButton = (Button) dialog.findViewById(R.id.dialogButtonOK);
            // if button is clicked, view all information custom dialog

            dialogButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {

                    ((MainActivity)mContext).finish();

                    Intent intent = new Intent(mContext,DetailViewActivity.class);
                    intent.putExtra("contact",contact);
                    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
                    intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
                    mContext.startActivity(intent);

                    dialog.dismiss();


                }
            });
            dialog.show();

        }
    }
 }

EDIT : I searched for the same issue with picasso library, when we load images from server, for this I got the solution from SO as :

   recyclerView.setHasFixedSize(true);
    recyclerView.setItemViewCacheSize(20);
    recyclerView.setDrawingCacheEnabled(true);
    recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);

And this worked when I am loading the images from server, but when I am loading the images from the external storage still the problem exists.

Can anyone help with this please? Thank you..

Upvotes: 12

Views: 1518

Answers (6)

Nandan Singh
Nandan Singh

Reputation: 1089

use this code in your Application class to apply setting in whole app.

final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = maxMemory / 8;
Picasso.Builder builder = new Picasso.Builder(getApplicationContext());
builder.memoryCache(new LruCache(cacheSize));
Picasso built = builder.build();
built.setIndicatorsEnabled(true);// it will show indicator where it downloaded from
built.setLoggingEnabled(false);
Picasso.setSingletonInstance(built);

now pass the url to Picasso to load it to view .If you are dealing with large image you can resize image size also for faster loading.

Picasso
    .with(mContext)
    .load(url)
    .error(R.drawable.on_error)
    .placeholder(R.drawable.place_holder)
    //.resize(custom_width,custom_height)  put custom_height to 0 if you want to maintain aspect ratio
    .into(your_view);

Upvotes: 1

Stephen
Stephen

Reputation: 10059

  • Check every images how much MB it is.For Example : If the image is 1mb or 500kb it will take more time to download images in background and after downloaded it will store in catche.That means first time only it will take some time to download the image.

  • on the second time, those images from catche.It will show all the images in the fraction of seconds.

  • Use Volley - for faster and Glide - for Memory management.

    Glide
        .with(context)
        .load(rowItem.getPosteduserpostimage())
        .asBitmap()
        .diskCacheStrategy(DiskCacheStrategy.SOURCE)
        .fitCenter()
        .placeholder(R.drawable.load_image)
        .error(R.drawable.cancel_image)
        .into(holder.ivPostedImageNew);
    
  • Refer here how to load large size bitmap.You need to scale down image.

  • Otherwise, When I'm uploading every image, I did crop using library.It will take care of scale down large bitmap.for eg: 5mb images will be scale down to 180 kb.

  • Check this CropImage lib to scale down bigger size images.Even if user not crop image,just select the entire portion when cropping, it will scale down 5 mb to 180kb.Image Quality also good.

I think without crop also you can use the below code to scale down image.You can see this code in that:

private void startCropImage() {

    Intent intent = new Intent(this, CropImage.class);

    intent.putExtra(CropImage.IMAGE_PATH, mFileTemp.getPath());
    intent.putExtra(CropImage.SCALE, true);
    intent.putExtra(CropImage.ASPECT_X, 0); //3 for aspect ratio
    intent.putExtra(CropImage.ASPECT_Y, 0); //2 for aspect ratio

    startActivityForResult(intent, REQUEST_CODE_CROP_IMAGE);
}
  • And also don't use nested scroll view inside recyclerview.

Upvotes: 0

Sam
Sam

Reputation: 546

Then Loading the images from external storage. I can see the images loaded, but as I scroll I can see some images for a second or two then they get disappear and I can see the default image icon.

This is becuase you are using Target callback in picasso. And callbacks are called little bit late when you are scrolling the list. Just remove the target and use the imageview into Picasso, it should work fine. Also you don't need to cache bitmaps on your own as Picasso does it for you.

public void onBindViewHolder(final ContactHolder contactHolder, int i) {
    final Contact contact = contactList.get(i);
    //  Log.e("Imagename",""+"http://xesoftwares.co.in/contactsapi/profile_images/85368a5bbd6cffba8a3aa202a80563a2.jpg");//+feedItem.getThumbnail());

    String url = ServiceUrl.getBaseUrl() + ServiceUrl.getImageUserUrl() + contact.getmProfileImage();
    Log.e("url",url);

    if(contact.getmProfileImage().equals("")) {
        file = new File("");
        fileExists = file.exists();
        contactHolder.thumbnail.setImageDrawable(ContextCompat.getDrawable(mContext, R.drawable.ic_account_circle_black_48dp));
    } else {
        file = new File(Environment.getExternalStorageDirectory() + "/ContactProfileImages/" + contact.getmProfileImage());
        fileExists = file.exists();
    }

    Log.e("Picasso",file.getAbsolutePath());

    Picasso.with(mContext).load(url)
        .error(R.drawable.ic_account_circle_black_24dp)
        .placeholder(R.drawable.ic_account_circle_black_24dp)
        .into(contactHolder.thumbnail);

    contactHolder.title.setText(contact.getmUserName());
    //feedListRowHolder.genre.setText(Html.fromHtml(feedItem.getGenre()));
}

Upvotes: 4

Aleksandar G
Aleksandar G

Reputation: 1181

Operations like loading images should be performed using AsyncTask on a background thread.

Upvotes: 0

Sanat Chandravanshi
Sanat Chandravanshi

Reputation: 665

If you wanna simply load the image then why you need to store in the external storage.if you have to store for any other purpose then store it but when you show it in the ImageView then please use any third party api like Picasso or Universal Image Loader it does lazy loading and also it does all type of cache memory management also provies many display fasality.

Hope this will help you

Upvotes: 0

romtsn
romtsn

Reputation: 11982

I may mistake, but your problem could be caused by that you use Contact that you got from your dataset by layout position, but since you use it asynchroniously in onBitmapLoaded callback, the position could already be changed at that moment. So all you need is to use adapter position as follows:

@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {

     // your code here ...

    Contact c = contactList.get(contactHolder.getAdapterPosition());

    bitmap = Bitmap.createScaledBitmap(bitmap,(int)(bitmap.getWidth()*0.8), (int)(bitmap.getHeight()*0.8), true);

    contactHolder.thumbnail.setImageBitmap(bitmap);

    Log.e("ProfileImage", c.getmProfileImage());

    SaveImages(bitmap, c.getmProfileImage());
}

I'm not sure that it would be helpful, but let's try, and I hope it will be:)

Upvotes: 0

Related Questions