Wednesday, 6 May 2015

Lesson 10 Using Android Intents Tutorial ---- 1

Using Android Intents Tutorial

Intents are just what they sound like, a way to declare to the Android Framework what you intend to do. This can be starting a specific activity, or it can be just asking Android to find some program that can perform an action (whether or not you know what programs are available). We can also go in the other direction, and determine what actions are available to us for a particular piece of content. We are going to cover each of these topics in this article.
In this article I showed you how to open a specific activity using the startActivity method and an Intent that specifies a class. This is just the tip of the iceberg when it comes to using intents.
Here is the basic activity I am going to be using for the rest of this article. At this point, it is just a ListActivity which is going to reference the different examples we are going to see.
package com.learnandroid.intents;
 
import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
 
public class UsingIntentsActivity extends ListActivity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ListAdapter adapter = createAdapter();
        setListAdapter(adapter);
    }
 
    protected ListAdapter createAdapter()
    {
     String[] listValues = new String[] {
       "The Activity You Know",
       "The URI You Know",
       "When You Just Don't Know"
     };
 
     // Create a simple array adapter (of type string) with the test values
     ListAdapter adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1
       , listValues);
 
     return adapter;
    }
}

Lesson 9 List with custom objects and adapter ---- 4

List with custom objects and adapter

Putting it all together

We’ve defined the project, layouts, objects, and adapter. Now that we have our new tools, we need to put them together to achieve our goal.
In order to make use of our new adapter, we’ll need to go back to our original Activity. We need to do the following:
  • Reference the ListView in the MainActivity
  • Bind the ListView to the NewsEntryAdapter
  • Populate the NewsEntryAdapter with NewsEntry objects
Here is the code we came up with:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
package com.learnandroid.listviewtutorial.adapter;
 
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.List;
 
import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;
 
public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Setup the list view
final ListView newsEntryListView = (ListView) findViewById(R.id.list);
final NewsEntryAdapter newsEntryAdapter = new NewsEntryAdapter(this, R.layout.news_entry_list_item);
newsEntryListView.setAdapter(newsEntryAdapter);
// Populate the list, through the adapter
for(final NewsEntry entry : getNewsEntries()) {
newsEntryAdapter.add(entry);
}
}
private List<NewsEntry> getNewsEntries() {
// Let's setup some test data.
// Normally this would come from some asynchronous fetch into a data source
// such as a sqlite database, or an HTTP request
final List<NewsEntry> entries = new ArrayList<NewsEntry>();
for(int i = 1; i < 50; i++) {
entries.add(
new NewsEntry(
"Test Entry " + i,
"Anonymous Author " + i,
new GregorianCalendar(2011, 11, i).getTime(),
i % 2 == 0 ? R.drawable.news_icon_1 : R.drawable.news_icon_2
)
);
}
return entries;
}
}
view rawMainActivity.java hosted with ❤ by GitHub
And that’s it! There are a lot more options to explore with ListViews and Adapters not covered here (yet.)
Download this project now: Git | ZIP ]
Consider leaving a comment if you would like to see other details covered from this article, any mistakes, or general questions. Thanks for reading!

Lesson 9 List with custom objects and adapter ---- 3

List with custom objects and adapter

NewsEntryAdapter

Create a new class that extends ArrayAdapter<NewsEntry>. You’ll need to provide a constructor; we’ve selected the (Context, int) signature for our purposes. Additionally we want to override the getView() method to customize how we’ll build each view.
Here is the final class we built. Don’t worry, we’ll dissect this class to understand everything it’s doing.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
package com.learnandroid.listviewtutorial.adapter;
 
import java.text.DateFormat;
 
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
 
