Sincronizar posições de rolagem ScrollView - android


95

Eu tenho 2 ScrollViews em meu layout de Android. Como posso sincronizar suas posições de rolagem?

Respostas:


289

Existe um método em ScrollView ...

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

Infelizmente, o Google nunca pensou que precisaríamos acessá-lo, e é por isso que eles o protegeram e não adicionaram um gancho "setOnScrollChangedListener". Portanto, teremos que fazer isso por nós mesmos.

Primeiro, precisamos de uma interface.

package com.test;

public interface ScrollViewListener {

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

}

Em seguida, precisamos substituir a classe ScrollView, para fornecer o 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);
        }
    }

}

E devemos especificar esta nova classe ObservableScrollView no layout, em vez das tags ScrollView existentes.

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

    ...

</com.test.ObservableScrollView>

Finalmente, colocamos tudo junto na classe 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);
        }
    }

}

O código scrollTo () cuida de quaisquer condições de loop para nós, então não precisamos nos preocupar com isso. A única ressalva é que essa solução não tem garantia de funcionar em versões futuras do Android, porque estamos substituindo um método protegido.


Olá, Andy. Obrigado por dedicar seu tempo para colocar esta resposta - me motivou. Obrigado novamente. Tim
Tim de

-Andy Eu também estou usando o código, mas ele está me jogando a expecção de ponteiro nulo, você pode me ajudar
hemant

@hemant Você pode apontar em qual linha o NullPointer é lançado e com qual versão do Android você está trabalhando?
sulai

Herói do dia: salvou minha vida! = D
Pedrão

Oi Andy, obrigado pelo código. Funciona muito bem. Gostaria de saber uma coisa [se você ou any1 tem ans. para isso] - Quando eu lanço meu scrollView, o método onscrollchanged não registra todas as coordenadas X e Y enquanto o scrollview se move. Isso só me dá a posição inicial e a última posição. Digamos por ex- eu começo o arremesso de Y = 10 e saio em Y = 30 e então a velocidade do arremesso leva para Y = 50 e então para. Assim, o onscrollchanged apenas registradores - talvez 10, 11, 12..30 e então 49, 50. Como posso fazer isso registrar também todas as localizações intermediárias e obter todas as coordenadas de 10 a 50 ??
alquimista

11

Uma melhoria na solução de Andy: em seu código, ele usa scrollTo, o problema é que, se você lançar um scrollview em uma direção e, em seguida, outro em outra direção, você notará que o primeiro não interrompe o lançamento anterior movimento.

Isso se deve ao fato de que scrollView usa computeScroll () para fazer seus gestos de arremesso e entra em conflito com scrollTo.

Para evitar isso, basta programar onScrollChanged desta forma:

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

com interceptScroll, um booleano estático inicializado como verdadeiro. (isso ajuda a evitar loops infinitos em ScrollChanged)

onOverScrolled é a única função que descobri que poderia ser usada para impedir o lançamento de scrollView (mas pode haver outras que eu perdi!)

Para acessar esta função (que é protegida), você deve adicioná-la ao seu ObservableScrollViewer

public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
    super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
}

2
Que bela captura, filho!
FranciscoBouza

5

Porque não apenas implementa OnTouchListenerna sua atividade. Em seguida, substitua o método onTouch, obtenha a posição de rolagem do primeiro ScrollViewOne.getScrollY()e atualizeScrollViewTwo.scrollTo(0, ScrollViewOne.getScrollY());

Só mais uma ideia ... :)


7
Isso pode funcionar, mas quando você toca e solta, a visualização de rolagem pode rolar um pouco mais depois que você solta o que não é capturado.
richy de

Como posso consultar / obter a referência do scrollView ScrollViewOne neste caso em java?
Coelho Coelho

Existem também outras maneiras de rolar (onTrackballEvent () por exemplo)
surfealokesea

É uma ideia muito boa, mas em vez de obter e definir a posição de rolagem y, é mais fácil apenas despachar o evento de toque para a segunda visualização de rolagem. Isso também manterá o "lance" na segunda visualização de rolagem. MotionEvent newEvent = MotionEvent.obtain (evento); documentsScrollView.dispatchTouchEvent (newEvent);
Eduard Mossinkoff

3

No pacote Android support-v4, o Android fornece uma nova classe chamada NestedScrollView.

podemos substituir o <ScrollView>nó por <android.support.v4.widget.NestedScrollView>no layout xml e implementá-lo NestedScrollView.OnScrollChangeListenerem Java para lidar com a rolagem.

Isso torna as coisas mais fáceis.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.