I want to have a dynamic table, with rows added over time as a result of user interaction, using a TableLayout inside a ScrollView. This works fine, but when I want to scroll to the end of the table using fullScroll()
, it always leaves out the last line; that is, it scrolls so that the one before the last one is visible. The last line is visible when scrolling manually, and the scrollbar is correct too.
I'm of course open to suggestions as to how to make a better layout out of this; but I'm specifically interested in understanding why fullScroll()
behaves that way. Should I give it a different parameter, or use something else altogether? Or does it do that because the newly added line isn't yet visible somehow? (if so, how can I solve that?) Or did I miss some other obvious thing?
The following code replicates the problem:
TestActivity.java:
package com.example.android.tests;
import java.util.Random;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ScrollView;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
public class TestActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
((Button) findViewById(R.id.AddRow)).setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Random rnd = new Random();
TableRow nr = new TableRow(v.getContext());
for (int c=0; c<3; c++) {
TextView nv = new TextView(v.getContext());
nv.setText(Integer.toString(rnd.nextInt(20)-10));
nr.addView(nv);
}
((TableLayout) findViewById(R.id.Table)).addView(nr);
// Scrolls to line before last - why?
((ScrollView) findViewById(R.id.TableScroller)).fullScroll(View.FOCUS_DOWN);
}
});
}
}
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Button
android:text="Add Row"
android:id="@+id/AddRow"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" />
<ScrollView
android:id="@+id/TableScroller"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="@id/AddRow"
android:layout_alignParentTop="true" >
<TableLayout
android:id="@+id/Table"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:stretchColumns="0,1,2" />
</ScrollView>
</RelativeLayout>
Edit: for reference, I implemented Romain Guy's solution as follows:
In TestActivity.java, replace:
// Scrolls to line before last - why?
((ScrollView) findViewById(R.id.TableScroller)).fullScroll(View.FOCUS_DOWN);
with:
// Enqueue the scrolling to happen after the new row has been layout
((ScrollView) findViewById(R.id.TableScroller)).post(new Runnable() {
public void run() {
((ScrollView) findViewById(R.id.TableScroller)).fullScroll(View.FOCUS_DOWN);
}
});
Which works fine.
At the time you are doing your fullScroll() the layout has not happened yet, so the ScrollView uses the "old" size for the table. Instead of calling fullScroll() right away, use View.post(Runnable).
Finding the hint above useful, here is a simple implementation that scrolls a ScrollView to make a given child visible...
a: Prepare the following helper class
public class ScrollToTrick implements Runnable {
ScrollView scroller;
View child;
ScrollToTrick(ScrollView scroller, View child) {
this.scroller=scroller;
this.child=child;
}
public void run() {
scroller.scrollTo(0, child.getTop());
}
}
b) call it like this
my_scroller.post(new ScrollToTrick(my_scroller,child_to_scroll_to) );
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With