/**
* Adapts NewsEntry objects onto views for lists
*/
public final class NewsEntryAdapter extends ArrayAdapter<NewsEntry> {
 
private final int newsItemLayoutResource;
 
public NewsEntryAdapter(final Context context, final int newsItemLayoutResource) {
super(context, 0);
this.newsItemLayoutResource = newsItemLayoutResource;
}
 
@Override
public View getView(final int position, final View convertView, final ViewGroup parent) {
// We need to get the best view (re-used if possible) and then
// retrieve its corresponding ViewHolder, which optimizes lookup efficiency
final View view = getWorkingView(convertView);
final ViewHolder viewHolder = getViewHolder(view);
final NewsEntry entry = getItem(position);
// Setting the title view is straightforward
viewHolder.titleView.setText(entry.getTitle());
// Setting the subTitle view requires a tiny bit of formatting
final String formattedSubTitle = String.format("By %s on %s",
entry.getAuthor(),
DateFormat.getDateInstance(DateFormat.SHORT).format(entry.getPostDate())
);
viewHolder.subTitleView.setText(formattedSubTitle);
// Setting image view is also simple
viewHolder.imageView.setImageResource(entry.getIcon());
return view;
}
 
private View getWorkingView(final View convertView) {
// The workingView is basically just the convertView re-used if possible
// or inflated new if not possible
View workingView = null;
if(null == convertView) {
final Context context = getContext();
final LayoutInflater inflater = (LayoutInflater)context.getSystemService
(Context.LAYOUT_INFLATER_SERVICE);
workingView = inflater.inflate(newsItemLayoutResource, null);
} else {
workingView = convertView;
}
return workingView;
}
private ViewHolder getViewHolder(final View workingView) {
// The viewHolder allows us to avoid re-looking up view references
// Since views are recycled, these references will never change
final Object tag = workingView.getTag();
ViewHolder viewHolder = null;
if(null == tag || !(tag instanceof ViewHolder)) {
viewHolder = new ViewHolder();
viewHolder.titleView = (TextView) workingView.findViewById(R.id.news_entry_title);
viewHolder.subTitleView = (TextView) workingView.findViewById(R.id.news_entry_subtitle);
viewHolder.imageView = (ImageView) workingView.findViewById(R.id.news_entry_icon);
workingView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) tag;
}
return viewHolder;
}
/**
* ViewHolder allows us to avoid re-looking up view references
* Since views are recycled, these references will never change
*/
private static class ViewHolder {
public TextView titleView;
public TextView subTitleView;
public ImageView imageView;
}
}
The constructor we’ve specified below receives a Context and an int.
public NewsEntryAdapter(final Context context, final int newsItemLayoutResource) {
 super(context, 0);
 this.newsItemLayoutResource = newsItemLayoutResource;
}
We must specify the Context to our parent class, ArrayAdapter, and the newsItemLayoutResource tells us which layout we should use for each news item. We’ve already created one at res/layout/news_entry_list_item.xml, so the resource we’ll pass into this constructor will be R.layout.news_entry_list_item.

getView()

Here we’re overriding the getView() method of ArrayAdapter.
@Override
public View getView(final int position, final View convertView, final ViewGroup parent) {
 
 // We need to get the best view (re-used if possible) and then
 // retrieve its corresponding ViewHolder, which optimizes lookup efficiency
 final View view = getWorkingView(convertView);
 final ViewHolder viewHolder = getViewHolder(view);
 final NewsEntry entry = getItem(position);
 
 // Setting the title view is straightforward
 viewHolder.titleView.setText(entry.getTitle());
 
 // Setting the subTitle view requires a tiny bit of formatting
 final String formattedSubTitle = String.format("By %s on %s",
  entry.getAuthor(),
  DateFormat.getDateInstance(DateFormat.SHORT).format(entry.getPostDate())
 );
 
 viewHolder.subTitleView.setText(formattedSubTitle);
 
 // Setting image view is also simple
 viewHolder.imageView.setImageResource(entry.getIcon());
 
 return view;
}
There’s a lot going on here, so let’s break it down. First we’ll talk about the method’s arguments (specifically convertView), the workingView, and then viewHolder.
The primary functionality of the adapter starts at getView. This is what is called each time the ListView needs something to display.
The position argument tells us which item in the array of objects we added is being rendered (which can be very useful to know.)
The convertView argument is our possibly-recycled view. If this is non-null, then this is a view that’s no longer visible and should be used to conserve view objects. If it is null, then we’ll go ahead and create a new view. The resulting view (either reused or newly created) is what we call the working view.
The parent argument is a reference to the parent view containing ours (ListView.) It’s not necessary for our purposes here.
The following lines determine our working view and retrieve a ViewHolder plus the corresponding NewsEntry object.
 // We need to get the best view (re-used if possible) and then
 // retrieve its corresponding ViewHolder, which optimizes lookup efficiency
 final View view = getWorkingView(convertView);
 final ViewHolder viewHolder = getViewHolder(view);
 final NewsEntry entry = getItem(position);
