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

mid*_*ite 15 android textview android-layout android-scrollview android-orientation

我想要的是,在设备更改方向时,屏幕中的顶线在纵向时仍然是横向屏幕上的顶行.反之亦然.

由于纵向和横向之间的屏幕宽度可能不同,因此文本的线宽(也就是宽度TextView和宽度ScrollView)会有所不同.因此,在不同的屏幕配置(纵向与横向,大与小)中,换行将有所不同.在不同的情况下,换行符将处于不同的位置.

有三种不太完美的解决方案供您参考.还解释了它们的缺点.


首先,最基本的方法:

(1)仅将y偏移存储在像素中

请查看:http://eliasbland.wordpress.com/2011/07/28/how-to-save-the-position-of-a-scrollview-when-the-orientation-changes-in-android/

为什么这不是那么完美:

在肖像中,线条被包裹.

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
Run Code Online (Sandbox Code Playgroud)

在横向中,线条不会被包裹.

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
Run Code Online (Sandbox Code Playgroud)

想象一下Line_2_Word_A在保存时在Portrait中读取(在屏幕顶部).当更改为横向时,它将显示Line_3_Word_A(在屏幕顶部).(因为顶部有两行偏移的像素.)


然后我想出一个方法,

(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()));
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

当且仅当每条线的长度相同时,这才能完美地工作.

为什么这不是那么完美:

在肖像中,线条被包裹.

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
Run Code Online (Sandbox Code Playgroud)

在横向中,线条不会被包裹.

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
Run Code Online (Sandbox Code Playgroud)

想象一下Line_2_Word_A在保存时在Portrait中读取(在屏幕顶部).当更改为横向时,它将显示Line_3_Word_A(在屏幕顶部).(因为它滚动了50%.)


然后我确实发现了这种方法

(3)存储第一条可见线

请看一下(第一个答案):如何在屏幕旋转后恢复textview滚动位置?

为什么这不是那么完美:

在肖像中,线条被包裹.

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
Run Code Online (Sandbox Code Playgroud)

在横向中,线条不会被包裹.

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
Run Code Online (Sandbox Code Playgroud)

想象一下Line_1_Word_E在保存时在Portrait中读取(在屏幕顶部).当更改为横向时,它将显示Line_3_Word_A(在屏幕顶部).(因为它是第三行.)

一个完美的,在风景中,在屏幕顶部显示Line_1_Word_A(以及Line_1_Word_E).


你能建议一个完美的方法吗?


编辑:

经过一番思考,方法(3)实际上与方法(1)相同吗?: - /


编辑2:

好吧,我刚刚提出了另一种不完美但更完美的方法,而不是上述三种方法:

(4)基于段落的存储

将段落(或文本块)分隔为不同的TextView对象.

然后通过类似方法(3)的代码,或者以任何其他方式,不难检测哪个段落(或块),即哪个TextView对象当前位于屏幕的顶部.

然后恢复并向下滚动到该段落(或块).答对了!

正如我所说,它不是那么完美.但至少用户可以回到他/她正在阅读的段落(或块).他/她只需要向下看一下就可以找到那条特定的线.(或者甚至可能更好的提醒读者前几行,即从段落的开头读取.)我知道如果我们有一个很长的长段落可能会非常糟糕: - /

好吧,我们实际上可以"改进"这种方法.简化为单词级别,单词为TextView.所以它在逻辑上是"完美的".但是,我想,这不是一个明智的选择.

PS浴室始终是头脑风暴的最佳场所( - :


仍然在寻找你​​的完美答案!

mid*_*ite 14

哇哇哇 - 嘿伙计们!我很自豪地说,我现在得到了一个完美的解决方案*****

嘘....(对不起,我太兴奋了.如果你发现任何错误/错误/弱点,请给我你宝贵的建议,请随时纠正我.:-)

切废话.干得好 !!!

@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);
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

而已.:-)

如果它对你有帮助,请拍手.< - 这很重要!!

如果您愿意,请单击那个小直立三角形.(确保你先拍了拍手!)

欢呼,
midnite

  • `ScrollView`(以及任何关于此问题的视图)必须具有id才能由Android持久保存.你不应该这样做.在滚动视图中添加id将强制android保存/恢复其状态,包括其滚动位置.请看:http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4.2_r1/android/widget/ScrollView.java#ScrollView.SavedState (5认同)
  • 我热爱你的热情!你为什么不用getScrollY?http://developer.android.com/reference/android/view/View.html#getScrollY() (2认同)