Mam 2 ScrollViews w moim układzie Androida. Jak mogę zsynchronizować ich pozycje przewijania?
Odpowiedzi:
W ScrollView istnieje metoda ...
protected void onScrollChanged(int x, int y, int oldx, int oldy)
Niestety Google nigdy nie pomyślał, że będziemy musieli uzyskać do niego dostęp, dlatego zabezpieczyli go i nie dodali hooka „setOnScrollChangedListener”. Więc będziemy musieli to zrobić dla siebie.
Najpierw potrzebujemy interfejsu.
package com.test;
public interface ScrollViewListener {
void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy);
}
Następnie musimy zastąpić klasę ScrollView, aby zapewnić punkt zaczepienia 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);
}
}
}
I powinniśmy określić tę nową klasę ObservableScrollView w układzie zamiast istniejących tagów ScrollView.
<com.test.ObservableScrollView
android:id="@+id/scrollview1"
... >
...
</com.test.ObservableScrollView>
Na koniec zebraliśmy to wszystko razem w klasie 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);
}
}
}
Kod scrollTo () dba o wszelkie warunki pętli za nas, więc nie musimy się tym martwić. Jedynym zastrzeżeniem jest to, że nie ma gwarancji, że to rozwiązanie będzie działać w przyszłych wersjach Androida, ponieważ zastępujemy metodę chronioną.
Ulepszenie rozwiązania Andy'ego: w swoim kodzie używa scrollTo, problem polega na tym, że jeśli przerzucisz jeden widok scroll w jednym kierunku, a potem inny w innym, zauważysz, że pierwszy nie zatrzymuje swojego poprzedniego rzutu ruch.
Wynika to z faktu, że scrollView używa computeScroll () do wykonywania swoich flingujących gestów i wchodzi w konflikt z scrollTo.
Aby temu zapobiec, po prostu zaprogramuj onScrollChanged w ten sposób:
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 statyczną wartość logiczną zainicjowaną na true. (pomaga to uniknąć nieskończonych pętli w ScrollChanged)
onOverScrolled to jedyna funkcja, którą znalazłem, której można użyć do zatrzymania przewijania widoku scrollView (ale mogą być inne, które przegapiłem!)
Aby uzyskać dostęp do tej funkcji (która jest chroniona), musisz dodać ją do swojego ObservableScrollViewer
public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
}
Dlaczego nie po prostu wdraża OnTouchListener
w swojej działalności. Następnie zastąp metodę onTouch, a następnie pobierz pozycję przewijania pierwszej ScrollViewOne.getScrollY()
i zaktualizujScrollViewTwo.scrollTo(0, ScrollViewOne.getScrollY());
Kolejny pomysł ... :)
W pakiecie Android support-v4 system Android udostępnia nową klasę o nazwie NestedScrollView
.
możemy zamienić <ScrollView>
węzeł na <android.support.v4.widget.NestedScrollView>
w układzie XML i zaimplementować go NestedScrollView.OnScrollChangeListener
w Javie do obsługi przewijania.
To ułatwia sprawę.