We’ll take a look at getWorkingView() and getViewHolder() soon enough. The getItem() simply returns to us the NewsEntry we’re currently trying to render for the ListView to display.
The remaining lines perform the actual decoration of the view:
 // Setting the title view is straightforward
 viewHolder.titleView.setText(entry.getTitle());
 
 // Setting the subTitle view requires a tiny bit of formatting
 final String formattedSubTitle = String.format("By %s on %s",
  entry.getAuthor(),
  DateFormat.getDateInstance(DateFormat.SHORT).format(entry.getPostDate())
 );
 
 viewHolder.subTitleView.setText(formattedSubTitle);
 
 // Setting image view is also simple
 viewHolder.imageView.setImageResource(entry.getIcon());
 
 return view;
The title is set simply to the NewsEntry title. The subtitle requires a little bit of date formatting, but isn’t altogether hard. Like the title, the image is set simply to the NewsEntryIcon.
Finally we return the view at the end of getView(), which will cause it to be recycled later on when possible.

getWorkingView()

Here we define our own method getWorkingView().
private View getWorkingView(final View convertView) {
 // The workingView is basically just the convertView re-used if possible
 // or inflated new if not possible
 View workingView = null;
 
 if(null == convertView) {
  final Context context = getContext();
  final LayoutInflater inflater = (LayoutInflater)context.getSystemService
       (Context.LAYOUT_INFLATER_SERVICE);
 
  workingView = inflater.inflate(newsItemLayoutResource, null);
 } else {
  workingView = convertView;
 }
 
 return workingView;
}
The working view is the result of whether we have a recycled view or forced to create a new one. This should be pretty straightforward.

getViewHolder()

Here we define our own method getViewHolder().
private ViewHolder getViewHolder(final View workingView) {
 // The viewHolder allows us to avoid re-looking up view references
 // Since views are recycled, these references will never change
 final Object tag = workingView.getTag();
 ViewHolder viewHolder = null;
 
 if(null == tag || !(tag instanceof ViewHolder)) {
  viewHolder = new ViewHolder();
 
  viewHolder.titleView = (TextView) workingView.findViewById(R.id.news_entry_title);
  viewHolder.subTitleView = (TextView) workingView.findViewById(R.id.news_entry_subtitle);
  viewHolder.imageView = (ImageView) workingView.findViewById(R.id.news_entry_icon);
 
  workingView.setTag(viewHolder);
 
 } else {
  viewHolder = (ViewHolder) tag;
 }
 
 return viewHolder;
}
 
/**
 * ViewHolder allows us to avoid re-looking up view references
 * Since views are recycled, these references will never change
 */
private static class ViewHolder {
 public TextView titleView;
 public TextView subTitleView;
 public ImageView imageView;
}
The ViewHolder is a custom class defined out of convenience, efficiency, and some type safety. The primary benefit of the ViewHolder relies on the fact that, when stored as a view’s tag, it is recycled along with the view itself. A view can store any object, known as its tag, for convenient referencing. In our case, we will store an object that already has direct references to the subviews within the item layout. This avoids having to re-lookup (through findViewById()) each subview each time it’s re-drawn. As the number of getView() calls increase, the number of times findViewById() is invoked remains the same at a certain point. We can also cast it to our specific types of views (TextView, ImageView) and never have to cast it again.

