Introduction
This article is brief introduction to using the twitter4j API, which is used for creating your own Twitter applications in Java. This API gives access to the Twitter infrastructure, and allows you to do such tasks as updating your status, getting a list of your followers, and many other useful tasks all from your Java program.
A good understanding of Java and Swing gui’s is needed. Our goal is to build a basic desktop application using Swing as our view technology, that will:
- Login through a pop-up
- Display the users followers
- Display the users updates
- Allow the user to update his status on Twitter (send a tweet)
- Search for Tweets
- Send direct tweets
This will serve as a skeleton for your own applications. Use your imagination, and feel free to embellish on the example we create here. Although you could use any view technology, such as JSP, I’ve decided to use Swing in order to avoid entering into too many additional topics.
Currently, there are three Java Twitter API’s; Twitter4J by Yusuke Yamamoto, Java-Twitter by DeWitt Clinton, and JTwitter by Daniel Winterstein. At the time of this writing Twitter4J is at version 2.0.8, Java-Twitter is at version 0.9, and JTwitter is at version 1.2. We’ll be using Twitter4J since it’s the most mature of the three API’s.
Before getting started, you will need a Twitter account; this takes all of 3 minutes to complete at most. You will also need to understand the basics. What a tweet is, following others, and sending direct tweets. And off we go!
import twitter4j.*;public class Test {
public static void main(String[] args) throws TwitterException {
Twitter twitter = new Twitter(“username”, “password”); // login to service
twitter.updateStatus(“Tweeting!”); // update your status
}
}
}
In the preceding code, we first need to import the twitter4j classes. Inside of the main method, an instance of the Twitter class is created. The user name and password are passed in as strings to the constructor. If the login is successful, the users status is updated with the call to the method updateStatus
. You can verify that the update worked by logging in to your Twitter account.
Since a login is obviously required to do anything, our application should first display a dialog which requires the necessary data to login. This dialog will have two JLabels
, a JTextField
, a JPasswordField
, a confirmation JButton
, and a cancel JButton
in case the user changes their mind. Just to keep it clean, we’ll create a class for our login dialog.
import javax.swing.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.*;class LoginDialog extends JDialog implements ActionListener {
JLabel labelName;
JLabel labelPass;
JTextField textName;
JPasswordField passField;
JButton okButton;
JButton cancelButton;JDialog dialog;
public LoginDialog() {
JPanel panelOne = new JPanel();
labelName = new JLabel(“Name”);
textName = new JTextField(15);
panelOne.add(labelName);
panelOne.add(textName);JPanel panelTwo = new JPanel();
labelPass = new JLabel(“Password”);
passField = new JPasswordField(15);
panelTwo.add(labelPass);
panelTwo.add(passField);JPanel panelThree = new JPanel();
okButton = new JButton(“OK”);
cancelButton = new JButton(“Cancel”);
okButton.addActionListener(this);
cancelButton.addActionListener(this);
panelThree.add(okButton);
panelThree.add(cancelButton);dialog = new JDialog();
dialog.setResizable(false);
dialog.getContentPane().add(panelOne);
dialog.getContentPane().add(panelTwo);
dialog.getContentPane().add(panelThree);
dialog.setTitle(“Login in to Twitter”);
dialog.getContentPane().setLayout(new FlowLayout());
dialog.setSize(350, 150);
dialog.setLocationRelativeTo(null); // place in center of screen
dialog.setModal(true);
dialog.setVisible(true);}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == okButton) {
dialog.dispose();
} else if (e.getSource() == cancelButton) {
System.exit(0);
}
}public String getUserName() {
return textName.getText();
}public String getPassword() {
return String.valueOf(passField.getPassword());
}
}
Up to now, this is all pretty straight forward, old fashion Java code. Here we are using a FlowLayout, and we’re inheriting from JDialog
. Our implemented action is to either close the dialog window if the ok button is pressed, or to exit the program all together. There are two getter methods; getUserName()
and getPassword()
. If the ok button is pressed, we will pass on the information through the get methods. The actual login will occur in the main program which will hold our Twitter instance, and pass it around as needed. If you want to test this you can add a main to this class such as this:
public static void main(String[] args) throws TwitterException{
LoginDialog login = new LoginDialog();
String userName = login.getUserName();
String password = login.getPassword();try{
Twitter twitter = new Twitter(userName, password);
twitter.verifyCredentials();
JOptionPane.showMessageDialog(login, “Login successful!”);
} catch(TwitterException e){
JOptionPane.showMessageDialog(login, “Unable to login”);
}
}
Creating a new Twitter instance does not throw a TwitterException
, but calling the method verifyCredentials()
does. Upon a successful login, you should see the message in figure 2.
Displaying your Followers
Our sample application will contain three main panels. One for displaying your followers, a second for displaying your tweets and updating them, and a third that will both search for tweets and send direct messages. The following code listing shows the code for follower display panel.
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.User;import javax.swing.*;
import java.net.URL;
import java.awt.*;public class FollowerPanel extends JPanel {
URL imageUrl;
int panelWidth = 250;
int panelHeight = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();
java.util.List<User> followers;
JLabel followerName;
JLabel followerImg;
JPanel followerPanel;
JLabel followerTitleLabel;
JScrollPane followerJScrollPane;public FollowerPanel(Twitter twitter) throws TwitterException {
followers = twitter.getFollowers();
this.setLayout(new GridLayout(1, followers.size()));
this.setSize(panelWidth, panelHeight);
this.setPreferredSize(new Dimension(panelWidth, panelHeight));
this.setMinimumSize(new Dimension(panelWidth, panelHeight));followerPanel = new JPanel();
followerPanel.setLayout(new GridLayout(followers.size(), 1, 0, 0));
followerPanel.setSize(panelWidth, panelHeight);
followerPanel.setPreferredSize(new Dimension(panelWidth, panelHeight));
followerPanel.setMinimumSize(new Dimension(panelWidth, panelHeight));int subPanelHeight = 50;
for (User u : followers) {
imageUrl = u.getProfileImageURL();followerName = new JLabel(u.getName());
followerImg = new JLabel(new ImageIcon(imageUrl));
followerImg.setSize(50, 50);
followerImg.setPreferredSize(new Dimension(50, subPanelHeight));
followerImg.setMaximumSize(new Dimension(50, subPanelHeight));
JPanel thisFollowerPanel = new JPanel(new GridLayout(1, 2));
thisFollowerPanel.add(followerName);
thisFollowerPanel.add(followerImg);followerPanel.add(thisFollowerPanel);
}
followerJScrollPane = new JScrollPane(followerPanel,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, // vertical bar
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
this.add(followerJScrollPane);
}
}
The constructor requires a single argument of type Twitter, and throws an exception of TwitterException. We make a call to method twitter.getFollowers()
which returns a List of type twitter4j.User. We iterate through the list of users, and retrieve each user image location by a call to getProfileImageURL()
, and the user name by a call to method getName()
. Notice we’re not actually getting the byte data for the image, but rather a URL which is all we need to create a new ImageIcon.
Class FollowerPanel
inherits from JPanel
. Because it’s nice to both have the image and the display name of our followers, we’ll create a JPanel
that has a JLabel
(containing the name of the follower), and an ImageIcon that will hold the followers display picture.
Each follower JPanel
is positioned using the GridLayout
nested inside of the previously mentioned JPanel
which itself is placed inside of a JScrollBar
. The JScrollBar
is then added to the instance, which again is a JPanel. The following figure shows the resulting panel.
Figure 3 – The follower panel
The Time Line
The single most important thing you’re going to want to do is send tweets! You’re probably going to want to see the time line of your updates as well. Note, we’re not going to duplicate the time line exactly how it works on Twitter, which mixes updates from everyone that you follow, but rather just display your own. Again, modify to your hearts content.
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.Status;import javax.swing.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.*;
import java.util.*;
import java.text.SimpleDateFormat;public class StatusTimeLine implements ActionListener {
Twitter twitter;
JPanel timeLinePanel;
JPanel updatePanel;
JTextField updateField;
JButton updateButton;public StatusTimeLine(Twitter twitter) throws TwitterException {
this.twitter = twitter;
timeLinePanel = new JPanel();
updateField = new JTextField();updatePanel = new JPanel(new FlowLayout());
updatePanel.setSize(800, 30);
updatePanel.setPreferredSize(new Dimension(800, 30));
updatePanel.setMaximumSize(new Dimension(800, 30));updateButton = new JButton(“Update”);
updateButton.setSize(90, 20);
updateButton.setPreferredSize(new Dimension(90, 20));
updateButton.setMaximumSize(new Dimension(90, 20));
updateButton.addActionListener(this);updateField = new JTextField();
updateField.setSize(600, 20);
updateField.setPreferredSize(new Dimension(600, 20));
updateField.setMaximumSize(new Dimension(600, 20));updatePanel.add(updateField);
updatePanel.add(updateButton);
timeLinePanel.add(updatePanel);
updateTimePanel();}
private void updateTimePanel() throws TwitterException {
java.util.List<STATUS> statusList = twitter.getUserTimeline();String statusArr[] = new String[statusList.size()];
timeLinePanel.setLayout(new BoxLayout(timeLinePanel, BoxLayout.Y_AXIS));for (int i = 0; i < statusList.size(); i++) {
Date tweetDate = statusList.get(i).getCreatedAt();
SimpleDateFormat formatter = new SimpleDateFormat(“dd-MMM-yy HH:mm”);
statusArr[i] = formatter.format(tweetDate) + “-” + statusList.get(i).getText();
}JList statusJList = new JList(statusArr);
statusJList.setFixedCellHeight(20);
JScrollPane scrollPane = new JScrollPane(statusJList,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, // vertical bar
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
timeLinePanel.add(scrollPane);
}public JPanel getTimeLinePanel() {
return timeLinePanel;
}public void actionPerformed(ActionEvent event) {
try {
twitter.updateStatus(updateField.getText());
updateTimePanel();
timeLinePanel.remove(1);
timeLinePanel.updateUI();
updateField.setText(“”);
} catch (TwitterException exception) {
JOptionPane.showMessageDialog(null, “An error has occurred while updating.”);
exception.printStackTrace();
}
}
}
At the top of this panel, we’ll place a JTextField for entering the tweet, followed by JButton for updating. In a sub panel underneath, is a JList which will display all of the users tweets. Upon pressing the update button either a pop up will appear, giving an error message, or a tweet is sent to the users account, and the JList is updated with the new tweet.
Again, as with the follower panel, an instance of the Twitter object is passed in to the constructor. In order to retrieve the list of updates, which we want to use to populate the status JList upon loading the program, we get the list of tweets using the method getUserTimeline()
of object twitter. This returns a List of type Status. The time line area, will display both the time of the tweet, and the message. The time of the tweet is retrieved with the method getCreatedAt()
of object Status, which returns a Date object. A SimpleDateFormat object is used to return the date as a string in format dd-MMM-yy HH:mm for clarity. Each tweet message is retrieved by a call to method getText()
. The date and message are concatenated together and inserted into a string array. The status JList is then populated with the status string array, statusArr. Since, you almost certainly have hundreds of tweets 🙂 we’ll place our JList
in a scrollbar for easy viewing.
In short, when the update button is pressed, the method actionPerformed
, sends a tweet, clears the text area, updates the time panel, and redraws the interface with the updated information. If anything goes wrong with the update process, a pop up appears warning the user of an error.
import twitter4j.*;
import javax.swing.*;
import java.awt.*;public class RightSideBar extends JPanel{
int componentsWidth = 180;
int panelHeight = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();public RightSideBar(Twitter twitter) throws TwitterException {
super(new FlowLayout());
this.setSize(componentsWidth, panelHeight);
this.setPreferredSize(new Dimension(componentsWidth, panelHeight));
this.setMaximumSize(new Dimension(componentsWidth, panelHeight));this.add(new SearchTweetsPanel(twitter));
this.add(new DirectMessagePanel(twitter));
this.add(new TrendsPanel(twitter));
}
}
The previous code listing shows the setup for the RightSideBar
class. It inherits from JPanel
, uses a flow layout, and contains instances of SearchTweetsPanel
, DirectMessagePanel
, and TrendsPanel
. The class definition of SearchTweetsPanel is as follows:
import twitter4j.*;import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;public class SearchTweetsPanel extends JPanel {
Twitter twitter;
JButton searchTweetsButton;
JTextField searchTweetsTextField;
int componentsWidth = 180;
int panelHeight = 50;public SearchTweetsPanel(final Twitter twitter) {
super(new FlowLayout());
this.twitter = twitter;this.setSize(componentsWidth, panelHeight);
this.setPreferredSize(new Dimension(componentsWidth, panelHeight));
this.setMaximumSize(new Dimension(componentsWidth, panelHeight));searchTweetsButton = new JButton(“Search Tweets”);
searchTweetsButton.setSize(new Dimension(componentsWidth, 20));
searchTweetsButton.setPreferredSize(new Dimension(componentsWidth, 20));
searchTweetsTextField = new JTextField();
searchTweetsTextField.setSize(componentsWidth, 20);
searchTweetsTextField.setPreferredSize(new Dimension(componentsWidth, 20));
searchTweetsTextField.setMinimumSize(new Dimension(componentsWidth, 20));
searchTweetsButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
QueryResult q = null;
java.util.List<Tweet> tweets = null;try {
q = twitter.search(new Query(searchTweetsTextField.getText()));
tweets = q.getTweets();
String[] tweetArr = new String[tweets.size()];for (int i = 0; i < tweets.size(); i++) {
tweetArr[i] = tweets.get(i).getFromUser() + “-” + tweets.get(i).getText();
}// open a dialog box and show result
JFrame resultDialog = new JFrame();
JList list = new JList(tweetArr);
JScrollPane scrollPane = new JScrollPane(list,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, // vertical bar
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
resultDialog.add(scrollPane);resultDialog.setSize(new Dimension(1100, 320));
resultDialog.setPreferredSize(new Dimension(1100, 320));
resultDialog.setLocationRelativeTo(null);
resultDialog.setVisible(true);
} catch (TwitterException e1) {
e1.printStackTrace();
JOptionPane.showMessageDialog(null, “Error occurred during search, try again.”);
}
}
});
this.add(searchTweetsButton);
this.add(searchTweetsTextField);
}
}
This class contains a JButton(searchTweetsButton), a JTextField, a dialog window defined inside of the constructor, and a JOptionPane which displays on error if there are any problems querying the tweets. Just to keep things compact, we use an anonymous inner class defined inside of the addActionListener
method of the searchTweetsButton
. This is where all the tweeting magic goes on. The method twitter.search()
expects a twitter4j.Query
object, which itself expects a string as input. This string contains the tokens that we’re searching for in tweets. For example,
Query q = new Query(terminator);
This creates a Query object that’s to search for tweets that contain the string terminator
. In order to actually get some searching done, we call the method twitter.search
(new Query(my query string)). In our application, we get our string from the JTextField, searchTweetsTextField by calling it’s getText()
method.
The call to search returns a QueryResult object. To actually get the tweet strings is a little bit more involved. First, we need a java.util.List
collection which contains the type Tweet. In order to populate the list, call the QueryResult objects getTweets()
method. Easy enough. Now, we create an array of strings, tweetArr
, that will hold each tweet text. We iterate through the list of tweets, and insert into tweetArr
the user name, and that tweet. The result of the search is displayed in resultDialog
which is of type JFrame.
Figure 6 – Error pop up
Sending a Direct Message
Remember, in order to send a direct message in Twitter, it is necessary for the other person to follow you, as well as for you to follow them.
The next component we’ll be adding is a button on the right side panel that calls the SendDirectMessageDialog
. Again, we use an anonymous inner class to implement the action.
import twitter4j.Twitter;import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.*;public class DirectMessagePanel extends JPanel {
int componentsWidth = 180;
public DirectMessagePanel(final Twitter twitter) {
JButton sendDirectMessageButton = new JButton(“Send Direct Message”);
sendDirectMessageButton.setSize(new Dimension(componentsWidth, 20));
sendDirectMessageButton.setPreferredSize(new Dimension(componentsWidth, 20));
sendDirectMessageButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
SendDirectMessageDialog dialog = new SendDirectMessageDialog(twitter);
dialog.setVisible(true);
}
});this.add(sendDirectMessageButton);
}
The definition of the SendDirectMessageDialog is a little bit longer.
import twitter4j.Twitter;
import twitter4j.TwitterException;import javax.swing.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.*;public class SendDirectMessageDialog extends JFrame implements ActionListener {
int componentsWidth = 180;
Twitter twitter;
JComboBox followersComboBox;
JTextField messageTextField;
JButton sendButton;public SendDirectMessageDialog(Twitter twitter) {
super(“Direct Message Dialog”);this.twitter = twitter;
setLayout(new FlowLayout());
setSize(new Dimension(620, 100));
setPreferredSize(new Dimension(620, 100));
setLocationRelativeTo(null);messageTextField = new JTextField();
messageTextField.setSize(new Dimension(600, 20));
messageTextField.setPreferredSize(new Dimension(600, 20));
sendButton = new JButton(“Send”);
sendButton.addActionListener(this);followersComboBox = new JComboBox();
try {
String[] followerArr = new String[twitter.getFollowers().size()];
for (int i = 0; i < twitter.getFollowers().size(); i++) {
followerArr[i] = twitter.getFollowers().get(i).getScreenName();
}
followersComboBox = new JComboBox(followerArr);
} catch (TwitterException e1) {
e1.printStackTrace();
JOptionPane.showMessageDialog(this, “Error loading followers list.”);
}add(messageTextField);
add(followersComboBox);
add(sendButton);
}public void actionPerformed(ActionEvent e) {
try {
twitter.sendDirectMessage(followersComboBox.getSelectedItem().toString(),
messageTextField.getText());
JOptionPane.showMessageDialog(this, “Message Sent Successfully”);
messageTextField.setText(“”);
} catch (TwitterException e1) {
e1.printStackTrace();
JOptionPane.showMessageDialog(this, “Error sending direct messsage.”);
}
}
}
the first thing to notice is that we inherit from JFrame this time. The SendDirectMessageDialog
consists of a JTextField
, where the user inputs the message, a JComboBox
which displays the people we can send direct messages to, and the all important send JButton
.
Figure 8 – Direct message success pop up
Displaying Current Trends
Finally, the last part of our sample application is a component that displays the current trends on Twitter. This component contains a JLabel
with the text Trends
, and a JList
which displays the trends.
import twitter4j.Trends;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import javax.swing.*;
import java.awt.*;public class TrendsPanel extends JPanel {
Twitter twitter;
int componentsWidth = 180;
public TrendsPanel(Twitter twitter) throws TwitterException
{
super(new FlowLayout());
this.setSize(new Dimension(componentsWidth, 220));
this.setPreferredSize(new Dimension(componentsWidth, 220));
JLabel trendsLabel = new JLabel(“Trends”);
trendsLabel.setSize(new Dimension(componentsWidth, 20));
trendsLabel.setPreferredSize(new Dimension(componentsWidth, 20));
Trends trends = twitter.getTrends();
String trendsArr[] = new String[trends.getTrends().length];
for (int i = 0; i < trends.getTrends().length; i++) {
trendsArr[i] = trends.getTrends()[i].getName();
}
JList trendsList = new JList(trendsArr);
trendsList.setSize(new Dimension(componentsWidth, 200));
trendsList.setPreferredSize(new Dimension(componentsWidth, 200));
this.add(trendsLabel);
this.add(trendsList);
}
}
The TrendsPanel, again, inherits from JPanel, uses a flow layout, and throws a TwitterException
if there are any problems when the method twitter.getTrends()
is called. The getTrends returns a single Trends object (notice it’s in plural form). The method getTrends()
of class Trend, returns an array of Trend objects (notice it’s in singular form). In a similar fashion to how we extracted the searched tweets earlier, we create a string array called trendsArr
and populate it with a for loop that gets each trend by calling trends.getTrends()[i].getName()
, where it is the index value from the loop. The trends array is then passed in to the JList
. You should see something like the following:
Figure 9 – The current trends JPanel.
Putting it All Together
Now that all of our classes are defined, it’s just a matter of creating a main, and putting all of the components together.
import twitter4j.*;import javax.swing.*;
import java.awt.*;
import java.net.MalformedURLException;public class Application extends JFrame {
public Application() throws MalformedURLException, TwitterException {
super(“Simple Twitter App”);
getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.X_AXIS));LoginDialog login = new LoginDialog();
String userName = login.getUserName();
String password = login.getPassword();Twitter twitter = new Twitter(userName, password);
add(new FollowerPanel(twitter));
add((new StatusTimeLine(twitter)).getTimeLinePanel());
add(new RightSideBar(twitter));
}public static void main(String[] args) throws MalformedURLException, TwitterException {
Application application = new Application();
application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
application.setSize(Toolkit.getDefaultToolkit().getScreenSize());
application.setVisible(true);}
}
We create a class, Application, which extends from JFrame, and throws both MalformedURLException
and TwitterException
. This time, we use a box layout to place our three main components, the FollowerPanel
, StatusTimeLine
, and RightSideBar
. In following with the originally prescribed requirements, the LoginDialog
is first created, from that we get the user name and password which are passed in to the instantiation of our top level Twitter object.
In the main, we create the Application instance, set the default close operation to exit on close, and make it so the window is maximized. If everything is good with the world, when you compile and run, you should see the login dialog.
Conclusion
You’ve probably noticed that this program can’t do half the things that the Twitter website can do. It’s not meant as a replacement, but rather a foundation for your creativity. You could create a desktop application that shows recent updates, automatically, or use the twitter4j api to get statistics and then save it to an SQL database or even, write a web application that adds your twitter information to a small sidebar.
There are already dozens of Twitter applications out there, not just on the web, but also on mobile platforms like the iPhone. One of the more interesting applications I found while researching for this article, is http://asktwitr.com which searches for tweets. But different from other twitter search applications, this one shows the location of the person who sent the tweet on a Google map.
Who knows, maybe you’ll come up with the next killer app.
References
1. Java Swingby Marc Loy, Robert Eckstein, Dave Wood, James Elliot, and Brian Cole
2003 O’Reilly Media, Inc.
2. Twitter tips, tricks, and tweets
by Paul McFredries
2009 Wiley Publishing, Inc.
3. Libraries has a list of api’s
4. The twitter4j repository