Search code examples
androidjsonlistviewurlpicasso

Add Image in listView using image from url Picasso crash in android


Bonjour I have spent days and night looking for an answer suitable to my case can you help please Here is the code I am using anytime I add the ligne with Picasso, the program crashes It works fine with static images from drawable I just want to display the text with the image that corresponds

    public void Afficher_les_vues(String url_in) {

    OkHttpClient client = new OkHttpClient();

    Request request = new Request.Builder()
            .url(url_in)
            .build();

    client.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(@NotNull Call call, @NotNull IOException e) {
            e.printStackTrace();
        }

        @Override
        public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
            if (response.isSuccessful()) {

                myResponse = response.body().string();
                Annonces.this.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {


                        try {
                            JSONObject reader = new JSONObject(myResponse);

                            JSONArray eglises = reader.getJSONArray("Annonces_adv");


                            arrayList.clear();


                            for (int i = 0; i < eglises.length(); i++) {

                                JSONObject eglise = eglises.getJSONObject(i);
                                String titre = eglise.getString(titret);
                                String description = eglise.getString(descriptionet);
                                int ref = Integer.parseInt(eglise.getString(reft));
                                String image = eglise.getString(imaget);
                                String url_image = eglise.getString(imaget);

                                HashMap<String, String> data = new HashMap<>();


                                data.put(titret, titre);
                                data.put(descriptionet, description);
                                data.put(reft, String.valueOf(ref));
                                data.put(imaget, image);
                                data.put(url_imaget, url_image);

                                arrayList.add(data);


                                ImageView imageViewlogo = findViewById(R.id.imageViewLogoNews);
                                //Picasso.get().load(url_image).into(imageViewlogo);

                                //new GetImageFromUrl(imageViewlogo).execute(url_image);


                                ListAdapter adapter = new SimpleAdapter(Annonces.this, arrayList, R.layout.listview_layout
                                        , new String[]{titret, descriptionet, reft, url_imaget}, new int[]{R.id.titre_de, R.id.description_de,
                                        R.id.reference, R.id.url_image});


                                lv.setAdapter(adapter);

                            }


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


                    }
                });


            }

When I use Picasso to display and image in a simple ImageView not in a listView, it works fine

Thank you in advance I work with android studio Blessings


Solution

  • The main things you need to fix are not creating the adapter every loop instance and not accessing row views (the ImageView) from outside the adapter.

    1. Do not make the adapter inside the loop

    You should build the array of data to display first, then after the loop that builds the array create the adapter and set it on the ListView. When you have a custom adapter, it is helpful to use a small data class to hold the data you need to show in each row (see RowData later in the answer)

    arrayList.clear(); // ArrayList<RowData>()
    
    for (int i = 0; i < eglises.length(); i++) {
    
        JSONObject eglise = eglises.getJSONObject(i);
        String titre = eglise.getString(titret);
        String description = eglise.getString(descriptionet);
        int ref = Integer.parseInt(eglise.getString(reft));
        String url_image = eglise.getString(imaget);
    
        RowData data = new RowData(titre, description, String.valueOf(ref), url_image);
    
        arrayList.add(data);
    }
    
    // You CANNOT access row view here - in this context findViewById searches the main
    // view heirarchy, and will not find views in your ListView rows
    
    // Make the adapter *AFTER* the loop where you fill the array
    // you will need to make your own custom adapter to load the image
    ListAdapter adapter = new CustomAdapter(Annonces.this, R.layout.listview_layout, arrayList);
    
    
    lv.setAdapter(adapter);
    

    2. Do not attempt to access row views outside the adapter

    Your row views should only be accessed inside getView in your adapter itself. This is where you should load the image. If you were using a standard adapter before, you will need to implement your own custom adapter to do this. There are instructions for how to do that here. The adapter below takes an ArrayList of RowData items and uses that to populate each row.

    public class CustomAdapter extends ArrayAdapter<RowData> {
        private Context mContext;
        private int mResource;
    
        public MainAdapter(@NonNull Context context, int resource, @NonNull ArrayList<RowData> objects) {
            super(context, resource, objects);
            mContext=context;
            mResource=resource;
        }
    
        @NonNull
        @Override
        public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
    
            if( convertView == null ) {
                LayoutInflater inflater = LayoutInflater.from(mContext);
                convertView = inflater.inflate(mResource, parent, false);
            }
    
            // Get the views for this row (these must be in the
            // layout xml you passed in to the adapter constructor)
            TextView title = convertView.findViewById(R.id.titre_de);
            TextView description = convertView.findViewById(R.id.description_de);
            TextView reference = convertView.findViewById(R.id.reference);
            ImageView imageViewlogo = convertView.findViewById(R.id.imageViewLogoNews);
    
            // Get the RowData class for this row
            RowData data = getItem(position);
    
            // Set the text fields
            title.setText(data.title);
            description.setText(data.description);
            reference.setText(data.reference);
    
            // Start Picasso loading into the ImageView for this row
            Picasso.get().load(data.url).into(imageViewlogo);
            
            return convertView;
        }
    }
    

    Make a small data class

    When you have a custom adapter it is useful to make a custom data class that holds the data you want to show in each row. This avoids the possible errors in missing keys etc... you can get with a plain old map or passing in multiple lists.

    public class RowData {
        final String title;
        final String description;
        final String reference;
        final String url;
    
        RowData(String title, String desc, String ref, String url) {
            this.title = title;
            this.description = desc;
            this.reference = ref;
            this.url = url;
        }
    }