Layouts, Objects, Adapter Complete

Now that we’re done with our layouts, objects, and adapter, it’s time to make use of the new tool we have. On the next and final page, we’ll put it all together and conclude the tutorial.

Lesson 9 List with custom objects and adapter ---- 2

List with custom objects and adapter

Custom Objects

Frequently you’ll have some sort of object model that describes the kinds of data you’re trying to show– in our example, we’re going to have a very simple, immutable class called NewsEntry that encapsulates information about a fictitious news entry. Our use cases include a title, author, date of posting, and an icon to show. For simplicity we’ll assume the icon is a local resource in the APK.
Here is our NewsEntry class:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
package com.learnandroid.listviewtutorial.adapter;
 
import java.util.Date;
 
/**
* Encapsulates information about a news entry
*/
public final class NewsEntry {
private final String title;
private final String author;
private final Date postDate;
private final int icon;
 
public NewsEntry(final String title, final String author,
final Date postDate, final int icon) {
this.title = title;
this.author = author;
this.postDate = postDate;
this.icon = icon;
}
 
/**
* @return Title of news entry
*/
public String getTitle() {
return title;
}
 
/**
* @return Author of news entry
*/
public String getAuthor() {
return author;
}
 
/**
* @return Post date of news entry
*/
public Date getPostDate() {
return postDate;
}
 
/**
* @return Icon of this news entry
*/
public int getIcon() {
return icon;
}
 
}
view rawNewsEntry.java hosted with ❤ by GitHub
In our previous tutorial, we used an ArrayAdapter that took simple Strings and bound them to a simple TextView layout for each String in the list. Since we’re using more complex objects, such as NewsEntry, we can’t necessarily rely on a toString() to satisfy our complex view. Android actually provides a SimpleAdapter class that could serve our needs, but for the sake of depth we’ll extend and override ArrayAdapter for our purposes.

NewsEntry List Item Layout

We need to define how each item in the list is going to look. We won’t be focusing too much on the layout contents, as we have some excellent tutorials for layouts all by themselves here: Android Layout Tutorial
Here’s the example layout we’re going with:
Lots of Lists 2: News Entry Item Layout
Lots of Lists 2: News Entry Item Layout
This is the final layouts we came up with (plus two icons to use):
Place the following icons under res/drawable-mdpi/. They should be located at res/drawable-mdpi/news_icon_1.png and res/drawable-mdpi/news_icon_2.png when finished.
News Icon Variety #1
News Icon Variety #1
News Icon Variety #2
News Icon Variety #2
And place the following layout file under res/layout/news_entry_list_item.xml
12345678910111213141516171819202122232425262728293031323334353637
<?xml version="1.0" encoding="utf-8"?>
 
<!-- Layout for individual news entries in a list -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<!-- Icon shown next to the title/subtitle -->
<ImageView
android:id="@+id/news_entry_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:padding="3dp" />
<!-- Title of the news entry -->
<TextView
android:id="@+id/news_entry_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/news_entry_icon"
android:layout_alignTop="@id/news_entry_icon"
android:layout_margin="5dp"
android:textSize="14sp"
android:textStyle="bold" />
<!-- Subtitle contains author and date -->
<TextView
android:id="@+id/news_entry_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/news_entry_title"
android:layout_below="@id/news_entry_title"
android:textSize="12sp" />
 
</RelativeLayout>
As you can see, we used a RelativeLayout to position our title, icon, and subtitle views. To learn more about RelativeLayouts, check this out: Android Layout Tutorial – RelativeLayouts.
On the next page we’ll look at the implementation of our custom adapter, NewsEntryAdapter.