Android RSS - GridView - Headlines With Images and Text

android rss

This is an android RSS Images GridView example with XmlPullParser,AsyncTask ,HttpURLConnection and GridView..We shall download RSS Feeds from a local website and then parse the feed.We then show parsed images and text news in our GridView.

What we do :

  • Connect to Internet and make a HTTP GET request using HtttpURLConnection.
  • Download our data in the background thread.
  • WE use AsyncTask for our threading.
  • WE parse the downloaded xml feeds.
  • We shall be parsing using XmlPullParser.
  • We show our results in a GridView.
  • Our results shall consist of images and text.
  • The website we shall be parsing was hosted locally and it was a dummy wordpress site.

Cheers.

SECTION 1 : Our Dependencies and Manifest

Build.Gradle

  • Studio has added for us dependencies for AppCompat and Design support libraries.
  • Now lets add dependencies for CardView and Picasso library.
  • Our AdapterView shall consist of CardViews as our ViewItems.
  • Picasso ImageLoader shall load us images asynchronously and cache them.
apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.2"

    defaultConfig {
        applicationId "com.tutorials.hp.gridviewimagestextrss"
        minSdkVersion 15
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.3.0'
    compile 'com.android.support:design:23.3.0'
    compile 'com.android.support:cardview-v7:23.3.0'
   // compile project(':picasso-2.5.2')
    compile 'com.squareup.picasso:picasso:2.5.2'

}

AndroidManifest.xml

  • Remember we are downloading data from a network.
  • Hence we need to add permission to access the internet.
  • Or else Android won't allow us establish connection.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.tutorials.hp.gridviewimagestextrss">

    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

SECTION 2 : Our Data Object

Article.java

  • Represents a single article.
  • The article shall have various properties like name,description,title,date etc.
package com.tutorials.hp.gridviewimagestextrss.m_DataObject;

public class Article {
    String title,description,date,imageUrl;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    public String getImageUrl() {
        return imageUrl;
    }

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

SECTION 3 : Our Networking and Parsing classes

Connector class

Main Responsibility : ESTABLISH CONNECTION.

  • Establishes connection to our server for us.
  • We then set up connection properties like Request method.
  • In this case we are making a HTTP GET request to our server.
  • We shall use HttpURLConnection so our only method shall return its instance .
package com.tutorials.hp.gridviewimagestextrss.m_RSS;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class Connector {

    public static Object connect(String urlAddress)
    {
        try
        {
            URL url=new URL(urlAddress);
            HttpURLConnection con= (HttpURLConnection) url.openConnection();

            con.setRequestMethod("GET");
            con.setConnectTimeout(15000);
            con.setReadTimeout(15000);
            con.setDoInput(true);

            return con;

        } catch (MalformedURLException e) {
            e.printStackTrace();
            return ErrorTracker.WRONG_URL_FORMAT;

        } catch (IOException e) {
            e.printStackTrace();
            return ErrorTracker.CONNECTION_ERROR;
        }
    }
}

Downloader class

Main Responsibility : DOWNLOAD XML FEEDS.

  • We use our Connector class above to establish a connection.
  • If we have a connection then we download data XML feeds from server.
  • We download in background using AsyncTask to avoid freezing our user interface.
  • Meanwhile we show progress dialog while downloading.
  • We dismiss our progress dialog on completion.
  • Then we send this downloaded xml feeds to our Data Parser class for it to be processed.
package com.tutorials.hp.gridviewimagestextrss.m_RSS;

import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.widget.GridView;
import android.widget.Toast;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;

public class Downloader extends AsyncTask<Void,Void,Object> {

    Context c;
    String urlAddress;
    GridView gv;

    ProgressDialog pd;

    public Downloader(Context c, String urlAddress, GridView gv) {
        this.c = c;
        this.urlAddress = urlAddress;
        this.gv = gv;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        pd=new ProgressDialog(c);
        pd.setTitle("Fetch Articles");
        pd.setMessage("Fetching....Please wait");
        pd.show();
    }

    @Override
    protected Object doInBackground(Void... params) {
        return this.downloadData();
    }

    @Override
    protected void onPostExecute(Object data) {
        super.onPostExecute(data);

        pd.dismiss();
        if(data.toString().startsWith("Error"))
        {
            Toast.makeText(c,data.toString(),Toast.LENGTH_SHORT).show();
        }else {
            //PARSE
            new RSSParser(c, (InputStream) data,gv).execute();

        }
    }

    private Object downloadData()
    {
        Object cconnection=Connector.connect(urlAddress);
        if(cconnection.toString().startsWith("Error"))
        {
            return cconnection.toString();
        }

        try
        {
            HttpURLConnection con= (HttpURLConnection) cconnection;
            int responeCode=con.getResponseCode();

            if(responeCode==con.HTTP_OK)
            {
                InputStream is=new BufferedInputStream(con.getInputStream());
                return is;
            }

            return ErrorTracker.RESPONSE_EROR+con.getResponseMessage();

        } catch (IOException e) {
            e.printStackTrace();
            return ErrorTracker.IO_EROR;
        }
    }
}

RSS Parser class

Main Responsibility : DOWNLOAD XML FEEDS.

