Лучший способ: сохранить и восстановить позицию TextView в ScrollView

Я хочу, чтобы при изменении ориентации устройства верхняя строка на экране, когда в Portrait остается верхней строкой на экране в Landscape. И наоборот.

Поскольку ширина экрана может быть разной между Portrait и Landscape, ширина строки текста, а это означает также ширину TextView и ScrollView, будет отличаться. Таким образом, перенос строк будет отличаться в разных конфигурациях экрана (Portrait and Landscape, large vs. small). Разрыв линии будет в другом положении в разных случаях.

Для вашей справки есть три не очень совершенных решения. Также объяснили их недостатки.


Во-первых, самый простой подход:

(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

В Пейзаже линии не завернуты.

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

Представьте, что вы просматриваете Line_2_Word_A (вверху экрана) в Portrait при сохранении. Когда он будет изменен на Landscape, он 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()));
        }
    });
}

Это отлично работает тогда и только тогда, когда длина каждой строки одинакова.

Почему это не так прекрасно:

В портрете строки завернуты.

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

В Пейзаже линии не завернуты.

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

Представьте, что вы просматриваете Line_2_Word_A (вверху экрана) в Portrait при сохранении. Когда он будет изменен на Landscape, он Line_3_Word_A (сверху экрана). (Потому что он прокручивается на 50%.)


Затем я нашел такой подход, который действительно

(3) Сохранение первой видимой строки

Пожалуйста, взгляните на (первый ответ): Как восстановить положение прокрутки текста после поворота экрана?

Почему это не так прекрасно:

В портрете строки завернуты.

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

В Пейзаже линии не завернуты.

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

Представьте, что вы просматриваете Line_1_Word_E (вверху экрана) в Portrait при сохранении. Когда он будет изменен на Landscape, он Line_3_Word_A (сверху экрана). (Потому что это третья строка.)

Идеально было бы в Пейзаже, показывая Line_1_Word_A (а также Line_1_Word_E) на экране.


Не могли бы вы предложить идеальный подход?


Редактировать:

После нескольких мыслей, действительно ли метод (3) идентичен методу (1)? : -/


Изменить 2:

Ну, я только что придумал еще один не очень совершенный, но еще более совершенный подход, чем предыдущие три:

(4) Сохранение на основе абзаца

Разделение абзацев (или блоков текстов) на разные объекты TextView.

Затем по кодам, подобным методу (3), или любым другим способом, нетрудно определить, какой абзац (или блок), т.е. Какой объект TextView, в настоящее время находится в верхней части экрана.

Затем восстановите и прокрутите список до этого абзаца (или блока). Бинго!

Как я уже сказал, это не так прекрасно. Но, по крайней мере, пользователи могут вернуться к тому абзацу (или блоку), который он читал. Он/она просто должен немного подглядеть, чтобы найти эту конкретную линию. (Или может быть даже лучше напомнить читателям несколько предыдущих строк, то есть прочитать с начала абзаца.) Я знаю, что это может быть ужасно плохо, если у нас длинный длинный абзац: -/

Ну, мы можем "улучшить" этот метод. Сделайте это до уровня слова, слово TextView. Поэтому он логически "совершенен". Но, я думаю, это не разумный выбор.

PS ванная всегда лучшее место для мозгового штурма (-:


Я все еще ищу твой идеальный ответ!

Ответ 1

WOW WOW WOW - эй, ребята! я так горжусь, что могу сказать, что у меня сейчас есть решение PERFECT * * * * *

Sh.... (извините, я тоже в восторге от этого. Если вы найдете какие-либо ошибки/ошибки/слабость на нем, пожалуйста, дайте мне свои ценные предложения и, пожалуйста, не стесняйтесь меня исправлять. :-)

Вырежьте дерьмо. Ну вот !!!

@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);
        }
    });
}

Это. :-)

Если это поможет вам, пожалуйста, хлопните в ладоши. <- это важно!

И если вы хотите, щелкните этот маленький вертикальный треугольник. (убедитесь, что вы сначала хлопнули в ладоши!)

веселит,
Midnite