Hot questions for Using Glide in retrofit

Question:

I have ListView, where I have ImageView. I need to get the image from url and show in ImageView, but that's not working, the image is not visible. In that ListView I have a TextView and CheckBox too, but you not need it because that works. I'm using Glide. So what's the problem?

I set in glide placeholders and it loads the placeholders. I've done debug and I saw that the glide gets the image URL. But the image doesn't load.

Ok here's the item code.

public class LanguageItem {

String imagePath;

LanguageItem(String imagePath,) {
    this.imagePath = imagePath;
}

public String getImagePath() {
    return imagePath;
}

There are textView and checkbox too, but I'm not showing it you, because that works.

Here the adapter.

public class LanguageAdapter extends BaseAdapter {

private Context context;
private LayoutInflater lInflater;
private ArrayList<LanguageItem> objects;

LanguageAdapter(Context context, ArrayList<LanguageItem> itemObj) {
    this.context = context;
    objects = itemObj;
    lInflater = (LayoutInflater) context
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}

//amount of elements
@Override
public int getCount() {
    return objects.size();
}

//element by position
@Override
public Object getItem(int position) {
    return objects.get(position);
}

//id by position
@Override
public long getItemId(int position) {
    return position;
}


@Override
public View getView(int position, View convertView, ViewGroup parent) {

    View view = convertView;
    if (view == null) {
        view = lInflater.inflate(R.layout.language_items, parent, false);
    }

    ImageView imageView = (ImageView) view.findViewById(R.id.imageView);

Glide.with(context).load(objects.get(position).getImagePath()).thumbnail(0.5f).crossFade().into(imageView);


    return view;

}

And here's the fragment. I'm doing my work in fragment.

public class FragmentLanguage extends BaseFragment {

private static final String IMAGE = "IMAGE";
private ApiClient apiClient;
private ArrayList<LanguageItem> objS;
private LanguageAdapter adapter;

private View mainView;
private ListView listView;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    mainView = inflater.inflate(R.layout.languages, container, false);
    listView = (ListView) mainView.findViewById(R.id.language_list);

    apiClient = ApiClient.getInstance();

    //calling methods
    fillData();
    showResult();

    return mainView;
}

public void fillData() {

    objS = new ArrayList<>();

    getLanguageCall();

}

public void getLanguageCall() {
    Call<ResponseBody> getLanguage = apiClient.getLanguage(SharedPreferencesManager.getInstance().getAccessToken());
    getLanguage.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            if (response.isSuccessful()) {
                try {
                    String data = response.body().string();
                    JSONArray array = new JSONArray(data);


                    for (int i = 0; i < array.length(); i++) {
                        JSONObject object = array.getJSONObject(i);
                        String languageName = object.getString("name");
                        String path = object.getString("image_path");
                        String real_path = "https://supportop.eu-gb.mybluemix.net" + path.substring(1, path.length());


                        Toast.makeText(context, real_path, Toast.LENGTH_SHORT).show();
                        objS.add(new LanguageItem(languageName,real_path, false));
                    }
                    adapter = new LanguageAdapter(getActivity(), objS);
                    listView.setAdapter(adapter);

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

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            Toast.makeText(context, "Error", Toast.LENGTH_SHORT).show();
        }
    });
}

}

Ok here's the code. I done debug and the Image url it gets successfully, but glide not loads it. Thank you for reading.

Ok and here's the layout.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/white"
android:orientation="horizontal">

<ImageView
    android:id="@+id/imageView"
    android:layout_width="45dp"
    android:layout_height="30dp"
    android:layout_gravity="center"
    android:layout_marginStart="20dp" />

And here the listView part.

<ListView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/language_list"
    android:layout_marginTop="10dp"
    android:layout_below="@+id/linearLayout"
    android:layout_above="@+id/bottomNavigationView">

</ListView>

Answer:

Try to use http inside https. I think that's a server problem.

Question:

I have been using recyclerview to display my contacts and show their basic data including user images. It works as it should, Picasso is great and I get a very cool looking list.

