1

I am using the Spoonacular API which provides information on recipes. Naturally, the API provides a link to an image, similar to a preview, except that sometimes the link is to a default image: https://spoonacular.com/recipeImages/310822-90x90.jpg

leading to:RecyclerView with default image

I am using Glide to load the image inside an ImageView, inside a RecyclerView, using the onBindViewHolder() method. However, I don't want the View to be inflated if the image loaded inside it will be the default image (above). I can't seem to find a way to compare whether the image that would be loaded is equivalent to the default image. Unfortunately, the URLs to the recipe images are all different as well so a simple string comparison won't work... Any suggestions would be welcome.

Update: I had a go at what you suggested and it sorta worked? Something really strange seems to be happening. So when the view loads it looks like this and when I scroll down the View then back up it looks like this which seems like it's fixed it, however, it's just a repetition of the items in 18~22. Is this an issue with the API? Can't wrap my head around what's going on... package com.example.recipeapi;

import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.RecyclerView;

import com.bumptech.glide.Glide;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import static androidx.core.content.ContextCompat.startActivity;

public class RecipesAdapter extends RecyclerView.Adapter<RecipesAdapter.RecipeViewHolder> {
    private LayoutInflater mInflater;
    private JSONArray jsonArray;
    private Context context;

    public RecipesAdapter(Context context, JSONArray jsonArray) {
        this.mInflater = LayoutInflater.from(context);
        this.jsonArray = jsonArray;
        this.context = context;
    }

    @NonNull
    @Override
    public RecipeViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        ConstraintLayout constraintLayout = (ConstraintLayout) mInflater.inflate(R.layout.item_view, parent, false);
        return new RecipeViewHolder(constraintLayout, this);
    }

    @Override
    public void onBindViewHolder(@NonNull final RecipeViewHolder holder, int position) {
        try {
            final String title, prepTime, servings, imageURL, TEMPNOIMAGE;
            JSONObject object = (JSONObject) jsonArray.get(position);
            imageURL = String.format("https://spoonacular.com/recipeImages/%s-90x90.jpg", object.get("id"));

//            imported code start
            final Bitmap[] defaultImg = new Bitmap[1];

            Glide.with(context).asBitmap().load("https://spoonacular.com/recipeImages/310822-90x90.jpg").listener(new RequestListener<Bitmap>() {
                @Override
                public boolean onLoadFailed(@Nullable GlideException e, Object o, Target<Bitmap> target, boolean b) {
                    Toast.makeText(context, "unexpected error, try again", Toast.LENGTH_SHORT).show();
                    System.out.println("error 1");
                    return false;
                }

                @Override
                public boolean onResourceReady(Bitmap bitmap, Object o, Target<Bitmap> target, DataSource dataSource, boolean b) {
                    defaultImg[0] = bitmap;
                    return false;
                }
            }).submit();

// before setting image use sameAs() method on bitmap
            Glide.with(context).asBitmap().load(imageURL).listener(new RequestListener<Bitmap>() {
                @Override
                public boolean onLoadFailed(@Nullable GlideException e, Object o, Target<Bitmap> target, boolean b) {
                    Toast.makeText(context, "unexpected error", Toast.LENGTH_SHORT).show();
                    System.out.println("error 2");
                    return false;
                }

                @Override
                public boolean onResourceReady(Bitmap bitmap, Object o, Target<Bitmap> target, DataSource dataSource, boolean b) {
                    if (!bitmap.sameAs(defaultImg[0]))
//                        mimageView.setImage(ImageSource.bitmap(bitmap));
//                        holder.mImageView.setImage(ImageSource.bitmap(bitmap));
                        holder.mImageView.setImageBitmap(bitmap);
                    Glide.with(context).load(bitmap).into(holder.mImageView);
                    return false;
                }
            }).submit();
//            imported code end

//            Checking to see if there is an image first!
//            TEMPNOIMAGE = "https://spoonacular.com/recipeImages/310822-90x90.jpg";
//            Glide.with(context).load(imageURL).into(holder.mImageView);

            title = position + 1 + " " + object.getString("title");
//            Check if prep time is greater than 100 minutes
            prepTime = object.getString("readyInMinutes");
            if (Integer.parseInt(prepTime) > 99)
                holder.mPrepTimeTextView.setText("99+ minutes");
            else
                holder.mPrepTimeTextView.setText(prepTime + " minutes");

            servings = "Serves: " + object.getString("servings");
            holder.mTitleTextView.setText(title);
            holder.mServingsSizeTextView.setText(servings);
//            holder.mPrepTimeTextView.setText(prepTime);
            holder.url = Uri.parse(object.getString("sourceUrl").toString());

        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    @Override
    public int getItemCount() {
        return jsonArray.length();
    }

    public class RecipeViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        private Uri url;
        private ConstraintLayout view;
        private RecipesAdapter recipesAdapter;
        private TextView mTitleTextView, mPrepTimeTextView, mServingsSizeTextView;
        private ImageView mImageView;

        public RecipeViewHolder(@NonNull ConstraintLayout itemView, RecipesAdapter recipesAdapter) {
            super(itemView);
            this.recipesAdapter = recipesAdapter;
            this.view = itemView.findViewById(R.id.recipeConstraintLayout);
            this.view.setOnClickListener(this);
            this.mTitleTextView = itemView.findViewById(R.id.title_textview);
            this.mPrepTimeTextView = itemView.findViewById(R.id.time_required_textview);
            this.mServingsSizeTextView = itemView.findViewById(R.id.servings_textview);
            this.mImageView = itemView.findViewById(R.id.imageView);
        }

        @Override
        public void onClick(View view) {
            Toast.makeText(view.getContext(), ((TextView) view.findViewById(R.id.title_textview)).getText(), Toast.LENGTH_SHORT).show();
            Intent intent = new Intent(Intent.ACTION_VIEW, url);
            if (intent.resolveActivity(view.getContext().getPackageManager()) != null) {
                startActivity(view.getContext(), intent, null);
            } else {
                Log.d("ImplicitIntents", "Intent can't be handled");
            }

        }
    }
}
220284
  • 95
  • 7
  • what is the url in `Glide.with(context).load(**url**)` ? it is possible to compare it, if not then post the code for you recycle view – Nikhil Sharma Jul 31 '20 at 03:50
  • ok I got it what you are trying, the url value is `https://spoonacular.com/recipeImages/{ID}-{SIZE}.{TYPE}` – Nikhil Sharma Jul 31 '20 at 04:03

1 Answers1

1

You can save the bitmap from url https://spoonacular.com/recipeImages/310822-90x90.jpg and then compare it with the actal image's bitmap, some suggestion as follows -

//get a default img to a bitmap
Bitmap defaultImg;

Glide.with(cxt).asBitmap().load("https://spoonacular.com/recipeImages/310822-90x90.jpg").listener(new RequestListener<Bitmap>() {
    @Override
    public boolean onLoadFailed(@Nullable GlideException e, Object o, Target<Bitmap> target, boolean b) {
        Toast.makeText(cxt,getResources().getString(R.string.unexpected_error_occurred_try_again),Toast.LENGTH_SHORT).show();
        return false;
    }

    @Override
    public boolean onResourceReady(Bitmap bitmap, Object o, Target<Bitmap> target, DataSource dataSource, boolean b) {
        defaultImg = bitmap;
        return false;
    }
}).submit();

// before setting image use sameAs() method on bitmap
Glide.with(cxt).asBitmap().load(imageUrl).listener(new RequestListener<Bitmap>() {
    @Override
    public boolean onLoadFailed(@Nullable GlideException e, Object o, Target<Bitmap> target, boolean b) {
        Toast.makeText(cxt,getResources().getString(R.string.unexpected_error_occurred_try_again),Toast.LENGTH_SHORT).show();
        return false; 
    }

    @Override
    public boolean onResourceReady(Bitmap bitmap, Object o, Target<Bitmap> target, DataSource dataSource, boolean b) {
        if(!bitmap.sameAs(defaultImg)) 
            imageView.setImage(ImageSource.bitmap(bitmap));
        return false;
    }
}).submit();

There could be optimization on the above code as well but the idea will be same.

Get more information on bitmap.sameAs() and bitmap with Glide

Edit 1 (Repro Issue)

I try to repro the issue with following code, you can check result

public class DashboardFragment extends Fragment {

    View root;
    private static final String TAG = "DashboardFragment";
    private Bitmap defaultImage;
    private ImageView imageView;
    private TextView textView;
    private int imageID;
    private Bitmap defaultImageBitmap;
    ...........

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        ..........

        String[] imageUrl = {"https://spoonacular.com/recipeImages/310822-90x90.jpg" //Default Image
                , "https://spoonacular.com/recipeImages/579247-90x90.jpg"             // Actual Image
                , "https://spoonacular.com/recipeImages/556247-90x90.jpg"              // Actual Image
                , "https://spoonacular.com/recipeImages/328822-90x90.jpg"             //Default Image
                , "https://spoonacular.com/recipeImages/716429-90x90.jpg"};           //Actual Image

        Button loadImage = view.findViewById(R.id.load_another);
        imageView = view.findViewById(R.id.image);
        textView = view.findViewById(R.id.image_url);
        imageID = 0;

        defaultImage = BitmapFactory.decodeResource(requireContext().getResources(), R.drawable.test_recipe_img);

        Picasso.with(requireContext()).load("https://spoonacular.com/recipeImages/310822-90x90.jpg").into(new Target() {
            @Override
            public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                defaultImageBitmap = bitmap;
            }

            @Override
            public void onBitmapFailed(Drawable errorDrawable) {
            }

            @Override
            public void onPrepareLoad(Drawable placeHolderDrawable) {

            }
        });


        loadImage.setOnClickListener(v -> {
            if (imageID == 5)
                imageID = 0;
            loadImage(imageUrl[imageID]);
            textView.setText(imageUrl[imageID]);
            imageID++;

        });
    }

    private void loadImage(String imageUrl) {

        Glide.with(requireContext()).asBitmap().load(imageUrl).addListener(new RequestListener<Bitmap>() {
            @Override
            public boolean onLoadFailed(@Nullable GlideException e, Object model, com.bumptech.glide.request.target.Target<Bitmap> target, boolean isFirstResource) {
                return false;
            }

            @Override
            public boolean onResourceReady(Bitmap resource, Object model, com.bumptech.glide.request.target.Target<Bitmap> target, DataSource dataSource, boolean isFirstResource) {
                if (defaultImageBitmap.sameAs(resource))
                    Toast.makeText(requireContext(), "Default Image", Toast.LENGTH_SHORT).show();
                else {
                    imageView.setImageBitmap(resource);
                }
//                Log.d(TAG, "Default Image: " + defaultImage.toString());
//                Log.d(TAG, "Requested Image: " + resource.toString());
                return true;
            }
        }).submit();
    }
}

I used Picasso and Glide both and result was quite unpredictable. Tried diskCacheStrategy(DiskCacheStrategy.NONE) and skipMemoryCache(true) no success. As I'm not an expert of image processing so this might need some more attention.

Happy Coding !

Nikhil Sharma
  • 897
  • 1
  • 4
  • 18