Sincronizar posiciones de desplazamiento ScrollView - android

95

Tengo 2 ScrollViews en mi diseño de Android. ¿Cómo puedo sincronizar sus posiciones de desplazamiento?

Tim
fuente

Respuestas:

289

Hay un método en ScrollView ...

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

Desafortunadamente, Google nunca pensó que necesitaríamos acceder a él, por lo que lo protegieron y no agregaron un gancho "setOnScrollChangedListener". Así que tendremos que hacerlo por nosotros mismos.

Primero necesitamos una interfaz.

package com.test;

public interface ScrollViewListener {

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

}

Luego, necesitamos anular la clase ScrollView para proporcionar el gancho 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);
        }
    }

}

Y deberíamos especificar esta nueva clase ObservableScrollView en el diseño, en lugar de las etiquetas ScrollView existentes.

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

    ...

</com.test.ObservableScrollView>

Finalmente, lo juntamos todo en la clase 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);
        }
    }

}

El código scrollTo () se encarga de cualquier condición de bucle por nosotros, así que no tenemos que preocuparnos por eso. La única advertencia es que no se garantiza que esta solución funcione en versiones futuras de Android, porque estamos anulando un método protegido.

Andy
fuente
Hola, Andy. Gracias por tomarse el tiempo para poner esta respuesta. Me hizo avanzar. Gracias de nuevo. Tim
Tim
-Andy, también estoy usando el código, pero me arroja una expectativa de puntero nulo, ¿pueden ayudarme?
hemant
@hemant ¿Puede señalar en qué línea se lanza el NullPointer y con qué versión de Android está trabajando?
sulai
Héroe del día: ¡Me salvó la vida! = D
Pedrão
Hola Andy, gracias por el código. Funciona muy bien. Me gustaría saber una cosa [si tú o alguien tiene ans. a esto] - Cuando lanzo mi scrollView, el método onscrollchanged no registra todas las coordenadas X e Y mientras se mueve el scrollview. Solo me da la posición inicial y la última posición. Digamos, por ejemplo, que empiezo el lanzamiento desde Y = 10 y salgo en Y = 30 y luego la velocidad del lanzamiento lo lleva a Y = 50 y luego se detiene. Así que onscrollchanged solo registros, quizás 10, 11, 12..30 y luego 49, 50. ¿Cómo puedo hacer que registre todas las ubicaciones intermedias y obtenga todas las coordenadas de 10 a 50?
alquimista
11

Una mejora a la solución de Andy: en su código, usa scrollTo, el problema es que si arrojas una vista de desplazamiento en una dirección y luego arrojas otra en otra dirección, notarás que la primera no detiene su aventura anterior. movimiento.

Esto se debe al hecho de que scrollView usa computeScroll () para hacer sus gestos de lanzamiento, y entra en conflicto con scrollTo.

Para evitar esto, simplemente programe onScrollChanged de esta manera:

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

con interceptScroll un booleano estático inicializado en verdadero. (esto ayuda a evitar bucles infinitos en ScrollChanged)

onOverScrolled es la única función que encontré que podría usarse para detener el desplazamiento de scrollView (¡pero puede haber otras que me haya perdido!)

Para acceder a esta función (que está protegida), debe agregarla a su ObservableScrollViewer

public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
    super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
}
Taiko
fuente
2
¡Qué buena captura, hijo!
FranciscoBouza
5

¿Por qué no simplemente implementa OnTouchListeneren su actividad? Luego anule el método onTouch, luego obtenga la posición de desplazamiento del primero ScrollViewOne.getScrollY()y actualiceScrollViewTwo.scrollTo(0, ScrollViewOne.getScrollY());

Solo otra idea ... :)

Aaron Newton
fuente
7
Esto puede funcionar, pero cuando toca y suelta, la vista de desplazamiento puede desplazarse un poco más después de soltar lo que no se captura.
richy
¿Cómo puedo referir / obtener la referencia de scrollView ScrollViewOne en este caso en Java?
Bunny Rabbit
También hay otras formas de desplazarse (onTrackballEvent () por ejemplo)
surfealokesea
Es una muy buena idea, pero en lugar de obtener y establecer la posición de desplazamiento y, es más fácil enviar el evento táctil a la segunda vista de desplazamiento. Esto también mantendrá la "aventura" en la segunda vista de desplazamiento. MotionEvent newEvent = MotionEvent.obtain (evento); documentsScrollView.dispatchTouchEvent (newEvent);
Eduard Mossinkoff
3

En el paquete de Android support-v4, Android proporciona una nueva clase llamada NestedScrollView.

podemos reemplazar el <ScrollView>nodo con <android.support.v4.widget.NestedScrollView>en diseño xml e implementarlo NestedScrollView.OnScrollChangeListeneren Java para manejar el desplazamiento.

Eso facilita las cosas.

Wangzhangjian
fuente