简体   繁体   English

最佳方式:在 ScrollView 中保存和恢复 TextView 位置

[英]Best Way: Save & Restore TextView Position in ScrollView

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.由于纵向和横向的屏幕宽度可能不同,因此文本的线宽,即TextViewScrollView的宽度也会有所不同。 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 (1) 仅以像素为单位存储 y 偏移

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/请看一看: 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.想象一下在保存时在纵向中阅读Line_2_Word_A (在屏幕顶部)。 When changed to Landscape, it will be showing Line_3_Word_A (at screen top).当更改为横向时,它将显示Line_3_Word_A (在屏幕顶部)。 (Because of two-lines-offset-in-pixel from top.) (因为从顶部开始有两行像素偏移。)


Then i come up with an approach,然后我想出了一个方法,

(2) By saving the scroll-percentage (2) 通过保存滚动百分比

@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.想象一下在保存时在纵向中阅读Line_2_Word_A (在屏幕顶部)。 When changed to Landscape, it will be showing Line_3_Word_A (at screen top).当更改为横向时,它将显示Line_3_Word_A (在屏幕顶部)。 (Because it is scrolled by 50%.) (因为它滚动了 50%。)


Then i found this approach which indeed然后我发现这种方法确实

(3) Storing the first visible line (3) 存储第一条可见行

Please take a look at (first answer): How to restore textview scrolling position after screen rotation?请看(第一个答案): 屏幕旋转后如何恢复textview滚动位置?

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.想象一下在保存时在纵向中阅读Line_1_Word_E (在屏幕顶部)。 When changed to Landscape, it will be showing Line_3_Word_A (at screen top).当更改为横向时,它将显示Line_3_Word_A (在屏幕顶部)。 (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.一个完美的方法是,在 Landscape 中,在屏幕顶部显示Line_1_Word_A (以及Line_1_Word_E )。


Could you please suggest a perfect approach?你能提出一个完美的方法吗?


Edit:编辑:

After a few thoughts, is method (3) identical to method (1) in fact?想了想,方法(3)其实和方法(1)是一样的吗? :-/ :-/


Edit 2:编辑2:

Well, i have just come up with another not-so-perfect-yet-more-perfect approach than the above three:好吧,我刚刚提出了另一种比上述三种不那么完美但更完美的方法:

(4) Paragraph-based storing (4) 基于段落的存储

Separating paragraphs (or blocks of texts) into different TextView objects.将段落(或文本块)分成不同的 TextView 对象。

Then by the codes like method (3), or in any other ways, it is not hard to detect which paragraph (or block), ie which TextView object, is currently at the top of the screen.然后通过类似方法(3)的代码,或者其他任何方式,都不难检测出当前屏幕顶部是哪个段落(或块),即哪个TextView对象。

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, ie 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.把它降到单词级别,一个单词一个 TextView。 So it is logically "perfect".所以它在逻辑上是“完美的”。 But, i guess, it is not a wise choice.但是,我想,这不是一个明智的选择。

PS bathroom is always the best place for brainstorming (-: PS浴室永远是头脑风暴的最佳场所(-:


i am still looking for your perfect answer!!还在寻找你的完美答案!!

I am so proud to say, I got a perfect solution to this now.我很自豪地说,我现在得到了一个完美的解决方案。

Sh.... (sorry I am too excited about it. If you find any mistakes/bugs/weakness on it, please DO give me your valuable suggestions and please feel free to correct me. :-) Sh....(对不起,我太兴奋了。如果你发现任何错误/错误/弱点,请给我你的宝贵建议,请随时纠正我。:-)

Cut the crap.废话少说。 Here you go !!!干得好 !!!

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    final ScrollView scrollView = (ScrollView) findViewById(R.id.Trial_C_ScrollViewContainer);
    final TextView textView = (TextView) scrollView.getChildAt(0);
    final int firstVisableLineOffset = textView.getLayout().getLineForVertical(scrollView.getScrollY());
    final int firstVisableCharacterOffset = textView.getLayout().getLineStart(firstVisableLineOffset);
    outState.putInt(ScrollViewContainerTextViewFirstVisibleCharacterOffset, firstVisableCharacterOffset);
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    final int firstVisableCharacterOffset = savedInstanceState.getInt(ScrollViewContainerTextViewFirstVisibleCharacterOffset);

    final ScrollView scrollView = (ScrollView) findViewById(R.id.Trial_C_ScrollViewContainer);
    scrollView.post(new Runnable() {
        public void run() {
            final TextView textView = (TextView) scrollView.getChildAt(0);
            final int firstVisableLineOffset = textView.getLayout().getLineForOffset(firstVisableCharacterOffset);
            final int pixelOffset = textView.getLayout().getLineTop(firstVisableLineOffset);
            scrollView.scrollTo(0, pixelOffset);
        }
    });
}

That's it.而已。 :-) :-)

If it helps you, please clap your hands.如果对你有帮助,请拍拍手。 <-- this is important!! <--这很重要!!

And if you wish to, click that little upright triangle.如果您愿意,请单击那个小的直立三角形。 (make sure you have clapped your hands first!) (确保你先拍手!)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM