Quantcast
Viewing all articles
Browse latest Browse all 5

Scrolling Table – Part 1

I recently had a requirement to create a table layout which had a header row at the top. On the face of it this is relatively easy using TableLayout, but there was a further requirement: The header row should remain static while the data rows scroll. The problem here is that the standard TableLayout does not support a scrolling body area, so how can we achieve this?

Let’s start by creating a 2.3.3 project named “ScrollingTable” with a package name of “com.stylingandroid.ScrollingTable” and an Activity named “ScrollingTableActivity”.

Let’s add a drawable to res/drawable/border.xml:

<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
	android:insetLeft="@dimen/border_offset" 
	android:insetTop="@dimen/border_offset">
	<shape android:shape="rectangle">
		<stroke android:width="@dimen/border_width" 
			android:color="#7F000000" />
		<solid android:color="@android:color/transparent" />
	</shape>
</inset>

Then some dimensions to res/values/dimensions.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="border_width">0.5dp</dimen>
    <dimen name="border_offset">-0.5dp</dimen>
</resources>

And some styles to res/values/styles.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
	<style name="HeaderRow">
		<item name="android:background">#7FAF7F</item>
		<item name="android:layout_width">wrap_content</item>
		<item name="android:layout_height">wrap_content</item>
	</style>
	<style name="HeaderText">
		<item name="android:textColor">#0F7F0F</item>
		<item name="android:shadowColor">#AFFFFFFF</item>
		<item name="android:shadowDx">1</item>
		<item name="android:shadowDy">1</item>
		<item name="android:shadowRadius">1.0</item>
		<item name="android:padding">5dp</item>
		<item name="android:gravity">center</item>
		<item name="android:textSize">14sp</item>
		<item name="android:textStyle">bold</item>
		<item name="android:background">@drawable/border</item>
		<item name="android:layout_width">wrap_content</item>
		<item name="android:layout_height">wrap_content</item>
	</style>
	<style name="BodyRow">
		<item name="android:background">#DFDFDF</item>
		<item name="android:layout_width">wrap_content</item>
		<item name="android:layout_height">wrap_content</item>
	</style>
	<style name="BodyText">
		<item name="android:textColor">#7F7F7F</item>
		<item name="android:shadowColor">#AFFFFFFF</item>
		<item name="android:shadowDx">1</item>
		<item name="android:shadowDy">1</item>
		<item name="android:shadowRadius">1.0</item>
		<item name="android:padding">5dp</item>
		<item name="android:gravity">center</item>
		<item name="android:textSize">14sp</item>
		<item name="android:textStyle">bold</item>
		<item name="android:background">@drawable/border</item>
		<item name="android:layout_width">wrap_content</item>
		<item name="android:layout_height">wrap_content</item>
	</style>
</resources>

To try and solve this we could create two tables, one outside the ScrollView which contains the header, and the other inside the ScrollView which contains the body:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical" 
	android:layout_width="match_parent"
	android:layout_height="match_parent">
	<TableLayout android:layout_height="wrap_content"
		android:layout_width="match_parent">
		<TableRow style="@style/HeaderRow">
			<TextView android:text="Col 1" 
				style="@style/HeaderText" />
			<TextView android:text="Col 2" 
				style="@style/HeaderText"
				android:layout_weight="1" />
			<TextView android:text="Col 3" 
				style="@style/HeaderText" />
			<TextView android:text="Col 4" 
				style="@style/HeaderText" />
		</TableRow>
	</TableLayout>
	<ScrollView android:layout_width="match_parent"
		android:layout_height="200dp">
		<TableLayout android:layout_height="wrap_content"
			android:layout_width="match_parent">
			<TableRow style="@style/BodyRow">
				<TextView android:text="Cell 1,1" 
					style="@style/BodyText" />
				<TextView android:text="Cell 1,2" 
					style="@style/BodyText"
					android:layout_weight="1" />
				<TextView android:text="Cell 1,3" 
					style="@style/BodyText" />
				<TextView android:text="Cell 1,4" 
					style="@style/BodyText" />
			</TableRow>
			<TableRow style="@style/BodyRow">
				<TextView android:text="Cell 2,1" 
					style="@style/BodyText" />
				<TextView android:text="Cell 2,2" 
					style="@style/BodyText"
					android:layout_weight="1" />
				<TextView android:text="Cell 2,3" 
					style="@style/BodyText" />
				<TextView android:text="Cell 2,4" 
					style="@style/BodyText" />
			</TableRow>
			<TableRow style="@style/BodyRow">
				<TextView android:text="Cell 3,1" 
					style="@style/BodyText" />
				<TextView android:text="Cell 3,2" 
					style="@style/BodyText"
					android:layout_weight="1" />
				<TextView android:text="Cell 3,3" 
					style="@style/BodyText" />
				<TextView android:text="Cell 3,4" 
					style="@style/BodyText" />
			</TableRow>
			<TableRow style="@style/BodyRow">
				<TextView android:text="Cell 4,1" 
					style="@style/BodyText" />
				<TextView android:text="Cell 4,2" 
					style="@style/BodyText"
					android:layout_weight="1" />
				<TextView android:text="Cell 4,3" 
					style="@style/BodyText" />
				<TextView android:text="Cell 4,4" 
					style="@style/BodyText" />
			</TableRow>
			<TableRow style="@style/BodyRow">
				<TextView android:text="Cell 5,1" 
					style="@style/BodyText" />
				<TextView android:text="Cell 5,2" 
					style="@style/BodyText"
					android:layout_weight="1" />
				<TextView android:text="Cell 5,3" 
					style="@style/BodyText" />
				<TextView android:text="Cell 5,4" 
					style="@style/BodyText" />
			</TableRow>
			<TableRow style="@style/BodyRow">
				<TextView android:text="Cell 6,1" 
					style="@style/BodyText" />
				<TextView android:text="Cell 6,2" 
					style="@style/BodyText"
					android:layout_weight="1" />
				<TextView android:text="Cell 6,3" 
					style="@style/BodyText" />
				<TextView android:text="Cell 6,4" 
					style="@style/BodyText" />
			</TableRow>
			<TableRow style="@style/BodyRow">
				<TextView android:text="Cell 7,1" 
					style="@style/BodyText" />
				<TextView android:text="Cell 7,2" 
					style="@style/BodyText"
					android:layout_weight="1" />
				<TextView android:text="Cell 7,3" 
					style="@style/BodyText" />
				<TextView android:text="Cell 7,4" 
					style="@style/BodyText" />
			</TableRow>
			<TableRow style="@style/BodyRow">
				<TextView android:text="Cell 8,1" 
					style="@style/BodyText" />
				<TextView android:text="Cell 8,2" 
					style="@style/BodyText"
					android:layout_weight="1" />
				<TextView android:text="Cell 8,3" 
					style="@style/BodyText" />
				<TextView android:text="Cell 8,4" 
					style="@style/BodyText" />
			</TableRow>
		</TableLayout>
	</ScrollView>
</LinearLayout>

if we run this, we can see that we get the scrolling behaviour that we require, but the column widths of the body cells do not match those of the header cells:

One solution that we could use is to duplicate the entire table, and effectively hide much of it by setting the heights of some of the rows to 0dp. This will effectively hide some of the rows, while maintaining their content which enables TableLayout to correctly calculate the cell widths:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical" 
	android:layout_width="match_parent"
	android:layout_height="match_parent">
	<TableLayout android:layout_height="wrap_content"
		android:layout_width="match_parent">
		<TableRow style="@style/HeaderRow">
			<TextView android:text="Col 1" 
				style="@style/HeaderText" />
			<TextView android:text="Col 2" 
				style="@style/HeaderText"
				android:layout_weight="1" />
			<TextView android:text="Col 3" 
				style="@style/HeaderText" />
			<TextView android:text="Col 4" 
				style="@style/HeaderText" />
		</TableRow>
		<TableRow style="@style/BodyRow">
			<TextView android:text="Cell 1,1" 
				android:layout_height="0dp"
				style="@style/BodyText" />
			<TextView android:text="Cell 1,2" 
				android:layout_height="0dp"
				style="@style/BodyText"
				android:layout_weight="1" />
			<TextView android:text="Cell 1,3" 
				android:layout_height="0dp"
				style="@style/BodyText" />
			<TextView android:text="Cell 1,4" 
				android:layout_height="0dp"
				style="@style/BodyText" />
		</TableRow>
		.
		.
		.
		<TableRow style="@style/BodyRow">
			<TextView android:text="Cell 8,1" 
				android:layout_height="0dp"
				style="@style/BodyText" />
			<TextView android:text="Cell 8,2" 
				android:layout_height="0dp"
				style="@style/BodyText"
				android:layout_weight="1" />
			<TextView android:text="Cell 8,3" 
				android:layout_height="0dp"
				style="@style/BodyText" />
			<TextView android:text="Cell 8,4" 
				android:layout_height="0dp"
				style="@style/BodyText" />
		</TableRow>
	</TableLayout>
	<ScrollView android:layout_width="match_parent"
		android:layout_height="200dp">
		<TableLayout android:layout_height="wrap_content"
			android:layout_width="match_parent">
			<TableRow style="@style/HeaderRow">
				<TextView android:text="Col 1" 
					android:layout_height="0dp"
					style="@style/HeaderText" />
				<TextView android:text="Col 2" 
					android:layout_height="0dp"
					style="@style/HeaderText"
					android:layout_weight="1" />
				<TextView android:text="Col 3" 
					android:layout_height="0dp"
					style="@style/HeaderText" />
				<TextView android:text="Col 4" 
					android:layout_height="0dp"
					style="@style/HeaderText" />
		</TableRow>
			<TableRow style="@style/BodyRow">
				<TextView android:text="Cell 1,1" 
					style="@style/BodyText" />
				<TextView android:text="Cell 1,2" 
					style="@style/BodyText"
					android:layout_weight="1" />
				<TextView android:text="Cell 1,3" 
					style="@style/BodyText" />
				<TextView android:text="Cell 1,4" 
					style="@style/BodyText" />
			</TableRow>
			.
			.
			.
			<TableRow style="@style/BodyRow">
				<TextView android:text="Cell 8,1" 
					style="@style/BodyText" />
				<TextView android:text="Cell 8,2" 
					style="@style/BodyText"
					android:layout_weight="1" />
				<TextView android:text="Cell 8,3" 
					style="@style/BodyText" />
				<TextView android:text="Cell 8,4" 
					style="@style/BodyText" />
			</TableRow>
		</TableLayout>
	</ScrollView>
</LinearLayout>

If we run this, we can see that it gives us the scrolling behaviour that we require, and also lines up the columns correctly:

Although this does what we require, it is pretty inefficient because we are duplicating the entire table. It works where we have a small number of rows, but is rather hacky, and could run in to memory problems where we have a large number of rows.

After coming up with this approach, I found an article on the Android Adventures blog which offers a similar approach to mine. It is slightly more efficient because it does not fully duplicate the data, but is slightly less flexible as a result because it assumes that the header titles will always be longer than the text in the body cells.

In the second part of this article, we’ll look at an alternative approach which is a little more complex to implement, but should give us the behaviour that we require while being a little better behaved in terms of memory usage.

The source code for this article can be found here.

© 2011, Mark Allison. All rights reserved. This article originally appeared on Styling Android.

Portions of this page are modifications based on work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License

Image may be NSFW.
Clik here to view.
Delicious
Image may be NSFW.
Clik here to view.
StumbleUpon
Image may be NSFW.
Clik here to view.
Reddit
Image may be NSFW.
Clik here to view.
Digg
Image may be NSFW.
Clik here to view.
Bookmark/Favorites
Image may be NSFW.
Clik here to view.
Share


Viewing all articles
Browse latest Browse all 5

Trending Articles