Синхронизация позиций прокрутки ScrollView - android

95

У меня есть 2 ScrollViews в моем макете Android. Как я могу синхронизировать их положения прокрутки?

Тим
источник

Ответы:

289

В ScrollView есть метод ...

protected void onScrollChanged(int x, int y, int oldx, int oldy)

К сожалению, Google никогда не думал, что нам понадобится доступ к нему, поэтому они сделали его защищенным и не добавили ловушку "setOnScrollChangedListener". Так что нам придется сделать это для себя.

Для начала нам нужен интерфейс.

package com.test;

public interface ScrollViewListener {

    void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy);

}

Затем нам нужно переопределить класс ScrollView, чтобы предоставить перехватчик ScrollViewListener.

package com.test;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ScrollView;

public class ObservableScrollView extends ScrollView {

    private ScrollViewListener scrollViewListener = null;

    public ObservableScrollView(Context context) {
        super(context);
    }

    public ObservableScrollView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public ObservableScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setScrollViewListener(ScrollViewListener scrollViewListener) {
        this.scrollViewListener = scrollViewListener;
    }

    @Override
    protected void onScrollChanged(int x, int y, int oldx, int oldy) {
        super.onScrollChanged(x, y, oldx, oldy);
        if(scrollViewListener != null) {
            scrollViewListener.onScrollChanged(this, x, y, oldx, oldy);
        }
    }

}

И мы должны указать этот новый класс ObservableScrollView в макете вместо существующих тегов ScrollView.

<com.test.ObservableScrollView
    android:id="@+id/scrollview1"
    ... >

    ...

</com.test.ObservableScrollView>

Наконец, мы собрали все это вместе в классе Layout.

package com.test;

import android.app.Activity;
import android.os.Bundle;

public class Q3948934 extends Activity implements ScrollViewListener {

    private ObservableScrollView scrollView1 = null;
    private ObservableScrollView scrollView2 = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.q3948934);

        scrollView1 = (ObservableScrollView) findViewById(R.id.scrollview1);
        scrollView1.setScrollViewListener(this);
        scrollView2 = (ObservableScrollView) findViewById(R.id.scrollview2);
        scrollView2.setScrollViewListener(this);
    }

    public void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy) {
        if(scrollView == scrollView1) {
            scrollView2.scrollTo(x, y);
        } else if(scrollView == scrollView2) {
            scrollView1.scrollTo(x, y);
        }
    }

}

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

Энди
источник
Привет, Энди. Спасибо, что нашел время, чтобы ответить на этот вопрос - подтолкнул меня вперед. Еще раз спасибо. Тим
Тим
-Энди, я тоже использую код, но он бросает мне
запрос на
@hemant Не могли бы вы указать, в какой строке выбрасывается NullPointer и с какой версией Android вы работаете?
sulai
Юбиляр: Спас мне жизнь! = D
Pedrão
Привет, Энди, спасибо за код. Он отлично работает. Я хотел бы знать одну вещь [если у тебя или у кого-нибудь1 есть ans. к этому] - Когда я бросаю свой scrollView, метод onscrollchanged не регистрирует все координаты X и Y во время перемещения scrollview. Это дает мне только стартовую позицию и последнюю позицию. Скажем, например, я начинаю бросок с Y = 10 и выхожу на Y = 30, а затем скорость броска доводит его до Y = 50, а затем останавливается. Таким образом, onscroll изменил только регистры - возможно, 10, 11, 12..30, а затем 49, 50. Как я могу заставить его регистрировать все промежуточные местоположения и получать все координаты от 10 до 50 ??
alchemist
11

Усовершенствование решения Энди: в своем коде он использует scrollTo, проблема в том, что если вы бросаете одно изображение прокрутки в одном направлении, а затем другое в другом направлении, вы заметите, что первый не останавливает его предыдущий бросок. движение.

Это связано с тем, что scrollView использует computeScroll () для выполнения движущихся жестов и вступает в конфликт с scrollTo.

Чтобы предотвратить это, просто запрограммируйте onScrollChanged следующим образом:

    public void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy) {
    if(interceptScroll){
        interceptScroll=false;
        if(scrollView == scrollView1) {
            scrollView2.onOverScrolled(x,y,true,true);
        } else if(scrollView == scrollView2) {
            scrollView1.onOverScrolled(x,y,true,true);
        }
        interceptScroll=true;
    }
}

с interceptScroll статическое логическое значение, инициализированное значением true. (это помогает избежать бесконечных циклов в ScrollChanged)

onOverScrolled - единственная функция, которую я обнаружил, которую можно использовать, чтобы остановить прокручивание scrollView (но могут быть и другие, которые я пропустил!)

Чтобы получить доступ к этой функции (которая защищена), вы должны добавить ее в свой ObservableScrollViewer

public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
    super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
}
Тайко
источник
2
Какой хороший улов, сынок!
FranciscoBouza
5

Почему бы просто не внедрить OnTouchListenerв свою деятельность. Затем переопределите метод onTouch, затем получите позицию прокрутки первого ScrollViewOne.getScrollY()и обновитеScrollViewTwo.scrollTo(0, ScrollViewOne.getScrollY());

Еще одна идея ... :)

Аарон Ньютон
источник
7
Это может работать, но когда вы касаетесь и отпускаете, просмотр прокрутки может прокручиваться еще немного после того, как вы отпускаете, что не фиксируется.
Richy
Как я могу сослаться / получить ссылку на scrollView ScrollViewOne в этом случае в java?
Bunny Rabbit,
Есть и другие способы прокрутки (например, onTrackballEvent ())
surfealokesea
Это очень хорошая идея, но вместо получения и установки положения прокрутки y проще просто отправить событие касания во второй режим прокрутки. Это также будет поддерживать "бросок" во втором режиме прокрутки. MotionEvent newEvent = MotionEvent.obtain (событие); documentsScrollView.dispatchTouchEvent (newEvent);
Эдуард Моссинкофф
3

В пакете Android support-v4 Android предоставляет новый класс с именем NestedScrollView.

мы можем заменить <ScrollView>узел <android.support.v4.widget.NestedScrollView>на xml в макете и реализовать его NestedScrollView.OnScrollChangeListenerна Java для обработки прокрутки.

Это облегчает жизнь.

Wangzhangjian
источник