  • It receives raw XML feeds from downloader class.
  • It then processes/parses these feeds.
  • We use XmlPullParser to parse the feeds.
  • It then fills an arraylist of Article Objects with our results.
  • It then sends this arraylist to adapter for binding purposes.
package com.tutorials.hp.gridviewimagestextrss.m_RSS;

import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.widget.GridView;
import android.widget.Toast;

import com.tutorials.hp.gridviewimagestextrss.m_DataObject.Article;
import com.tutorials.hp.gridviewimagestextrss.m_UI.CustomAdapter;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;

public class RSSParser extends AsyncTask<Void,Void,Boolean> {

    Context c;
    InputStream is;
    GridView gv;

    ProgressDialog pd;
    ArrayList<Article> articles=new ArrayList<>();

    public RSSParser(Context c, InputStream is, GridView gv) {
        this.c = c;
        this.is = is;
        this.gv = gv;
    }
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        pd=new ProgressDialog(c);
        pd.setTitle("Parsing");
        pd.setMessage("Parsing....Please wait");
        pd.show();
    }

    @Override
    protected Boolean doInBackground(Void... params) {
        return this.parseRSS();
    }

    @Override
    protected void onPostExecute(Boolean isParsed) {
        super.onPostExecute(isParsed);

        pd.dismiss();
        if(isParsed)
        {
            //BIND
            gv.setAdapter(new CustomAdapter(c,articles));

        }else {
            Toast.makeText(c,"Unable To Parse",Toast.LENGTH_SHORT).show();
        }
    }

    private Boolean parseRSS()
    {
        try
        {
            XmlPullParserFactory factory=XmlPullParserFactory.newInstance();
            XmlPullParser parser=factory.newPullParser();

            parser.setInput(is,null);
            int event=parser.getEventType();

            String tagValue=null;
            articles.clear();

            Article article=new Article();
            Boolean isSiteMeta=true;

            do {

                String tagName=parser.getName();

                switch (event)
                {
                    case XmlPullParser.START_TAG:
                        if(tagName.equalsIgnoreCase("item"))
                        {
                            article=new Article();
                            isSiteMeta=false;
                        }
                        break;

                    case XmlPullParser.TEXT:
                        tagValue=parser.getText();
                        break;

                    case XmlPullParser.END_TAG:
                        if(!isSiteMeta)
                        {
                            if(tagName.equalsIgnoreCase("title"))
                            {
                                article.setTitle(tagValue);
                            }else if(tagName.equalsIgnoreCase("description"))
                            {
                                String desc=tagValue;
                                article.setDescription(desc.substring(desc.indexOf("/>")+2));

                                //EXTRACT IMG URL FROM DESC
                                String imageUrl=desc.substring(desc.indexOf("src=")+5,desc.indexOf("jpg")+3);
                                article.setImageUrl(imageUrl);

                            }else if(tagName.equalsIgnoreCase("date"))
                            {
                                article.setDate(tagValue);
                            }

                        }

                        if(tagName.equalsIgnoreCase("item"))
                        {
                            articles.add(article);
                            isSiteMeta=true;
                        }

                        break;
                }
                event=parser.next();
            }while (event != XmlPullParser.END_DOCUMENT);
            return true;
        } catch (XmlPullParserException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return false;
    }
}

Error Tracker class

Main Responsibility : HELP US TRACK CONNECTION EXCEPTIONS AT RUNTIME

  •  Consists of constants that we shall display when we encounter error at runtime.
package com.tutorials.hp.gridviewimagestextrss.m_RSS;

public class ErrorTracker {

    public final static String WRONG_URL_FORMAT="Error : Wrong URL Format";
    public final static String CONNECTION_ERROR="Error : Unable To Establish Connection";
    public final static String IO_EROR="Error : Unable To Read";
    public final static String RESPONSE_EROR="Error : Bad Response - ";

}

SECTION 3 : Adapter class

Custom Adapter class

Main Responsibility : HELPS US BIND CUSTOM DATA TO GRIDVIEW.

  • You can use one line of code to bind simple data to GridView.
  • But for custom data like ours,we need an adapter to adapt it.
  • We shall use BaseAdapter as our Base class.
  • Our BaseAdapter subclass shall receive an arraylist and a context.
package com.tutorials.hp.gridviewimagestextrss.m_UI;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.tutorials.hp.gridviewimagestextrss.R;
import com.tutorials.hp.gridviewimagestextrss.m_DataObject.Article;

import java.util.ArrayList;

public class CustomAdapter extends BaseAdapter {

    Context c;
    ArrayList<Article> articles;

    public CustomAdapter(Context c, ArrayList<Article> articles) {
        this.c = c;
        this.articles = articles;
    }

    @Override
    public int getCount() {
        return articles.size();
    }

