--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="de.example.lists"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk android:minSdkVersion="15" />
+
+ <application
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name" >
+ <activity
+ android:name=".ListActivityActivity"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
\ No newline at end of file
--- /dev/null
+/** Automatically generated file. DO NOT MODIFY */
+package de.example.lists;
+
+public final class BuildConfig {
+ public final static boolean DEBUG = true;
+}
\ No newline at end of file
--- /dev/null
+/* AUTO-GENERATED FILE. DO NOT MODIFY.
+ *
+ * This class was automatically generated by the
+ * aapt tool from the resource data it found. It
+ * should not be modified by hand.
+ */
+
+package de.example.lists;
+
+public final class R {
+ public static final class attr {
+ }
+ public static final class drawable {
+ public static final int ic_launcher=0x7f020000;
+ public static final int news_icon_1=0x7f020001;
+ public static final int news_icon_2=0x7f020002;
+ }
+ public static final class id {
+ public static final int list=0x7f050000;
+ public static final int news_entry_icon=0x7f050001;
+ public static final int news_entry_subtitle=0x7f050003;
+ public static final int news_entry_title=0x7f050002;
+ }
+ public static final class layout {
+ public static final int main=0x7f030000;
+ public static final int news_entry_list_item=0x7f030001;
+ }
+ public static final class string {
+ public static final int app_name=0x7f040001;
+ public static final int desc=0x7f040002;
+ public static final int hello=0x7f040000;
+ }
+}
--- /dev/null
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
--- /dev/null
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-15
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical" >
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/hello" />
+
+ <ListView android:id="@+id/list"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" />
+
+</LinearLayout>
\ No newline at end of file
--- /dev/null
+<?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"
+ android:orientation="vertical">
+
+ <!-- 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"
+ android:orientation="vertical"
+ android:contentDescription="@string/desc"/>
+
+
+
+ <!-- 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>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="hello">Hello World, ListActivityActivity!</string>
+ <string name="app_name">ListActivity</string>
+ <string name="desc">description</string>
+
+</resources>
\ No newline at end of file
--- /dev/null
+package de.example.lists;
+
+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 ListActivityActivity 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;
+ }
+}
\ No newline at end of file
--- /dev/null
+package de.example.lists;
+
+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;
+ }
+
+}
--- /dev/null
+package de.example.lists;
+
+import android.widget.ArrayAdapter;
+import java.text.DateFormat;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+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;
+ }
+
+
+}