What i want is, upon the device changes orientation, the top line on the screen when in Portrait remains the top line on screen in Landscape. And vice versa.
As the width of the screen is likely to be different between Portrait and Landscape, the line width of the text, which means also the width of the TextView
and the ScrollView
, will vary. Thus, the line-wrap will be different in different screen configurations (Portrait vs. Landscape, large vs. small). The line-break will be at different position in different cases.
There are three not-so-perfect solutions for your reference. Also explained the shortcomings of them.
Firstly, The very most basic approach:
(1) By just storing the y-offset in pixel
Please take a look at: http://eliasbland.wordpress.com/2011/07/28/how-to-save-the-position-of-a-scrollview-when-the-orientation-changes-in-android/
Why this is not-so-perfect:
In Portrait, lines are wrapped.
Line_1_Word_A Line_1_Word_B Line_1_Word_C
Line_1_Word_D
Line_2_Word_A Line_2_Word_B Line_2_Word_C
Line_2_Word_D
Line_3_Word_A Line_3_Word_B Line_3_Word_C
Line_3_Word_D
In Landscape, lines are not wrapped.
Line_1_Word_A Line_1_Word_B Line_1_Word_C Line_1_Word_D
Line_2_Word_A Line_2_Word_B Line_2_Word_C Line_2_Word_D
Line_3_Word_A Line_3_Word_B Line_3_Word_C Line_3_Word_D
Imagine reading Line_2_Word_A
(at screen top) in Portrait while saving. When changed to Landscape, it will be showing Line_3_Word_A
(at screen top). (Because of two-lines-offset-in-pixel from top.)
Then i come up with an approach,
(2) By saving the scroll-percentage
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
final ScrollView scrollView = (ScrollView) findViewById(R.id.Trial_C_ScrollViewContainer);
outState.putFloatArray(ScrollViewContainerScrollPercentage,
new float[]{
(float) scrollView.getScrollX()/scrollView.getChildAt(0).getWidth(),
(float) scrollView.getScrollY()/scrollView.getChildAt(0).getHeight() });
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
final float[] scrollPercentage = savedInstanceState.getFloatArray(ScrollViewContainerScrollPercentage);
final ScrollView scrollView = (ScrollView) findViewById(R.id.Trial_C_ScrollViewContainer);
scrollView.post(new Runnable() {
public void run() {
scrollView.scrollTo(
Math.round(scrollPercentage[0]*scrollView.getChildAt(0).getWidth()),
Math.round(scrollPercentage[1]*scrollView.getChildAt(0).getHeight()));
}
});
}
This works perfectly if and only if the length of every line is the same.
Why this is not-so-perfect:
In Portrait, lines are wrapped.
Line_1_Word_A Line_1_Word_B
Line_1_Word_C Line_1_Word_D
Line_1_Word_E Line_1_Word_F
Line_2_Word_A
Line_3_Word_A
Line_4_Word_A
In Landscape, lines are not wrapped.
Line_1_Word_A Line_1_Word_B Line_1_Word_C Line_1_Word_D Line_1_Word_E Line_1_Word_F
Line_2_Word_A
Line_3_Word_A
Line_4_Word_A
Imagine reading Line_2_Word_A
(at screen top) in Portrait while saving. When changed to Landscape, it will be showing Line_3_Word_A
(at screen top). (Because it is scrolled by 50%.)
Then i found this approach which indeed
(3) Storing the first visible line
Please take a look at (first answer): How to restore textview scrolling position after screen rotation?
Why this is not-so-perfect:
In Portrait, lines are wrapped.
Line_1_Word_A Line_1_Word_B
Line_1_Word_C Line_1_Word_D
Line_1_Word_E Line_1_Word_F
Line_2_Word_A
Line_3_Word_A
Line_4_Word_A
In Landscape, lines are not wrapped.
Line_1_Word_A Line_1_Word_B Line_1_Word_C Line_1_Word_D Line_1_Word_E Line_1_Word_F
Line_2_Word_A
Line_3_Word_A
Line_4_Word_A
Imagine reading Line_1_Word_E
(at screen top) in Portrait while saving. When changed to Landscape, it will be showing Line_3_Word_A
(at screen top). (Because it is the third line.)
A perfect one would be, in Landscape, showing Line_1_Word_A
(as well as Line_1_Word_E
) at screen top.
Could you please suggest a perfect approach?
Edit:
After a few thoughts, is method (3) identical to method (1) in fact? :-/
Edit 2:
Well, i have just come up with another not-so-perfect-yet-more-perfect approach than the above three:
(4) Paragraph-based storing
Separating paragraphs (or blocks of texts) into different TextView objects.
Then by the codes like method (3), or in any other ways, it is not hard to detect which paragraph (or block), i.e. which TextView object, is currently at the top of the screen.
Then restore and scroll down to that paragraph (or block). Bingo!
As i said, it is not-so-perfect. But at least the users can get back to that paragraph (or block) that he/she was reading. He/She just have to peek down a bit to find that particular line. (Or it might be even better to remind readers with a few previous lines, i.e. reading from the start of the paragraph.) i know it might be terribly bad if we have a long long long paragraph :-/
Well, we can actually "improve" this method. Make it down to word-level, a word a TextView. So it is logically "perfect". But, i guess, it is not a wise choice.
P.S. bathroom is always the best place for brainstorming (-: