Search code examples
androidandroid-fragmentsandroid-adapterpicassoandroid-viewholder

Picasso Returns Error when Loading Image URL into ImageView


I'm trying to load an image URL string into ImageView from the Adapter class of a RecyclerView, and I'm displaying the RecyclerView in a Fragment. However, nothing is displayed in the ImageView when using the Picasso code to load the URL. I tried to place context, this.main.getActivity(), this.main.getContext(), this.main.getActivity().getApplicationContext(). Nothing worked.

public class NewsAdapter extends RecyclerView.Adapter<NewsHolder> {

OneFragment main;
private Context context;
LayoutInflater inflater;
private ArrayList<Latest_News> arraylist;
private int itemResource;


public NewsAdapter(Context context, OneFragment main, ArrayList<Latest_News> arraylist)
{
    this.context = context;
    this.main = main;
    inflater = LayoutInflater.from(context);
    this.arraylist = arraylist;
}


@Override
public NewsHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
    View view = LayoutInflater.from(viewGroup.getContext())
            .inflate(R.layout.news_cell, viewGroup, false);

    return new NewsHolder(this.context, view);
}

@Override
public void onBindViewHolder(final NewsHolder newsHolder, int i) {

    Latest_News news = this.arraylist.get(i);

    Picasso.with(context)
            .load(news.getNewsImageURL())
            .into(newsHolder.newsImage);

}

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

@Override
public int getItemCount() {

    return this.arraylist.size();
}

}

This is the code of my ViewHolder, I called it NewsHolder:

public class NewsHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

public TextView subject;
public ImageView newsImage;

private Latest_News news;
private Context context;

public LinearLayout linearLayout;

ProgressBar progressBar;

public NewsHolder(Context context, View itemView) {

    super(itemView);

    // 1. Set the context
    this.context = context;

    // 2. Set up the UI widgets of the holder

    linearLayout = (LinearLayout) itemView.findViewById(R.id.linearLayout);
    this.subject = (TextView) itemView.findViewById(R.id.subject);
    this.newsImage = (ImageView) itemView.findViewById(R.id.newsImage);

    if (itemView != null) {
        progressBar = (ProgressBar) itemView.findViewById(R.id.progressBar);
        //progressBar.setVisibility(View.VISIBLE);
    }

    final Typeface tvFont = Typeface.createFromAsset(context.getAssets(), "fonts/FSMatthew-Medium.otf");
    this.subject.setTypeface(tvFont);

    // 3. Set the "onClick" listener of the holder
    itemView.setOnClickListener(this);
}

public void bindBakery(Latest_News news) {

    // 4. Bind the data to the ViewHolder
    this.news = news;
    this.subject.setText(news.subject);

    //load the image url with a callback to a callback method/class
    String iconUrl = news.getNewsImageURL();
}

@Override
public void onClick(View v) {

    // 5. Handle the onClick event for the ViewHolder
    if (this.news != null) {

        Toast.makeText(this.context, "Clicked on " + this.news.subject, Toast.LENGTH_SHORT ).show();
    }
}

private class ImageLoadedCallback implements Callback {
    ProgressBar progressBar;

    public  ImageLoadedCallback(ProgressBar progBar){
        progressBar = progBar;
    }

    @Override
    public void onSuccess() {

    }

    @Override
    public void onError() {

    }
}


}

This is the class of my data model, I called it Latest_News:

public class Latest_News {
public String subject;
public String body;
public String image_url;
public String news_url;


public Latest_News(String subject, String body, String image_url, String news_url) {
    this.subject = subject;
    this.body = body;
    this.image_url = image_url;
    this.news_url = news_url;
}

public Latest_News() {
}

public String getNewsSubject() {
    return this.subject;
}

public String getNewsBody() {
    return this.body;
}

public String getNewsImageURL() {
    return this.image_url;
}

public String getNewsURL() {
    return this.news_url;
}
}

This is the class of my Fragment, I called it OneFragment:

