Home > programming > Loading Twitter Data into Android with Lists

Loading Twitter Data into Android with Lists

February 22Hits:1
Advertisement

In my previous article, we created your first Android app featuring a basic Hello World function. Well, it seems the real Hello World of the mobile world is a Twitter feed.

This tutorial will guide you through creating a simple Android app to display a list of tweets coming from the JSON based search API provided by Twitter. We will work through:

  • displaying a list of items
  • customizing the look of each list item
  • accessing remote services and parsing data
  • creating responsive user interfaces

For instructions on how to set up an Android development environment, take a look at the previous article, which guides you all the way from installing software and establishing a workspace through to running a skeleton app in the Android Emulator.

Displaying Lists

To get started on our new project, create a new Android project in Eclipse (File -> New -> Other -> Android and select Android Project), entering an appropriate project name, application name, package name, and a name for the single launch activity.

You can see the options I’ve used below:

Loading Twitter Data into Android with Lists

To check that you have created the project correctly, run the skeleton app in the emulator by highlighting the project name in Eclipse and selecting Run -> Run As -> Android Application.

Currently the Activity you have created will be extending android.app.Activity, and in your onCreate method you will be setting the ContentView of the activity to R.layout.main (linking to the layout as specified in /res/layout/main.xml). The Android SDK provides a convenient way to quickly display a list of data using a superclass called android.app.ListActivity. This Activity already provides a ContentView, configured with a ListView, ready to use and populate with data.

Now change the superclass of TwitterFeedActivity to extend ListActivity, removing the setting of the ContentView from the onCreate method.

The ListView now needs to be given data to display, along with a means to map that data into rows. ListAdaptors provide this mechanism and are set on the underlying ListView of the ListActivity using setListAdaptor.

Create some sample data containing two Strings, and an Android SDK provided adaptor (ArrayAdaptor) that knows how to handle arrays of arbitrary data into ListViews (The Android SDK also comes with several other ListAdaptors, such as Cursor Adaptors, which can assist when connecting local data storage to a ListView). You also need to provide the adaptor with a layout it can use to render the elements onto each row. In the example below we are using the Android SDK provided layout, simple_list_item_1, which is a single text label–perfect for laying our single strings:

public class TwitterFeedActivity extends ListActivity {      @Override     public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);          String[] elements = {"Line 1", "Line 2"};         setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, elements));     }  }

Adjust your code as above and confirm the following results in the emulator:

Loading Twitter Data into Android with Lists

Customizing List Items

The basic Twitter client needs to display at least two fields per row: the author of the tweet, and the content of the tweet. In order to achieve this you will have to move beyond the inbuilt layout and ArrayAdaptor and implement your own instead.

Start by creating a class called Tweet that can be used to hold both the author and the content as Strings. Then create and populate a Tweet object with some test data to be displayed in the custom list item:

package com.sitepoint.mytwitter;  public class Tweet {          String author;          String content; }

Create a layout XML file in /res/layout/list_item.xml to define two TextViews to display the content and author on separate rows. In order to display them one above the other, use a LinearLayout, configured to render each element within it vertically (android:orientation="vertical").

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"   android:orientation="vertical"   android:layout_width="fill_parent"   android:layout_height="?android:attr/listPreferredItemHeight"   android:padding="6dip">    <TextView android:id="@+id/toptext" android:layout_width="fill_parent"       android:layout_height="wrap_content"       android:gravity="center_vertical" android:singleLine="true" />    <TextView android:layout_width="fill_parent"       android:layout_height="wrap_content"  android:id="@+id/bottomtext"       android:singleLine="true" />  </LinearLayout>

Once the XML file has been created, the Android Eclipse plugin will automatically add it as a reference into the generated R file. This R file is kept under the /gen folder of your project and acts as a bridge between your XML elements and your Java code. It allows your Java code to reference XML elements and files created under the /res folders. The file you have just created can now be referenced as R.layout.list_item in the Java code, as you will do next in the custom list adaptor.

