Posted by: kahgoh on: 29 March, 2009
Lately, I was looking at how to present data in a table format, where each row would be data from an object, and allow users to select a particular row. One way of doing it is writing a ListAdapter and using it with a ListView. However, the way I tried to do it was using the TableLayout.
Admittedly, using the TableLayout looked like it is more complicated than using the ListView, although I have yet to try using ListView. Using the TableLayout, the view is programatically created (without using an XML file). In order to implement this, the implementing class needs to keep track of a number of things.
1 package com.android.selectable;
2
3 import java.util.Collection;
4
5 import android.content.Context;
6 import android.graphics.Color;
7 import android.util.Log;
8 import android.view.MotionEvent;
9 import android.view.View;
10 import android.widget.TableLayout;
11 import android.widget.TableRow;
12
13 /**
14 * Manages the display of the watch list.
15 *
16 * @author Kah
17 */
18 public class ListDisplay<T>
19 {
20 /**
21 * Reference to the list of stocks that are in the
22 * watch list.
23 */
24 private final Collection<T> itemsCollection;
25
26 /**
27 * Reference to the view that displays the watch
28 * list on the user interface. This needs to
29 * inflated later, since the instance of the
30 * layout inflater is required to create it.
31 */
32 private final TableLayout display;
33
34 private final Context appContext;
35
36 private final DisplayAdapter<T> dataAdapter;
37
38 private SelectionReceiver<T> selectionObserver;
39
40 private T highlighted;
41
The DisplayAdapter is used to convert the objects into the data that gets displayed. A SelectionReceiver is notified when a item is selected by clicking on it.
The constructor creates the TableLayout, which may be set as the content view or may be added to another view.
67 /**
68 * Retrieves the display component to display the
69 * contents of the watch
70 * list on the user interface.
71 *
72 * @return The display view for viewing the contents
73 * of the watch list.
74 */
75 public View getDisplay()
76 {
77 return display;
78 }
79
80 /**
81 * Retrieves the last item that was selected, but not
82 * necessarily "clicked".
83 *
84 * @return The item that is currently at the cursor.
85 */
86 public T getSelected()
87 {
88 return highlighted;
89 }
90
Next, the data from the list needs to be loaded into the table. This is done by adding a series table rows. Each row needs to be made focusable. Listeners are added to row to handle the highlighting and when the user clicks on a row. For highlighting, the OnFocusChangeListener and OnTouchListener are required. Clicking is handled by OnClickListener.
91 /**
92 * Loads the current contents of the list to the
93 * displayed list.
94 */
95 private void initialiseDisplay()
96 {
97 // All rows will have the same parameters, so just
98 // create it once.
99 final TableRow.LayoutParams rowParams = new
100 TableRow.LayoutParams(
101 TableRow.LayoutParams.FILL_PARENT,
102 TableRow.LayoutParams.WRAP_CONTENT);
103
104
105 // Go through the list and create table entries for
106 // each one.
107 for (T item: itemsCollection)
108 {
109 TableRow newRow = new TableRow(appContext);
110 newRow.setLayoutParams(rowParams);
111
112 View[] content = dataAdapter.getContent(item);
113 for (int index = 0; index < content.length; index++)
114 {
115 newRow.addView(content[index]);
116 }
117
118 newRow.setFocusable(true);
119 newRow.setFocusableInTouchMode(true);
120 newRow.setOnFocusChangeListener(
121 new RowHighlighter(item));
122 newRow.setOnClickListener(new RowSelector(item));
123
124 display.addView(newRow);
125 }
126 }
127
Notifications about which item the user clicks is sent via the SelectionReceiver. This is an external class that we define. Of course, there needs to be a way of setting which instance of the SelectionReceiver will receive the notification.
128 /**
129 * Changes the receiver that is notified when an item
130 * is "clicked".
131 *
132 * @param receiver
133 * The receiver that will be notified of changes.
134 */
135 public void setSelectionReceiver(
136 SelectionReceiver<T> receiver)
137 {
138 this.selectionObserver = receiver;
139 }
140
The definition for the highlighting class:
141 /**
142 * This focus change listener handles the changing the
143 * background colour for highlighting the focused row.
144 * It also updates the variable keeping track the
145 * highlighted item.
146 */
147 private class RowHighlighter implements
148 View.OnFocusChangeListener, View.OnTouchListener
149 {
150
151 /**
152 * The item that the highlighter is associated with.
153 */
154 private final T association;
155
156 public RowHighlighter(T association)
157 {
158 this.association = association;
159 }
160
161 /**
162 * {@inheritDoc}
163 */
164 @Override
165 public void onFocusChange(View v, boolean hasFocus)
166 {
167 int bgColour = Color.TRANSPARENT;
168 if (hasFocus == true)
169 {
170 bgColour = Color.RED;
171 highlighted = association;
172 }
173
174 v.setBackgroundColor(bgColour);
175 }
176
177 /**
178 * {@inheritDoc}
179 */
180 @Override
181 public boolean onTouch(View v, MotionEvent event)
182 {
183 if (event.getAction() != MotionEvent.ACTION_UP)
184 {
185 v.requestFocus();
186 }
187 return false;
188 }
189
190 }
191
Finally, the internal class for updating the row that is selected.
192 /**
193 * Handles the "clicking" on a row.
194 *
195 * @author Kah
196 */
197 private class RowSelector implements View.OnClickListener
198 {
199 private final T association;
200
201 public RowSelector(T association)
202 {
203 this.association = association;
204 }
205
206 /**
207 * {@inheritDoc}
208 */
209 @Override
210 public void onClick(View v)
211 {
212 if (selectionObserver != null) {
213 selectionObserver.itemSelected(association);
214 }
215 }
216 }
217 }
218
219
If you wish to download the source code, you can get it from here. As mentioned previously, this seems a long way of achieving a selectable list of items with three columns. Using a ListView, the item selection and item highlighting is taken care of. But how to use ListView is left for another day.