public class OneFragment extends Fragment implements Download_data.download_complete {

RecyclerView listView;
public NewsAdapter adapter;

private static View view;

RecyclerView.LayoutManager layoutManager;

private ProgressBar spinner;

public static ArrayList<Latest_News> news;
public static ArrayList<Latest_News> news_sort;



public OneFragment() {
    // Required empty public constructor
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    view = inflater.inflate(R.layout.fragment_one, container, false);

    spinner = (ProgressBar)view.findViewById(R.id.progressBar1);
    spinner.setVisibility(View.VISIBLE);

    news = new ArrayList<>();
    news_sort = new ArrayList<>();

    adapter = new NewsAdapter(getActivity(), this, news);
    //adapter = new ListAdapter(getContext(), R.layout.stations_cell, stations);


    // 4. Initialize ItemAnimator, LayoutManager and ItemDecorators
    layoutManager = new LinearLayoutManager(getActivity());

    int verticalSpacing = 40;
    VerticalSpaceItemDecorator itemDecorator =
            new VerticalSpaceItemDecorator(verticalSpacing);
    ShadowVerticalSpaceItemDecorator shadowItemDecorator =
            new ShadowVerticalSpaceItemDecorator(getActivity(), R.drawable.drop_shadow);


     // 5. Setup our RecyclerView
    listView = (RecyclerView)view.findViewById(R.id.listings_view);



     // 6. For performance, tell OS RecyclerView won't change size
    listView.setHasFixedSize(true);

    // 7. Set the LayoutManager
    listView.setLayoutManager(layoutManager);

     // 8. Set the ItemDecorators
    listView.addItemDecoration(shadowItemDecorator);
    listView.addItemDecoration(itemDecorator);

    // 9. Attach the adapter to RecyclerView
    listView.setAdapter(adapter);


    additems();
    adapter.notifyDataSetChanged();

    // Inflate the layout for this fragment
    return view;
}

public void additems() {


    Download_data download_data = new Download_data((Download_data.download_complete) this);

    String apiURL = String.format("https://example.com/API/latestnews/1");

    download_data.download_data_from_link(apiURL);

    Log.d("myTag", "dafdewfewffe" + adapter.getItemCount());

}



public void get_data(String data)
{

    try {
        JSONArray data_array=new JSONArray(data);

        for (int i = 0 ; i < data_array.length() ; i++)
        {
            JSONObject obj=new JSONObject(data_array.get(i).toString());

            Latest_News developers = new Latest_News(obj.getString("subject"), obj.getString("body"), obj.getString("image_url"), obj.getString("news_url"));
            news.add(developers);
            spinner.setVisibility(View.GONE);

        }

        adapter = new NewsAdapter(getActivity(), this, news);
        listView.setAdapter(adapter);
        adapter.notifyDataSetChanged();

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

}

}

Solution

  • The issue was that Picasso not accepting to load http URLs. This is how I solved it:

    First I compiled the following in the gradle file together with the Picasso library that I have already compiled before:

    implementation 'com.squareup.picasso:picasso:2.5.2'
    implementation 'com.squareup.okhttp:okhttp:2.2.0'
    implementation 'com.squareup.okhttp:okhttp-urlconnection:2.2.0'
    

    Then I created the following class:

    public class PicassoTrustAll {
    
    private static Picasso mInstance = null;
    
    private PicassoTrustAll(Context context) {
        OkHttpClient client = new OkHttpClient();
        client.setHostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String s, SSLSession sslSession) {
                return true;
            }
        });
        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
            @Override
            public void checkClientTrusted(
                    java.security.cert.X509Certificate[] x509Certificates,
                    String s) throws java.security.cert.CertificateException {
            }
    
            @Override
            public void checkServerTrusted(
                    java.security.cert.X509Certificate[] x509Certificates,
                    String s) throws java.security.cert.CertificateException {
            }
    
            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return new java.security.cert.X509Certificate[] {};
            }
        } };
        try {
            SSLContext sc = SSLContext.getInstance("TLS");
            sc.init(null, trustAllCerts, new java.security.SecureRandom());
            client.setSslSocketFactory(sc.getSocketFactory());
        } catch (Exception e) {
            e.printStackTrace();
        }
    
        mInstance = new Picasso.Builder(context)
                .downloader(new OkHttpDownloader(client))
                .listener(new Picasso.Listener() {
                    @Override
                    public void onImageLoadFailed(Picasso picasso, Uri uri, Exception exception) {
                        Log.e("PICASSO", String.valueOf(exception));
                    }
                }).build();
    
    }
    
    public static Picasso getInstance(Context context) {
        if (mInstance == null) {
            new PicassoTrustAll(context);
        }
        return mInstance;
    }
    }
    

    And this is how I used Picasso in my Adapter class:

    PicassoTrustAll.getInstance(context)
                .load(news.getNewsImageURL())
                .into(newsHolder.newsImage);
    

    Thanks to @RamchandraSingh https://stackoverflow.com/a/49795716/4846301