Right now I am trying to disable users to view images until validated - ie I want to disable viewing images to anyone who is not an authenticated user. I have done this on my services and disabled anyone except my laravel app to open the image link. Now I face one issue - when I return image it is not a link, but it is in a byte[] format, and I cannot display it using Picasso. I could try and convert it to Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); and then convert bitmap to drawable like: Drawable drawable = new BitmapDrawable(getResources(), bitmap);, and then display drawable as image imageView.setImageDrawable(drawable); but reading into this I found quite a few drawbacks.

My question would be what is the best way to display image using picasso or glide, and retrofit to return images for authenticated users only. Is there a different way I can return image from laravel service? Is there a way to use the byte format with either picasso or glide? Or is there another format I can convert my byte[] to display in the recyclerview, that would not negatively influence performance?

Currently displaying image with Picasso in my ContactsAdapter:

@Override
public void onBindViewHolder(ContactsAdapter.ContactsViewHolder holder, int position) {
    Contact contact = contactList.get(position);

    holder.userName.setText(contact.getUserName());

    TextDrawable.IBuilder builder = TextDrawable.builder()
            .beginConfig()
            .withBorder(0)
            .toUpperCase()
            .endConfig()
            .round();

    ColorGenerator generator = ColorGenerator.MATERIAL;
    int color = generator.getColor(contact.getUserId());
    textDrawable = builder.build(contactList.get(position).getUserName().substring(0, 1), color);
    holder.thumbNail.setImageDrawable(textDrawable);

    Picasso.with(context)
            .load(AppConfig.URL_PROFILE_PHOTO + contact.getThumbnailUrl()) // DISK URL WITH USER IMAGE NAME
            .placeholder(textDrawable)
            .error(textDrawable)
            .transform(new CircleTransform())
            .into(holder.thumbNail);
}

Laravel service converts link to image like this:

return Image::make($storagePath)->response();

I extracted the ProfileImage method and tried to modify it to use the service I got:

private void ProfileImage(HashMap<String, String> user, String name, String id) {
    TokenManager tokenManager = TokenManager.getInstance(getSharedPreferences("prefs", MODE_PRIVATE));
    ApiInterface apiService = ApiClient.createServiceWithAuth(ApiInterface.class, tokenManager);

    ColorGenerator generator = ColorGenerator.MATERIAL;
    int color = generator.getColor(id);
    String firstLetter = name.substring(0, 1);
    TextDrawable textDrawable = TextDrawable.builder().buildRect(firstLetter, color);
    holder.thumbNail.setImageDrawable(textDrawable);

    Call<ResponseBody> call = apiService.getUserImage(id);
    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {

            if (response.isSuccessful()) {
                byte[] bytes = new byte[0];
                try {
                    bytes = response.body().bytes();

                    Picasso.with(getApplicationContext())
                            .load(/** I CANNOT USE THE URL HERE */)
                            .placeholder(textDrawable)
                            .error(textDrawable)
                            .centerCrop()
                            .fit()
                            .into(holder.thumbNail);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else {
                if (response.code() == 401) {
                    finish();
                } else {
                    Log.d(TAG, "There was an error: " + response);
                }
            }
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            Log.e(TAG, t.toString());
        }
    });
}

Answer:

If you can switch to Glide, you can use Glide.with().load(byte[] model) directly

bytes = response.body().bytes();

Glide.with(getApplicationContext())
     .load(bytes)
     .placeholder(textDrawable)
     .error(textDrawable)
     .centerCrop()
     .fitCenter()
     .into(holder.thumbNail);

Question:

I have an okay response received by retrofit and I'm able to show the model attributes on my view except for one list of images. I am getting a null value from it although I see the log has the required JSON data. As it is a list of images, I'm using Glide to display them and on the log says W/Glide: Load failed for null with size [0x0] class com.bumptech.glide.load.engine.GlideException: Received null model

The attribute I'm trying to access is promotionalimages in the class Data.

Do let me know if further information is required.

What am I doing wrong?

model:

RestaurantModel

part of my activity where im displaying the response:

Call<RestaurantModel> call = apiInterface.getRestaurant(getSharedPreference.getToken(), identifier);
    call.enqueue(new Callback<RestaurantModel>() {
        @Override
        public void onResponse(@NonNull Call<RestaurantModel> call, @NonNull Response<RestaurantModel> response) {
            restaurant = response.body();

            rName.setText(restaurant.getData().getName());
            ratingtxt.setText(String.format("%s", restaurant.getData().getAvgRatingCount()));
            Glide.with(context).load(restaurant.getData().getPromotionalimages().get(0).getPromotionalPicture()).apply(RequestOptions.centerCropTransform().placeholder(R.drawable.default_image)).into(holder.image);

the response:

{
"data": {
    "identifier": "0464eee2-5160-40e7-8ae0-77e7484fe251",
    "name": "Apple Lab",
    "phonenumber": "11116425300",
    "start_time": "7:00 AM",
    "end_time": "1:00 PM",
    "created_at": "2018-07-30 20:10:05",
    "updated_at": "2018-07-30 20:11:00",
    "reviews_count": 0,
    "avgRatingCount": 0,
    "categoryCount": 0,
    "subCategoryCount": 0,
    "totalItemCount": 0,
    "availableItemCount": 0,
    "isOpen": 1,
    "logo": "https://x.co/storage/logoandcover/0464eee2-5160-40e7-8ae0-77e7484fe251/15b5f1dc0ef064.png",
    "cover": "https://x.co/storage/logoandcover/0464eee2-5160-40e7-8ae0-77e7484fe251/15b5f1df0e2f78.png",
    "galleryimages": [
        {
            "id": 6,
            "identifier": "0464eee2-5160-40e7-8ae0-77e7484fe2511",
            "restaurant_id": 7,
            "gallery_picture": "https://x.co/storage/galleryimages/0464eee2-5160-40e7-8ae0-77e7484fe251/15b5f1e2cc80aa.png",
            "created_at": "2018-07-30 20:18:20",
            "updated_at": "2018-07-30 20:18:20"
        }
    ],
    "location": {
        "house": "x",
        "road": "x",
        "details": "x",
        "country": "x",
        "created_at": "2018-07-30 20:12:28",
        "updated_at": "2018-07-30 20:12:28",
        "cityName": "x",
        "areaName": "x"
    },
    "promotionalimages": [
        {
            "id": 10,
            "identifier": "0464eee2-5160-40e7-8ae0-77e7484fe2511",
            "restaurant_id": 7,
            "promotional_picture": "https://x.co/storage/promotionalimages/0464eee2-5160-40e7-8ae0-77e7484fe251/15b5f1ec0afffb.png",
            "created_at": "2018-07-30 20:20:48",
            "updated_at": "2018-07-30 20:20:48"
        },
        {
            "id": 11,
            "identifier": "0464eee2-5160-40e7-8ae0-77e7484fe2512",
            "restaurant_id": 7,
            "promotional_picture": "https://x.co/storage/promotionalimages/0464eee2-5160-40e7-8ae0-77e7484fe251/15b5f1ec13a9b7.png",
            "created_at": "2018-07-30 20:20:49",
            "updated_at": "2018-07-30 20:20:49"
        }
    ],
    "features": [
        {
            "id": 25,
            "restaurant_id": 7,
            "name": "Food Quality",
            "created_at": "2018-07-30 20:11:00",
            "updated_at": "2018-07-30 20:11:00",
            "favoritesCount": 0,
            "isFavorited": false,
            "favorites": []
        },
        {
            "id": 26,
            "restaurant_id": 7,
            "name": "Customer Service",
            "created_at": "2018-07-30 20:11:00",
            "updated_at": "2018-07-30 20:11:00",
            "favoritesCount": 0,
            "isFavorited": false,
            "favorites": []
        },
        {
            "id": 27,
            "restaurant_id": 7,
            "name": "Environment",
            "created_at": "2018-07-30 20:11:00",
            "updated_at": "2018-07-30 20:11:00",
            "favoritesCount": 0,
            "isFavorited": false,
            "favorites": []
        },
        {
            "id": 28,
            "restaurant_id": 7,
            "name": "Food Pricing",
            "created_at": "2018-07-30 20:11:00",
            "updated_at": "2018-07-30 20:11:00",
            "favoritesCount": 0,
            "isFavorited": false,
            "favorites": []
        }
    ],
    "reviews": [],
    "categories": [],
    "menus": [],
    "profile": {
        "id": 7,
        "restaurant_id": 7,
        "logo": "https://x.co/storage/logoandcover/0464eee2-5160-40e7-8ae0-77e7484fe251/15b5f1dc0ef064.png",
        "cover": "https://x.co/storage/logoandcover/0464eee2-5160-40e7-8ae0-77e7484fe251/15b5f1df0e2f78.png",
        "payable_amount": "0.00",
        "payment_received": "0.00",
        "last_paid": "2018-07-30 14:17:21",
        "score": "100.00",
        "vat_reg_number": null,
        "created_at": "2018-07-30 20:11:00",
        "updated_at": "2018-07-30 20:17:21",
        "status": "Bronze"
    }}}

Thank you so much for your time.


Answer:

In the JSON response, I see:

"promotionalimages": [
    {
        ...
        "promotional_picture": "https://x.co/storage/promotionalimages/0464eee2-5160-40e7-8ae0-77e7484fe251/15b5f1ec13a9b7.png",
    }
]

However, in your model classes, I see:

public static class Promotionalimage {
    ...
    private String promotionalPicture;
}

These two things don't have the same name: the JSON is using promotional_picture but the Java is using promotionalPicture. It's possible that you have some other code set up to convert from underscores to camel case, but I bet you don't.

There are two ways to fix. I'd recommend adding the @SerializedName annotation:

public static class Promotionalimage {
    ...
    @SerializedName("promotional_picture")
    private String promotionalPicture;
}

but you could also simply change the name of the Java field:

public static class Promotionalimage {
    ...
    private String promotional_picture;
}

Question:

i have JSON response like this :

    [{
    "id": 94640,
    "date": "2016-08-14T21:40:31",
    "slug": "terungkap-van-gaal-tolak-kehadiran-paul-pogba-di-mu",
    "title": {
        "rendered": "Terungkap, Van Gaal Tolak Kehadiran Paul Pogba di MU"
    },
    "better_featured_image": {
        "media_details": {
            "sizes": {
                "medium": {
                    "source_url": "http:\/\/www.opinianda.com\/wp-content\/uploads\/2016\/08\/Pogba-MU-300x300.jpg"
                }
            }
        }
    }
}, {
    "id": 94634,
    "date": "2016-08-14T19:51:50",
    "slug": "liverpool-bakal-fight-di-semua-kompetisi-musim-ini",
    "title": {
        "rendered": "Liverpool Bakal &#8216;Fight&#8217; di Semua Kompetisi Musim ini"
    },
    "better_featured_image": {
        "media_details": {
            "sizes": {
                "medium": {
                    "source_url": "http:\/\/www.opinianda.com\/wp-content\/uploads\/2016\/08\/Klopp-300x300.png"
                }
            }
        }
    }
}, {
    "id": 94608,
    "date": "2016-08-14T13:12:29",
    "slug": "gara-gara-pogba-pirlo-dan-buffon-tertawakan-manchester-united",
    "title": {
        "rendered": "Gara-gara Pogba, Pirlo dan Buffon Tertawakan Manchester United"
    },
    "better_featured_image": {
        "media_details": {
            "sizes": {
                "medium": {
                    "source_url": "http:\/\/www.opinianda.com\/wp-content\/uploads\/2016\/08\/buffon_pirlo-300x300.jpg"
                }
            }
        }
    }
}]

and i want to load source_url with glide. im already use glide at onbindview but its show me error "You cannot load a null Context"

my onload response

            public void onResponse(Call<List<Coba>> call, Response<List<Coba>> response) {
            try {
                List<Coba> jsonResponse = response.body();
                data= new ArrayList<>();
                data.addAll(jsonResponse);
                adapter = new MyAdapter(data);
                adapter.notifyDataSetChanged();
                recyclerView.setAdapter(adapter);
                Log.i("HASIL", "onResponse: "+data);
            }catch (Exception e) {
                Log.d("onResponse", "There is an error");
                e.printStackTrace();
            }
        }

thi is my adapter class

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private String[] mDataset;
private ArrayList<Coba> coba;
private Context mContext;

public static class MyViewHolder extends RecyclerView.ViewHolder {
    public CardView mCardView;
    public TextView mTextView;
    public TextView mDetView;
    public TextView mVerView;
    public ImageView mImgPost;

    public MyViewHolder(View v) {
        super(v);
        mImgPost = (ImageView) v.findViewById(R.id.iv_image);
        mCardView = (CardView) v.findViewById(R.id.card_view);
        mTextView = (TextView) v.findViewById(R.id.tv_text);
        mDetView = (TextView) v.findViewById(R.id.tv_blah);
        mVerView = (TextView) v.findViewById(R.id.tv_wew);

    }
}

// Provide a suitable constructor (depends on the kind of dataset)
public MyAdapter(ArrayList<Coba> coba) {
    this.coba = coba;
}

// Create new views (invoked by the layout manager)
@Override
public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent,
                                                 int viewType) {
    // create a new view
    View v = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.card_item, parent, false);
    // set the view's size, margins, paddings and layout parameters
    MyViewHolder vh = new MyViewHolder(v);
    return vh;
}

@Override
public void onBindViewHolder(MyAdapter.MyViewHolder holder, int position) {
    Glide.with(mContext).load(String.valueOf(coba.get(position).getBetterFeaturedImage().getMediaDetails().getSizes().getMedium().getSourceUrl()))
            .thumbnail(0.5f)
            .crossFade()
            .diskCacheStrategy(DiskCacheStrategy.ALL)
            .into(holder.mImgPost);
    holder.mTextView.setText(Html.fromHtml(String.valueOf(coba.get(position).getTitle().getRendered())));
    holder.mDetView.setText(String.valueOf(coba.get(position).getSlug()));
    holder.mVerView.setText(String.valueOf(coba.get(position).getId()));
    holder.mCardView.setOnClickListener(new View.OnClickListener(){
        @Override
        public void onClick(View v){
            Intent intent = new Intent(v.getContext(),DetailBerita.class);
            v.getContext().startActivity(intent);
            System.out.println("Clicked");
        }
    });
}

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

}