Create a private class (inside the Activity) called TweetListAdaptor which subclasses ArrayAdaptor. This class should be used to store an ArrayList of the Tweets being displayed, as well as providing a way to map the Tweet objects to the TextViews you created in the layout above.

This mapping overrides the getView method in ListAdaptor, and should return a View object populated with the contents of the data at the requested position:

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

Furthermore, it should–if possible–reuse any View objects being passed into the method through the convertView parameter. If a new View object has to be created for every element in a list, large lists would stutter when they scroll. Caching Views allows the minimum amount of View objects to be created to populate a large list of data.

The complete implementation of the custom TweetListAdaptor is below. Note the “if statement” checking whether the passed convertView is able to be reused. If the View is null, the ListView has run out of Views to display and requires a new View to be created for this row.

This is achieved using the LayoutService, which is able to inflate (or load) the layout as you specified earlier in XML. You will see here how to reference the layout file using the generated R class through the R.layout.list_item reference.

Once a View is created (or reused), the particular Tweet is extracted from the ArrayList at the position required to be rendered by the ListView. You are then able to obtain references to the two TextView elements using the ids assigned to them in the XML (for example, android:id="@+id/toptext"). Once you have the references, you can set them with the appropriate content and author from the Tweet object.

private class TweetListAdaptor extends ArrayAdapter<Tweet> {          private ArrayList<Tweet> tweets;          public TweetListAdaptor(Context context,                                     int textViewResourceId,                                     ArrayList<Tweet> items) {                  super(context, textViewResourceId, items);                  this.tweets = items;         }          @Override         public View getView(int position, View convertView, ViewGroup parent) {                 View v = convertView;                 if (v == null) {                         LayoutInflater vi = (LayoutInflater) getSystemService                         (Context.LAYOUT_INFLATER_SERVICE);                         v = vi.inflate(R.layout.list_item, null);                 }                 Tweet o = tweets.get(position);                 TextView tt = (TextView) v.findViewById(R.id.toptext);                 TextView bt = (TextView) v.findViewById(R.id.bottomtext);                 tt.setText(o.content);                 bt.setText(o.author);                  return v;         } }

Now the onCreate method can be adjusted to use the custom list adaptor with the created test data as shown below:

    public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);          Tweet tweet = new Tweet();         tweet.author = "dbradby";         tweet.content = "Android in space";         ArrayList<Tweet> items = new ArrayList<Tweet>();         items.add(tweet);          TweetListAdaptor adaptor = new TweetListAdaptor(this,R.layout.list_item, items);         setListAdapter(adaptor);     }

which will display the following, when run in the emulator:

Loading Twitter Data into Android with Lists

Accessing remote services and parsing data

The Android SDK contains packages aimed at simplifying access to HTTP-based APIs. The Apache HTTP classes have been included and can be found under the org.apache.http package. You’ll be using these classes, along with the org.json classes to parse the data coming back from a call out the to Twitter search API.

Before any remote services can be accessed from an Android app, a permission has to be declared. This will alert the user (of your app) of what the app might be doing. Permissions could be to monitor incoming SMS, read the address book or, in your case, to simply make a request on the internet. These permissions are displayed to a user in the Android Market place before an app is installed, giving the user the choice of whether they want to give this app the declared permissions.

The permission you need to use is INTERNET and should be inserted outside of the application tag in the AndroidManifest.xml file.

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

With the permission in place, we can create a private method in the Activity that makes a request, parses the result, and returns an ArrayList of Tweet objects. The code listed below makes the request and looks for the resulting JSON array, which is iterated to extract each tweet’s text and from_user elements.