    @Override
    public Object getItem(int position) {
        return articles.get(position);
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if(convertView==null)
        {
            convertView= LayoutInflater.from(c).inflate(R.layout.model,parent,false);
        }

        TextView titleTxt= (TextView) convertView.findViewById(R.id.titleTxt);
        TextView descTxt= (TextView) convertView.findViewById(R.id.descTxt);
        TextView dateTxt= (TextView) convertView.findViewById(R.id.dateTxt);
        ImageView img= (ImageView) convertView.findViewById(R.id.articleImage);

        Article article= (Article) this.getItem(position);

        String title=article.getTitle();
        String desc=article.getDescription();
        String date=article.getDate();
        String imageUrl=article.getImageUrl().replace("localhost","10.0.2.2");

        titleTxt.setText(title);
        descTxt.setText(desc.substring(0,130));
        dateTxt.setText(date);
        PicassoClient.downloadImage(c,imageUrl,img);

        return convertView;
    }
}

Picasso Client class.

Main Responsibility : DOWNLOAD OUR IMAGES AND CACHE THEM.

  • Picasso library shall help us download images when supplied with a URL.
package com.tutorials.hp.gridviewimagestextrss.m_UI;

import android.content.Context;
import android.widget.ImageView;

import com.squareup.picasso.Picasso;
import com.tutorials.hp.gridviewimagestextrss.R;

public class PicassoClient {

    public static void downloadImage(Context c,String imageUrl,ImageView img)
    {
        if(imageUrl.length()>0 && imageUrl != null)
        {
            Picasso.with(c).load(imageUrl).placeholder(R.drawable.placeholder).into(img);

        }else {
            Picasso.with(c).load(R.drawable.placeholder).into(img);
        }
    }
}

SECTION 4 : Our Activity

MainActivity class.

Main Responsibility : LAUNCH OUR APP.

  • We shall reference the views like GridView and Floating action button here,from our XML Layouts.
  • We then execute our Downloader class when Floating action button is clicked.
  • We shall also define here our feeds url and parse the data.
package com.tutorials.hp.gridviewimagestextrss;

import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.GridView;

import com.tutorials.hp.gridviewimagestextrss.m_RSS.Downloader;

public class MainActivity extends AppCompatActivity {

    final static String urlAddress="http://10.0.2.2/galacticnews/index.php/feed";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);

        final GridView gv= (GridView) findViewById(R.id.gv);

        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new Downloader(MainActivity.this,urlAddress,gv).execute();
            }
        });
    }

}

SECTION 5 : Our Layouts

ActivityMain.xml Layout.

  • Inflated as our activity's view.
  • Includes our content main.
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.tutorials.hp.gridviewimagestextrss.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        android:src="@android:drawable/ic_dialog_email" />

</android.support.design.widget.CoordinatorLayout>

ContentMain.xml Layout.

  • Defines our view hierarchy.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.tutorials.hp.gridviewimagestextrss.MainActivity"
    tools:showIn="@layout/activity_main">

    <GridView
        android:id="@+id/gv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:numColumns="2" />
</RelativeLayout>

Model.xml Layout.

  • Inflated as our AdapterView's viewitems.
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_margin="10dp"
    card_view:cardCornerRadius="5dp"
    card_view:cardElevation="5dp"
    android:layout_height="200dp">

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <ImageView
            android:layout_width="100dp"
            android:layout_height="wrap_content"
            android:id="@+id/articleImage"
            android:padding="10dp"
            android:src="@drawable/placeholder" />

        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:text="Title"
                android:id="@+id/titleTxt"
                android:padding="10dp"
                android:textColor="@color/colorAccent"
                android:textStyle="bold"
                android:layout_alignParentLeft="true"
                />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:text="Description....................."
                android:lines="3"
                android:id="@+id/descTxt"
                android:padding="10dp"

                android:layout_alignParentLeft="true"
                />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textAppearance="?android:attr/textAppearanceMedium"
                android:text="Date"
                android:textStyle="italic"
                android:id="@+id/dateTxt" />

        </LinearLayout>

    </LinearLayout>
</android.support.v7.widget.CardView>

LAST SECTION

  • Lets share more tips in OUR FB PAGE.
  • To see the XML  we were parsing and the website itself please have look at the tutorial at our youtbe channel : ProgramminWizards.
  • You'll also find the demo for this example and step by step explanations.

How do You Feel after reading this?

According to scientists, we humans have 8 primary innate emotions: joy, acceptance, fear, surprise, sadness, disgust, anger, and anticipation. Feel free to tell us how you feel about this article using these emotes or via the comment section. This feedback helps us gauge our progress.

Help me Grow.

I set myself some growth ambitions I desire to achieve by this year's end regarding this website and my youtube channel. Am halfway. Help me reach them by:




Recommendations


What do You Think

Dear readers drop us your comments below. We are building a community of students and learners. Start by dropping us your suggestions below. What tutorials do you want us to do for example? Where can we improve? What are some awesome resources out there? Do you have any code you want to share with us?
By the way that example or snippet you have lying in your computer can really help beginner programmers. We can share it here with other students.

Previous Post Next Post