error log :

java.lang.IllegalArgumentException: You cannot start a load on a null Context
                                                                                 at com.bumptech.glide.manager.RequestManagerRetriever.get(RequestManagerRetriever.java:84)
                                                                                 at com.bumptech.glide.Glide.with(Glide.java:629)
                                                                                 at com.oktagon.sumberbola.sumberbola.adapters.MyAdapter.onBindViewHolder(MyAdapter.java:71)
                                                                                 at com.oktagon.sumberbola.sumberbola.adapters.MyAdapter.onBindViewHolder(MyAdapter.java:26)
                                                                                 at android.support.v7.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:6279)
                                                                                 at android.support.v7.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:6312)
                                                                                 at android.support.v7.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:5258)
                                                                                 at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5521)
                                                                                 at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5363)
                                                                                 at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5359)
                                                                                 at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2141)
                                                                                 at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1525)
                                                                                 at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1488)
                                                                                 at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:585)
                                                                                 at android.support.v7.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3506)
                                                                                 at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:3254)
                                                                                 at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:3767)
                                                                                 at android.view.View.layout(View.java:15648)
                                                                                 at android.view.ViewGroup.layout(ViewGroup.java:4967)
                                                                                 at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1077)

there is nothing wrong if i remove the imageview. thanks for your help.


Answer:

You must initialize your mContext variable .

For example :

@Override
public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent,
                                                 int viewType) {
     mContext = parent.getContext();
    // create a new view
    View v = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.card_item, parent, false);
    // set the view's size, margins, paddings and layout parameters
    MyViewHolder vh = new MyViewHolder(v);
    return vh;
}

Hope this helps.

Sorry for my english.