private ArrayList<Tweet> loadTweets(){         ArrayList<Tweet> tweets = new ArrayList<Tweet>();         try {                  HttpClient hc = new DefaultHttpClient();                 HttpGet get = new                 HttpGet("http://search.twitter.com/search.json?q=android");                 HttpResponse rp = hc.execute(get);                  if(rp.getStatusLine().getStatusCode() == HttpStatus.SC_OK)                 {                         String result = EntityUtils.toString(rp.getEntity());                         JSONObject root = new JSONObject(result);                         JSONArray sessions = root.getJSONArray("results");                         for (int i = 0; i < sessions.length(); i++) {                                 JSONObject session = sessions.getJSONObject(i);                                  Tweet tweet = new Tweet();                                 tweet.content = session.getString("text");                                 tweet.author = session.getString("from_user");                                 tweets.add(tweet);                         }                 }          } catch (Exception e) {                 Log.e("TwitterFeedActivity", "Error loading JSON", e);         }         return tweets; }

Now replace the dummy data you previously used with a call to the loadTweets method when constructing the custom list adaptor in the onCreate method.

TweetListAdaptor adaptor = new TweetListAdaptor(this,R.layout.list_item, loadTweets());

Run this in the emulator and you should now see real data coming back from the API request being displayed in the list view as below:

Loading Twitter Data into Android with Lists

Creating responsive user interfaces

The code in its current state has the potential to cause an Application Not Responding (ANR) dialog to appear, prompting the user to quit your app. This can occur due to the long-running work of making a remote request for data being performed within methods such as onCreate.

Long-running tasks should never be performed on the main application thread (which drives the user interface event loop). They should instead be spawned off into child threads to perform the work.

While Java’s Thread class can be used for this task, there is a complication in that once the long-running task is complete, it generally wants to change the user interface to report the results (that is, display a list of tweets loaded from a request).

User interface elements can only have their state altered from the main thread, as the Android UI toolkit is not thread-safe, therefore the background thread needs to message back to the main thread in order to manipulate the UI.

Thankfully, the Android SDK provides a convenient class AsyncTask, which provides an easy mechanism for asynchronous tasks to interact safely with the UI thread. This is achieved by subclassing AsyncTask and overriding the doInBackground method to perform the long-running task, then overriding onPostExecute to perform any manipulations on the UI.

When the AsyncTask is created (it has to be created on the UI thread) and executed, the doInBackground method is invoked on a background thread. On completion, the onPostExecute method is invoked back on the main UI thread.

To use this in your app, you will need to implement a private class within the Activity (like the custom adaptor class) called MyTask, which subclasses AsyncTask. You can override the doInBackground method with the contents of your previous loadTweets method.

Instead of the ArrayList being returned, you maintain an instance variable in the Activity so that the data can be shared across the private classes. Then in the onPostExecute you can set the List Adaptor with the data, as was done previously in onCreate. The onCreate method now simply creates the MyTask object and calls the execute method.

In order to demonstrate the UI responding while the background request is being performed, I have added a ProgressDialog to the AsyncTask as well. The complete listing of the Activity is below:

package com.sitepoint.mytwitter;  import java.util.ArrayList; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.util.EntityUtils; import org.json.JSONArray; import org.json.JSONObject;  import android.app.ListActivity; import android.app.ProgressDialog; import android.content.Context; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.TextView;  public class TwitterFeedActivity extends ListActivity {         private ArrayList<Tweet> tweets = new ArrayList<Tweet>();      @Override     public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);          new MyTask().execute();      }        private class MyTask extends AsyncTask<Void, Void, Void> {               private ProgressDialog progressDialog;                protected void onPreExecute() {                       progressDialog = ProgressDialog.show(TwitterFeedActivity.this,                                         "", "Loading. Please wait...", true);               }                @Override               protected Void doInBackground(Void... arg0) {                       try {                                HttpClient hc = new DefaultHttpClient();                               HttpGet get = new                               HttpGet("http://search.twitter.com/search.json?q=android");                                HttpResponse rp = hc.execute(get);                                if(rp.getStatusLine().getStatusCode() == HttpStatus.SC_OK)                               {                                       String result = EntityUtils.toString(rp.getEntity());                                       JSONObject root = new JSONObject(result);                                       JSONArray sessions = root.getJSONArray("results");                                       for (int i = 0; i < sessions.length(); i++) {                                               JSONObject session = sessions.getJSONObject(i);                                        Tweet tweet = new Tweet();                                                tweet.content = session.getString("text");                                                tweet.author = session.getString("from_user");                                                tweets.add(tweet);                                       }                              }                      } catch (Exception e) {                              Log.e("TwitterFeedActivity", "Error loading JSON", e);                      }                      return null;          }         @Override         protected void onPostExecute(Void result) {                 progressDialog.dismiss();                 setListAdapter(new TweetListAdaptor(                                 TwitterFeedActivity.this, R.layout.list_item, tweets));          }      }      private class TweetListAdaptor extends ArrayAdapter<Tweet> {              private ArrayList<Tweet> tweets;              public TweetListAdaptor(Context context,                                                                         int textViewResourceId,                                                                        ArrayList<Tweet> items) {                       super(context, textViewResourceId, items);                       this.tweets = items;             }              @Override             public View getView(int position, View convertView, ViewGroup parent) {                     View v = convertView;                     if (v == null) {                             LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);                             v = vi.inflate(R.layout.list_item, null);                     }                     Tweet o = tweets.get(position);                      TextView tt = (TextView) v.findViewById(R.id.toptext);                     TextView bt = (TextView) v.findViewById(R.id.bottomtext);                     tt.setText(o.content);                     bt.setText(o.author);                      return v;             }        } }

You should now have a complete app requesting the Twitter data in the background.

The AsyncTask has quite a few more features to explore. You can be sure we will cover those in future articles. In the meantime you should read the excellent Android document covering these classes at http://developer.android.com.

Tags:android

Related Articles

  • Loading Twitter Data into Android with Lists

    Loading Twitter Data into Android with ListsFebruary 22

    In my previous article, we created your first Android app featuring a basic Hello World function. Well, it seems the real Hello World of the mobile world is a Twitter feed. This tutorial will guide you through creating a simple Android app to display

  • Twitter 2 for Android crash every time I try uploading multi photosFebruary 23

    I'm using the new Twitter 2 on Android 2.1. Whenever I hit the button which enables me to upload multiple photos in a single tweet, I always get the error "The application Camera (process com.sonyericsson.camera) has stopped unexpectidly. Please try

  • How do I load BSP files in Android?October 14

    I understand that Opengl directly cannot load BSP files. How would I go about loading them into my Android game? --------------Solutions------------- Luckily, the BSP file format is well documented (with an overview on wikipedia). While this is great

  • How to scrape data from Android apps? February 21

    How could you scrape data from an Android app (one without an available API)? There are some apps that contain personal data I've entered that I'd like to be able to hook into and extract (rather than manual transcribing by hand). --------------Solut

  • Flash Script - Load XML Data into FlashApril 28

    Here, I'll show you how to load xml data into Flash. Note that this tutorial assumes that you have a very good knowledge of xml. Download the sample files here. 1. Let's start by creating a xml document. Open notepad and insert: <?xml version="1.0

  • How can I view my Google Analytics data on Android?October 2

    I want to view the Google Analytics of my web sites on my Android 2.1 mobile phone. How can I view my Google Analytics data on Android? --------------Solutions------------- Actually, they now have their very own Google Analytics app. Enjoy! Descripti

  • How can I load XML data into a MySQL database?October 11

    On Windows, I can use this statement to load XML data into the database: LOAD XML LOCAL INFILE 'c:/users/yourname/desktop/myfile.xml' INTO TABLE MyTable ROWS IDENTIFED BY '<product>' However, I cannot seem to accomplish this on Ubuntu 11.2.0 with th

  • Best way of representing Hierarchical data in android

    Best way of representing Hierarchical data in androidFebruary 24

    I am trying to find out the best way of representing hierarchical data in android. There are no tree view controls and expandable list views dont work since i can have many levels of hierarchy. I thought I could use the Pulse app view to show data bu

  • How to load spatial data into ArcGIS Javascript API application from SQL Server 2008?March 1

    I have geometry data in SQL Server 2008 R2. How do I load that spatial data using the ArcGIS Javascript API? --------------Solutions------------- Write a WCF service that returns a JSONP response. WCF service should return a data in following format:

  • Apex Data Loader is loading previous dateFebruary 3

    Apex Data loader is loading previous date. Salesforce Organization Default Time Zone is GMT+05:30 Salesforce User Time Zone is GMT+05:30 Apex Data Loader time zone is GMT+05:30 CSV, for Apex Data Loader, has date as 2013-01-01 Salesforce is showing 1

  • Get data/data/com.android.browser/databases/webview.db from backup.abFebruary 22

    I followed Nikolay Elenkov's method of unpacking backup.ab. Is it possible to get data/data/com.android.browser/databases/webview.db from backup.ab? --------------Solutions------------- I found a bypass to this problem. Just do a full adb backup incl

  • Is data/data/com.android.browser/databases/webview.db being encrypted?March 25

    I rooted my phone (Galaxy Nexus 4.1.1 JRO03C). I am able to access nearly all files under data/data/* folder. However, a few files cannot be opened, such as data/data/com.android.browser/databases/webview.db I suspect these files are encrypted. Is th

  • Unable to load ntuser.dat for a currently non-logged user

    Unable to load ntuser.dat for a currently non-logged userMay 22

    I have a Windows XP machine with 2 accounts: "User" - limited acc "Admin" - has strong password User got a virus, that blocked access. I logined as Admin and launched regedit. I tried to load HKEY_CURRENT_USER for "User", usi

  • ClientContext.Load on data that is already loadedJune 14

    I need clarification how does ClientContext.ExecuteQuery method works in client context. For example if I use ClientContext.Load to load the data that is already initialized - will it make an request again to refresh loaded information or use previou

  • Searching Open Source tool to load raster data into Oracle spatialSeptember 4

    I'm looking for a tool to load raster data into oracle spatial. I found several commercial products but no open source tool. Is there anything available in the open source world? --------------Solutions------------- I have also been looking for a sof

  • How to transfer data from Android device to PC that runs on Windows via USBSeptember 12

    I am using Kindle Fire which runs on Android and need to transfer data from Android to my PC via USB. I have been using SnapPea for 9 months and it worked flawlessly. Today, when I really need to download these files urgently, it doesn't work... The

  • Developing a browser-based RPG: Loading user dataDecember 30

    I'm writing a browser-based RPG in JavaScript, HTML5 with Canvas, EaselJS, etc... I'd like to develop the back end data saving/user profile aspect now. This is a single player RPG where the player will interact with NPCs to solve quests. It is not on

  • MySQL warnings while loading integer data into table (SOLVED)February 13

    For some reason, after loading a large amount of data into the following table (from a single file), all of the values register as 0. CREATE TABLE secondTable ( freq int NULL, w1 int NULL, w2 int NULL, w3 int NULL ) Then loading the data: LOAD DATA L

  • Loading OSM data into QGISFebruary 28

    I have been trying to use OSM maps and my data is 60 GB around. I have imported it to QGIS as a vector layer but it seems the data is so big that the rendering is taking forever. My system has Intel Dual Core and 2GB of RAM. So can anyone please tell

  • How to implement displayed LaTeX format data on Android devices April 1

    I'm working on a study that implements displayed format data on Android devices and it's original source code is written in LaTeX format. I'm puzzled. Although there have been Android software for such function, I don't know how to achieve that as I

Copyright (C) 2017 ceus-now.com, All Rights Reserved. webmaster#ceus-now.com 14 q. 0.555 s.