Đồng bộ hóa vị trí cuộn ScrollView - Android


95

Tôi có 2 ScrollView trong bố cục Android của mình. Làm cách nào để đồng bộ hóa vị trí cuộn của chúng?

Câu trả lời:


289

Có một phương pháp trong ScrollView ...

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

Thật không may, Google chưa bao giờ nghĩ rằng chúng tôi sẽ cần truy cập nó, đó là lý do tại sao họ bảo vệ nó và không thêm hook "setOnScrollChangedListener". Vì vậy, chúng tôi sẽ phải làm điều đó cho chính mình.

Đầu tiên chúng ta cần một giao diện.

package com.test;

public interface ScrollViewListener {

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

}

Sau đó, chúng ta cần ghi đè lớp ScrollView, để cung cấp móc 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);
        }
    }

}

Và chúng ta nên chỉ định lớp ObservableScrollView mới này trong bố cục, thay vì các thẻ ScrollView hiện có.

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

    ...

</com.test.ObservableScrollView>

Cuối cùng, chúng ta tập hợp tất cả lại với nhau trong lớp 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);
        }
    }

}

Mã scrollTo () sẽ xử lý bất kỳ điều kiện vòng lặp nào cho chúng ta, vì vậy chúng ta không cần phải lo lắng về điều đó. Cảnh báo duy nhất là giải pháp này không được đảm bảo hoạt động trong các phiên bản Android trong tương lai, vì chúng tôi đang ghi đè một phương pháp được bảo vệ.


Xin chào Andy Cảm ơn bạn đã dành thời gian đưa ra câu trả lời này - đã chuyển tôi về phía trước. Cảm ơn một lần nữa. Tim
Tim

-Andy Tôi cũng đang sử dụng mã nhưng nó được ném tôi rỗng trỏ Nguyện vọng bạn có thể giúp tôi
Hemant

@hemant Bạn có thể vui lòng chỉ ra NullPointer được ném vào dòng nào và bạn đang làm việc với phiên bản Android nào không?
sulai

Anh hùng trong ngày: Đã cứu mạng tôi! = D
Pedrão

Xin chào Andy, cảm ơn vì mã này. Nó hoạt động rất tốt. Tôi muốn biết một điều [nếu bạn hoặc any1 có ans. về điều này] - Khi tôi di chuyển chế độ xem cuộn, phương thức thay đổi trên không đăng ký tất cả các tọa độ X và Y trong khi chế độ xem cuộn di chuyển. Nó chỉ cho tôi vị trí bắt đầu và vị trí cuối cùng. Ví dụ, tôi bắt đầu ném từ Y = 10 và để ở Y = 30 và sau đó vận tốc ném đến Y = 50 rồi dừng lại. Vì vậy, onscrollchanged chỉ đăng ký - có lẽ là 10, 11, 12..30 và sau đó là 49, 50. Làm cách nào để tôi có thể đăng ký tất cả các vị trí trung gian và nhận tất cả các tọa độ từ 10 đến 50 ??
nhà giả kim

11

Một cải tiến đối với giải pháp của Andy: Trong mã của anh ấy, anh ấy sử dụng scrollTo, vấn đề là, nếu bạn lướt một lượt xem cuộn theo một hướng và sau đó lật một chế độ xem khác theo hướng khác, bạn sẽ nhận thấy rằng chế độ xem đầu tiên không dừng lần lướt trước đó của anh ta chuyển động.

Điều này là do thực tế là scrollView sử dụng computeScroll () để thực hiện các cử chỉ lướt và nó xung đột với scrollTo.

Để ngăn chặn điều này, chỉ cần lập trình onScrollChanged theo cách này:

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

với intercept Cuộn một boolean tĩnh được khởi tạo thành true. (điều này giúp tránh các vòng lặp vô hạn trên ScrollChanged)

onOverScrolled là chức năng duy nhất tôi tìm thấy có thể được sử dụng để ngăn scrollView nhấp nháy (nhưng có thể có những chức năng khác mà tôi đã bỏ qua!)

Để truy cập chức năng này (được bảo vệ), bạn phải thêm chức năng này vào ObservableScrollViewer của bạn

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

2
Thật là một cú đánh hay, con trai!
FranciscoBouza

5

Tại sao không chỉ thực hiện OnTouchListenertrong hoạt động của bạn. Sau đó ghi đè phương thức onTouch, rồi lấy vị trí cuộn của phương thức đầu tiên ScrollViewOne.getScrollY()và cập nhậtScrollViewTwo.scrollTo(0, ScrollViewOne.getScrollY());

Chỉ là một ý tưởng khác ... :)


7
Điều này có thể hoạt động nhưng khi bạn chạm và thả ra, chế độ xem cuộn có thể cuộn thêm một chút sau khi bạn buông ra mà không được chụp.
giàu có

Làm cách nào để tôi có thể tham chiếu / lấy tham chiếu của scrollView ScrollViewOne trong trường hợp này trong java?
Bunny Rabbit

Ngoài ra còn có những cách khác để cuộn (onTrackballEvent () ví dụ)
surfealokesea

Đó là một ý tưởng rất hay nhưng thay vì lấy và thiết lập vị trí cuộn y, sẽ dễ dàng hơn nếu chỉ gửi sự kiện chạm đến chế độ xem cuộn thứ hai. Điều này cũng sẽ giữ cho "fling" tiếp tục trong lần xem cuộn thứ hai. MotionEvent newEvent = MotionEvent.obtain (sự kiện); documentScrollView.dispatchTouchEvent (newEvent);
Eduard Mossinkoff

3

Trong gói Android support-v4, Android cung cấp một lớp mới có tên NestedScrollView.

chúng ta có thể thay thế <ScrollView>nút bằng <android.support.v4.widget.NestedScrollView>xml bố cục và triển khai nó NestedScrollView.OnScrollChangeListenertrong Java để xử lý việc cuộn.

Điều đó làm cho mọi thứ dễ dàng hơn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.