Sinkronisasi posisi scroll ScrollView - android

95

Saya memiliki 2 ScrollView di layout android saya. Bagaimana cara menyinkronkan posisi gulir mereka?

Tim
sumber

Jawaban:

289

Ada metode di ScrollView ...

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

Sayangnya Google tidak pernah berpikir bahwa kami perlu mengaksesnya, itulah sebabnya mereka membuatnya dilindungi dan tidak menambahkan pengait "setOnScrollChangedListener". Jadi kita harus melakukannya sendiri.

Pertama kita membutuhkan antarmuka.

package com.test;

public interface ScrollViewListener {

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

}

Kemudian kita perlu mengganti kelas ScrollView, untuk menyediakan pengait 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);
        }
    }

}

Dan kita harus menentukan kelas ObservableScrollView baru ini di layout, bukan di tag ScrollView yang ada.

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

    ...

</com.test.ObservableScrollView>

Terakhir, kami menggabungkan semuanya di kelas 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);
        }
    }

}

Kode scrollTo () menangani kondisi loop apa pun untuk kita, jadi kita tidak perlu khawatir tentang itu. Satu-satunya peringatan adalah bahwa solusi ini tidak dijamin akan berfungsi di versi Android mendatang, karena kami mengganti metode yang dilindungi.

Andy
sumber
Hai Andy Terima kasih telah meluangkan waktu untuk mengajukan jawaban ini - memajukan saya. Terima kasih lagi. Tim
Tim
-Andy Saya juga menggunakan kode tetapi itu membuang saya penunjuk nol harapan dapatkah Anda membantu saya
hemant
@hemant Bisakah Anda menunjukkan di baris mana NullPointer dilemparkan dan versi Android apa yang Anda gunakan?
sulai
Pahlawan hari ini: Menyelamatkan hidupku! = D
Pedrão
Hai Andy, terima kasih untuk kodenya. Ini berfungsi dengan baik. Saya ingin tahu satu hal [jika Anda atau siapa pun memiliki jawaban. ke ini] - Saat saya melemparkan scrollView saya, metode onscrollchanged tidak mendaftarkan semua koordinat X dan Y saat scrollview bergerak. Itu hanya memberi saya posisi awal dan posisi terakhir. Katakanlah untuk ex- Saya memulai lemparan dari Y = 10 dan pergi pada Y = 30 dan kemudian kecepatan lempar membawanya ke Y = 50 dan kemudian berhenti. Jadi onscrollchanged hanya register-mungkin 10, 11, 12..30 dan kemudian 49, 50. Bagaimana cara membuatnya mendaftarkan semua lokasi perantara juga dan mendapatkan semua koordinat dari 10 hingga 50 ??
alkemis
11

Perbaikan pada solusi Andy: Dalam kodenya, dia menggunakan scrollTo, masalahnya adalah, jika Anda melemparkan satu tampilan gulir ke satu arah dan kemudian melemparkan yang lain ke arah lain, Anda akan melihat bahwa yang pertama tidak menghentikan lemparan sebelumnya. gerakan.

Hal ini disebabkan oleh fakta bahwa scrollView menggunakan computeScroll () untuk melakukan gerakan melempar, dan bentrok dengan scrollTo.

Untuk mencegahnya, cukup program onScrollChanged dengan cara ini:

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

dengan interceptScroll boolean statis yang diinisialisasi ke true. (ini membantu menghindari loop tak terbatas di ScrollChanged)

onOverScrolled adalah satu-satunya fungsi yang saya temukan yang dapat digunakan untuk menghentikan scrollView agar tidak terlempar (tetapi mungkin ada fungsi lain yang saya lewatkan!)

Untuk mengakses fungsi ini (yang dilindungi) Anda harus menambahkan ini ke ObservableScrollViewer Anda

public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
    super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
}
Taiko
sumber
2
Hasil tangkapan yang bagus, Nak!
FranciscoBouza
5

Mengapa tidak menerapkan OnTouchListenerdalam aktivitas Anda. Kemudian timpa metode onTouch, lalu dapatkan posisi scroll pertama ScrollViewOne.getScrollY()dan perbaruiScrollViewTwo.scrollTo(0, ScrollViewOne.getScrollY());

Hanya ide lain ... :)

Aaron Newton
sumber
7
Ini dapat bekerja tetapi ketika Anda menyentuh dan melepaskan tampilan gulir dapat menggulir sedikit lagi setelah Anda melepaskan yang tidak ditangkap.
richy
Bagaimana saya bisa merujuk / mendapatkan referensi scrollView ScrollViewOne dalam kasus ini di java?
Kelinci Kelinci
Ada juga cara lain untuk menggulir (misalnya onTrackballEvent ())
surfealokesea
Ini adalah ide yang sangat bagus tetapi alih-alih mendapatkan dan mengatur posisi gulir y, lebih mudah untuk hanya mengirimkan acara sentuh ke tampilan gulir kedua. Ini juga akan membuat "lemparan" tetap berjalan di tampilan gulir kedua. MotionEvent newEvent = MotionEvent.obtain (acara); documentsScrollView.dispatchTouchEvent (newEvent);
Eduard Mossinkoff
3

Dalam paket Android support-v4, Android menyediakan kelas baru bernama NestedScrollView.

kita bisa mengganti <ScrollView>node dengan <android.support.v4.widget.NestedScrollView>dalam layout xml, dan mengimplementasikannya NestedScrollView.OnScrollChangeListenerdi Java untuk menangani pengguliran.

Itu membuat segalanya lebih mudah.

wangzhangjian
sumber