Синхронізуйте ScrollView позиції прокрутки - android


95

У моєму андроїд-макеті є 2 ScrollViews. Як я можу синхронізувати їхні позиції прокрутки?

Відповіді:


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, оскільки ми замінюємо захищений метод.


Привіт, Енді. Дякую, що знайшов час, щоб викласти цю відповідь - перемістив мене вперед. Знову дякую. Tim
Tim

-Andy Я також використовую код, але він підкидає мені
очікування

@hemant Чи можете ви вказати, в який рядок кинуто NullPointer і з якою версією Android ви працюєте?
сулай

Герой дня: Врятував мені життя! = D
Педрао

Привіт, Енді, дякую за код. Він чудово працює. Я хотів би знати одне [якщо у вас або у кого-небудь з них є ans. до цього] - Коли я перекидаю scrollView, метод onscrollchanged не реєструє всіх координат X та Y під час переміщення прокрутки. Це дає мені лише початкову та останню позиції. Скажімо, наприклад, я починаю кидання з Y = 10 і залишаю при Y = 30, а потім швидкість кидання переносить його на Y = 50, а потім зупиняється. Тож onscrollchanged лише регістри - можливо, 10, 11, 12..30, а потім 49, 50. Як я можу зробити так, щоб він також реєстрував усі проміжні місця і отримував усі координати від 10 до 50 ??
алхімік

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

with interceptScroll статичний логічний код, ініціалізований до true. (це допомагає уникнути нескінченних циклів на ScrollChanged)

onOverScroll - це єдина функція, яку я знайшов, яка може бути використана для зупинки прокрутки 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
Це може спрацювати, але коли ви торкаєтесь і відпускаєте режим прокрутки, ви можете прокрутити ще трохи після того, як ви відпустите, що не захоплене.
Річі

Як я можу звернутися / отримати посилання на scrollView ScrollViewOne в цьому випадку в Java?
Bunny Rabbit

Існують також інші способи прокрутки (наприклад, onTrackballEvent ())
surfealokesea

Це дуже гарна ідея, але замість того, щоб отримувати та встановлювати положення прокрутки у, простіше просто відправити подію дотику до другого перегляду прокрутки. Це також дозволить продовжувати рух у другому прокручуваному перегляді. MotionEvent newEvent = MotionEvent.obtain (подія); documentsScrollView.dispatchTouchEvent (newEvent);
Едуард Моссінков

3

У пакеті підтримки Android-v4 Android надає новий клас із назвою NestedScrollView.

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

Це робить справи простішими.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.