0

I'm new to android, i have a news website and i'm developing an android app, the main activity catches a JSON node from this link and displays all the articles in a ListView, each item in the list has an image, title and a teaser of this particular article.

now i have written a java code for the title and the description and everything is working perfectly, but i want to display the images too and i don't know how to do that.

Here's my MainActivity.java:

import java.util.ArrayList;
import java.util.HashMap;

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

import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Html;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;



public class MainActivity extends ListActivity {

    //Create a progress dialog instance
    private ProgressDialog pDialog;

    // URL to get contacts JSON
    private static String url = "http://www.ana.fm/api/main/";

    // JSON Node names
    private static final String TAG_ARTICLES = "articles";
    private static final String TAG_ID = "id";
    private static final String TAG_TITLE = "title";
    private static final String TAG_TEASER = "teaser";
    private static final String TAG_COVER_PHOTO = "cover_photo";

    // contacts JSONArray
    JSONArray articles = null;

    // Hashmap for ListView
    ArrayList<HashMap<String, String>> contactList;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        contactList = new ArrayList<HashMap<String, String>>();

        ListView lv = getListView();

        // Listview on item click listener
        lv.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                                    int position, long id) {
                String article_id = ((TextView) view.findViewById(R.id.article_id))
                        .getText().toString();
                // Starting single contact activity
                Intent in = new Intent(getApplicationContext(),
                        SingleContactActivity.class);
                in.putExtra(TAG_ID, article_id);
                startActivity(in);

            }
        });

        // Calling async task to get json
        new GetContacts().execute();
    }

    /**
     * Async task class to get json by making HTTP call
     * */
    private class GetContacts extends AsyncTask<Void, Void, Void> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            // Showing progress dialog
            pDialog = new ProgressDialog(MainActivity.this);
            pDialog.setMessage("Please wait...");
            pDialog.setCancelable(false);
            pDialog.show();
        }
        @Override
        protected Void doInBackground(Void... arg0) {
            // Creating service handler class instance
            ServiceHandler sh = new ServiceHandler();

            // Making a request to url and getting response
            String jsonStr = sh.makeServiceCall(url, ServiceHandler.GET);
            if (jsonStr != null) {
                try {
                    JSONObject jsonObj = new JSONObject(jsonStr);

                    // Getting JSON Array node
                    articles = jsonObj.getJSONArray(TAG_ARTICLES);

                    // looping through All Contacts
                    for (int i = 0; i < articles.length(); i++) {
                        JSONObject c = articles.getJSONObject(i);

                        String id = c.getString(TAG_ID);
                        String title = c.getString(TAG_TITLE);
                        title = Html.fromHtml(title).toString();
                        String teaser = c.getString(TAG_TEASER);
                        teaser = Html.fromHtml(teaser).toString();
                        String cover_photo = "http://www.ana.fm/med_photos/articles/";
                        cover_photo = cover_photo.concat(c.getString(TAG_COVER_PHOTO));



                        // tmp hashmap for single contact
                        HashMap<String, String> article = new HashMap<String, String>();

                        // adding each child node to HashMap key => value
                        article.put(TAG_ID, id);
                        article.put(TAG_TITLE, title);
                        article.put(TAG_TEASER, teaser);
                        article.put(TAG_COVER_PHOTO, cover_photo);


                        // adding contact to contact list
                        contactList.add(article);
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            } else {
                Log.e("ServiceHandler", "Couldn't get any data from the url");
                new GetContacts().execute();
            }
            return null;
        }
        @Override
        protected void onPostExecute(Void result) {
            super.onPostExecute(result);
            // Dismiss the progress dialog
            if (pDialog.isShowing())
                pDialog.dismiss();
            /**
             * Updating parsed JSON data into ListView
             * */
            ListAdapter adapter = new SimpleAdapter(
                    MainActivity.this, contactList,
                    R.layout.list_item, new String[] { TAG_ID, TAG_TITLE, TAG_TEASER, TAG_COVER_PHOTO}, new int[] { R.id.article_id,  R.id.title,
                    R.id.teaser, R.id.cover_photo});
            setListAdapter(adapter);
        }
    }
}

And here's the activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:background="@color/white">
    <!-- Main ListView 
         Always give id value as list(@android:id/list)
    -->
    <ListView
        android:id="@android:id/list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:divider="@android:color/transparent"
        android:dividerHeight="10.0sp"
        />

</LinearLayout>

And here's the list_item.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="10dp"
    android:layout_marginBottom="20dp"
    android:background="@color/light_grey">


    <!-- Cover photo -->
    <ImageView
        android:id="@+id/cover_photo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal" />

    <!-- ID Label -->
    <TextView
        android:id="@+id/article_id"
        android:visibility="gone"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="2dip"
        android:paddingTop="6dip"
        android:textColor="#43bd00"
        android:textSize="16sp"
        android:textStyle="bold" />
    <!-- Title Label -->
    <TextView
        android:id="@+id/title"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="10dip"
        android:paddingTop="6dip"
        android:textColor="@color/black"
        android:textSize="20sp"
        android:textStyle="bold" />

    <!-- Teaser label -->
    <TextView
        android:id="@+id/teaser"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="2dip"
        android:textColor="@color/medium_grey" />



</LinearLayout>

Anyone can help ?

Fadi Obaji
  • 1,454
  • 4
  • 27
  • 57
  • You will have to implement a custom ListAdapter – Marcus Feb 16 '15 at 15:07
  • 1
    Can you show me an example @Marcus – Fadi Obaji Feb 16 '15 at 15:09
  • 3
    [This](http://stackoverflow.com/questions/8166497/custom-adapter-for-list-view) is a good example. – Marcus Feb 16 '15 at 15:10
  • From [your example](http://stackoverflow.com/questions/8166497/custom-adapter-for-list-view), do i have to declare the class in a new file @Marcus ? – Fadi Obaji Feb 16 '15 at 15:46
  • It's not necessary, but I recommend it for maintainability. Usually, I put my ListAdapters in a separate package, i.e. I put my activities in one package and ListAdapters in another. But you could declare your ListAdapter as a private inner class, if you wish. – Marcus Feb 16 '15 at 15:57

2 Answers2

3

So here is how to implement a custom adapter.

For this example we have a person object containing properties for name, surname and imageUrl (the web location for the image)

The following is the Person Class Object:

public class Person {

    String name;
    String surname;
    String imageUrl;

    public Person() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSurname() {
        return surname;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }

    public String getImageUrl() {
        return imageUrl;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }
}

Now we create an xml layout which will populate our listview with data. Nothing fancy here just a layout containing a textview for name, another for surname and an imageview for our image. The file in this case is called person_cell.xml

 <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:text="Medium Text"
        android:id="@+id/person_cell_txtName" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:text="Medium Text"
        android:id="@+id/person_cell_txtSurname" />

     <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/person_cell_imageview" />
</LinearLayout>

So til now we have a class for our person object, and an xml layout ready for use. Now we build our Custom Adapter. Create a class named MyAdapter which extends ArrayAdapter of type Person. Note that we need to pass the context to the adapter since we will be using Picasso to load the image.

public class MyAdapter extends ArrayAdapter<Person> {

    Context context;
    List<Person>myList;

    public MyAdapter(Context context, int resource, List<Person> objects) {
        super(context, resource, objects);

        this.context = context;
        this.myList = objects;
    }


    @Override
    public int getCount() {
        if(myList != null)
            return myList.size();
        return 0;
    }

    @Override
    public Person getItem(int position) {
        if(myList != null)
            return myList.get(position);
        return null;
    }

    @Override
    public long getItemId(int position) {
        if(myList != null)
            return myList.get(position).hashCode();
        return 0;

    }


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

        Holder holder;

        //If the listview does not have an xml layout ready set the layout
        if (convertView == null){

            //we need a new holder to hold the structure of the cell
            holder = new Holder();

            //get the XML inflation service
            LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

            //Inflate our xml cell to the convertView
            convertView = inflater.inflate(R.layout.person_cell, null);

            //Get xml components into our holder class
            holder.txtName = (TextView)convertView.findViewById(R.id.person_cell_txtName);
            holder.txtSurname = (TextView)convertView.findViewById(R.id.person_cell_txtSurname);
            holder.imageView = (ImageView)convertView.findViewById(R.id.person_cell_imageview);

            //Attach our holder class to this particular cell
            convertView.setTag(holder);

        }else{

            //The listview cell is not empty and contains already components loaded, get the tagged holder
            holder = (Holder)convertView.getTag();

        }

        //Fill our cell with data

        //get our person object from the list we passed to the adapter
        Person person = getItem(position);

        //Fill our view components with data
        holder.txtName.setText(person.getName());
        holder.txtSurname.setText(person.getSurname());

             Picasso.with(context).load(person.getImageUrl()).fit().into(holder.imageView);

        return convertView;
    }

    /**
     * This holder must replicate the components in the person_cell.xml 
     * We have a textview for the name and the surname and an imageview for the picture
     */
    private class Holder{

        TextView txtName;
        TextView txtSurname;
        ImageView imageView;

    }
}

Then in our Activity we can simply populate our List of person objects and create an instance of our adapter and set it as the listview main adapter.

ListView myListView = new ListView(getApplicationContext());

List<Person> personList = new ArrayList<>();

Person person = new Person();

person.setName("John");
person.setSurname("Doe");
person.setImageUrl("https://lh3.googleusercontent.com/-Sa9kdnhuE5E/AAAAAAAAAAI/AAAAAAAAABs/ILmJ8_sk9aY/photo.jpg");

 MyAdapter adapter = new MyAdapter(getApplicationContext(), R.layout.person_cell, personList);

myListView.setAdapter(adapter);

This is basically the story behind a custom adapter.

Marcus
  • 6,697
  • 11
  • 46
  • 89
J.Vassallo
  • 2,282
  • 3
  • 15
  • 14
  • Thank you very much!, this is exactly what i'm looking for, let me try it and i'll get back to you :) – Fadi Obaji Feb 16 '15 at 15:51
  • 1
    Enjoy your code :D and btw inflate your listview from the xml, dont create one like i did here "ListView myListView = new ListView(getApplicationContext());", I created the listview only to spare some time – J.Vassallo Feb 16 '15 at 15:55
  • So @JohnV, i tried your code, everything works except for the images :( , i downloaded Picasso and put it in a "libraries" folder in the app, still it doesn't work – Fadi Obaji Feb 16 '15 at 17:40
  • what ide are you using? – J.Vassallo Feb 16 '15 at 17:44
  • also are the images hosted? can you access them from the browser with the url you are storing? – J.Vassallo Feb 16 '15 at 17:45
  • Android Studio, and yes ofcourse i can, the logcat is giving me these errors : ' E/BitmapFactory﹕ Unable to decode stream: java.io.FileNotFoundException: http:/www.ana.fm/med_photos/articles/2424767783.jpg: open failed: ENOENT (No such file or directory)' – Fadi Obaji Feb 16 '15 at 17:48
  • im trying to figue whats wrong...and for android studio to include Picasso you only have to put the following in your app gradle : compile 'com.squareup.picasso:picasso:2.4.0' – J.Vassallo Feb 16 '15 at 17:51
  • and for the dependencies ? – Fadi Obaji Feb 16 '15 at 17:52
  • hi...you load the URL in Picasso after pasring to URL like so.. Picasso.with(context).load(Url.parse(http:/www.ana.fm/med_photos/articles/2424767783.jpg)) – J.Vassallo Feb 16 '15 at 17:53
  • I tried it , still the same, let me try to load the images without Picasso and i'll get back to you – Fadi Obaji Feb 16 '15 at 18:11
  • stick to picasso trust its worth it...in the meantime there is an error since the picture was removed...try this image its works.. https://fanart.tv/fanart/music/847e8284-8582-4b0e-9c26-b042a4f49e57/artistbackground/placebo-4efe384812669.jpg – J.Vassallo Feb 16 '15 at 18:21
  • the .load should look like this..dont forget the Uri.parse : .load(Uri.parse("https://fanart.tv/fanart/music/847e8284-8582-4b0e-9c26-b042a4f49e57/artistbackground/placebo-4efe384812669.jpg")) – J.Vassallo Feb 16 '15 at 18:22
  • Great, i got it working, i deleted the '.fit()' and it worked !, so now it's like this : Picasso.with(context).load(Uri.parse(article.getImageUrl())).into(holder.imageView); – Fadi Obaji Feb 16 '15 at 18:32
  • but there's one problem though, when i scroll down the screen and then scroll back up, the images disappear then re-appear again after what i think is re-downloading ! – Fadi Obaji Feb 16 '15 at 18:34
  • 1
    you can increase the cache size for picasso wih (Picasso p = new Picasso.Builder(context) .setCache(new LruCache(36000)) .build();)..also you can store low res copy of the images if you want on the device and make picasso load from the device and check for update from the uri – J.Vassallo Feb 16 '15 at 18:38
  • Thank you very much, you've been amazing, one last thing : how to avoid 'OutOfMemoryError' ? – Fadi Obaji Feb 16 '15 at 18:47
  • 1
    Your welcome :) here is an article on the out of memory..http://blog.jpardogo.com/resize-and-respect/ – J.Vassallo Feb 16 '15 at 18:51
0

Based on your JSON,all you have is the image file name. ie:

{
.
.
.
"cover_photo":"2018061675.jpg"
}   

You will first need an API to deliver the InputStream from your image. after that you have 3 options:

  1. Volley

    • Its a very simple to use(but very powerful) library which takes care of maintaining your resource pool and memory when you.
    • This video is an introduction to volley.Please watch it if you have prior android experience or I would suggest watching to merely understand how it affects your normal methods.
    • making an image request is mentioned in the android tutorial for volley
  2. OkHttp

    • It requires more working and is lightweight for overall http request use.
    • the image request can be obtained as file as detailed here
  3. Picasso

    • Simple library meant solely for image request purposes
  4. Do it manually

    • Use an input stream and fetch and build your image manually
    • This is is a good example or that.
Community
  • 1
  • 1
Droidekas
  • 3,464
  • 2
  • 26
  • 40
  • your approaches are for downloading one image and i know how to do that, i need to get the images for all ListView items. I think i have to make a custom ListAdapter for that. Can you help ? – Fadi Obaji Feb 16 '15 at 15:28
  • Did you check the volley example?that does not require you to manually add the image – Droidekas Feb 16 '15 at 15:29