ViewPager bên trong ScrollView không cuộn tương quan


88

Toi co 'trang phuc' co mot so phan phoi tren, nhung ai cung co the chieu dai hon so voi chieu cao. Tốt thôi, chỉ cần đặt tất cả bố cục (toàn bộ trang) bên trong a ScrollView, không vấn đề gì.

Một trong những thành phần là a ViewPager. Điều này hiển thị chính xác, nhưng phản ứng với thao tác vuốt / lướt không hoạt động chính xác, nó bị chập chờn và không phải lúc nào cũng hoạt động. Nó dường như bị 'nhầm lẫn' với ScrollView, vì vậy chỉ hoạt động 100% khi bạn ném theo một đường ngang chính xác.

Nếu tôi loại bỏ ScrollView, ViewPager phản hồi hoàn hảo.

Tôi đã tìm kiếm xung quanh và không tìm thấy đây là một khiếm khuyết đã biết. Đã có ai thử điều này chưa?

  • Phiên bản nền tảng: 1.6
  • Thư viện tương thích v4.
  • Thiết bị: HTC Incredible S

Dưới đây là một số mã ví dụ để bạn kiểm tra, nhận xét ScrollView để xem nó hoạt động chính xác.

Hoạt động:

package com.ss.activities;

import com.ss.R;

import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.widget.TextView;

public class PagerInsideScollViewActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        ViewPager vp = (ViewPager) findViewById(R.id.viewpager);
        vp.setAdapter(new MyPagerAdapter(this));
    }
}

class MyPagerAdapter extends PagerAdapter {

    private Context ctx;

    public MyPagerAdapter(Context context) {
        ctx = context;
    }

    @Override
    public int getCount() {
        return 2;
    }

    @Override
    public Object instantiateItem(View collection, int position) {

        TextView tv =  new TextView(ctx);
        tv.setTextSize(50);
        tv.setTextColor(Color.WHITE);
        tv.setText("SMILE DUDE, SMILE DUDE, SMILE DUDE, SMILE DUDE, SMILE DUDE, " +
                "SMILE DUDE, SMILE DUDE, SMILE DUDE, SMILE DUDE, SMILE DUDE, " +
                "SMILE DUDE, SMILE DUDE, SMILE DUDE, SMILE DUDE, SMILE DUDE, " +
                "SMILE DUDE, SMILE DUDE, SMILE DUDE");

        ((ViewPager) collection).addView(tv);

        return tv;

    }

    @Override
    public void destroyItem(View collection, int position, Object view) {
         ((ViewPager) collection).removeView((View) view);
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }

    @Override
    public Parcelable saveState() {
        return null;
    }

    @Override
    public void restoreState(Parcelable arg0, ClassLoader arg1) {
    }

    @Override
    public void startUpdate(View arg0) {
    }

    @Override
    public void finishUpdate(View arg0) {
    }
}

Bố trí:

<?xml version="1.0" encoding="utf-8"?>
    <ScrollView
         xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:orientation="vertical" >

            <android.support.v4.view.ViewPager
                android:id="@+id/viewpager"
                android:layout_width="fill_parent"
                android:layout_height="300dp" />

        </LinearLayout>

    </ScrollView>


Vui lòng hiển thị câu trả lời của tôi trong liên kết này: stackoverflow.com/questions/7381360/…
Geral Alexander Torres,

Câu trả lời:


60

Tôi đã từng gặp vấn đề tương tự. Giải pháp của tôi là gọi requestDisallowInterceptTouchEventkhi cuộn ViewPager bắt đầu.

Đây là mã của tôi:

pager.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        v.getParent().requestDisallowInterceptTouchEvent(true);
        return false;
    }
});

pager.setOnPageChangeListener(new SimpleOnPageChangeListener() {
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        pager.getParent().requestDisallowInterceptTouchEvent(true);
    }
});

1
đây là loại hoạt động. Tôi muốn cuộn xem đi theo chiều dọc ngay cả khi liên lạc được bắt đầu từ ngày xem pager nếu động thái này là "chủ yếu" thẳng đứng
Nemanja Kovacevic

3
có cần thiết phải có requestDisallowInterceptTouchEvent trong cả onTouch và onPageScrolled hay chỉ là quá mức cần thiết? Chúng tôi có thể nhận được kết quả tương tự bằng cách chuyển requestDisallowInterceptTouchEvent thành chỉ onPageScrollStateChanged không?
craigrs84

1
Tôi có cùng một vấn đề, và tìm thấy giải pháp tốt nhất này, hy vọng điều này làm việc cho bạn. bitbucket.org/NxAlex/swipes-navigation-demo/src/…
Jitendra Nath

làm thế nào tôi vẫn có thể ghi đè sự kiện onClick? nó xung đột với trên Touch?
Kiddo

4
máy nhắn tin và máy nhắn tin ở đây tôi cần cung cấp đầu vào là gì?
Sujithrao

30

Đọc thêm đã cho thấy rằng có vấn đề với các thành phần cuộn bên trong các thành phần cuộn.

Một giải pháp là 'vô hiệu hóa' tính năng cuộn theo chiều dọc của ScrollView trên khu vực của thành phần có thể cuộn được chứa, trong trường hợp của tôi là ViewPager.

Mã cho giải pháp này được nêu chi tiết ở đây (và nó đơn giản là tuyệt vời!)


tôi nên tham khảo giải pháp nào trong trang đó?
Harsha MV

1
requestDisallowInterceptTouchEvent là cái mà bạn tìm kiếm.
yahya

1
Tôi có cùng một vấn đề, và tìm thấy giải pháp tốt nhất này, hy vọng điều này làm việc cho bạn. bitbucket.org/NxAlex/swipes-navigation-demo/src/…
Jitendra Nath

nhờ các liên kết, giải pháp tôi tìm thấy ở đó vẫn còn cho tôi vấn đề với không swipes hoàn hảo ngang trong ViewPager, vì vậy tôi sửa đổi nó một chút để có được nó làm việc: stackoverflow.com/a/33696740/2093236
Dmide

15

Đây là một giải pháp:

    mPager.setOnTouchListener(new View.OnTouchListener() {

        int dragthreshold = 30;
        int downX;
        int downY;

        @Override
        public boolean onTouch(View v, MotionEvent event) {

            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = (int) event.getRawX();
                downY = (int) event.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                int distanceX = Math.abs((int) event.getRawX() - downX);
                int distanceY = Math.abs((int) event.getRawY() - downY);

                if (distanceY > distanceX && distanceY > dragthreshold) {
                    mPager.getParent().requestDisallowInterceptTouchEvent(false);
                    mScrollView.getParent().requestDisallowInterceptTouchEvent(true);
                } else if (distanceX > distanceY && distanceX > dragthreshold) {
                    mPager.getParent().requestDisallowInterceptTouchEvent(true);
                    mScrollView.getParent().requestDisallowInterceptTouchEvent(false);
                }
                break;
            case MotionEvent.ACTION_UP:
                mScrollView.getParent().requestDisallowInterceptTouchEvent(false);
                mPager.getParent().requestDisallowInterceptTouchEvent(false);
                break;
            }
            return false;
        }
    });

Về cơ bản thiết lập các giá trị X, Y khi bạn nhấn xuống và tính toán khoảng cách trong khi kéo để xác định con đường chúng ta muốn đi. Thử nghiệm với ngưỡng kéo để tối ưu hóa cho trường hợp của bạn.


Tôi đã gặp vấn đề tương tự, đây là giải pháp duy nhất hiệu quả. Làm tốt lắm ... :)
rawcoder064

Đây là giải pháp tốt nhất cho tôi. Nó sẽ không làm phiền ScrollView trượt lên và xuống.
Lei Guo

Đây là giải pháp tốt nhất cho tôi. Ngoài ra, chúng tôi có tùy chọn để định cấu hình giá trị ngưỡng trong giải pháp này.
Vinayak

không làm việc cho tôi, vì chặn scrollview các liên lạc trước khi tuyên bố hủy bỏ này được đạt tới
AdrianoCelentano

Lớn làm việc, và nếu bạn muốn sử dụng điều này với Wrapcontentviewpager sau đó cũng đang làm việc rất tốt
Silvans Solanki

4

Với ViewPager, bạn có thể nắm bắt các sự kiện thay đổi trang và ngăn ScrollView chặn sự kiện chạm gây ra thay đổi trang.

Điều này rất đơn giản, sử dụng ViewGroup.requestDisallowInterceptTouchEvent(boolean). Nó cũng cho phép người dùng kéo ScrollView ngay cả khi họ bắt đầu kéo trên ViewPager, nhưng kéo ngang trên máy nhắn tin sẽ vẫn hoạt động mà không bị ScrollView can thiệp.

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // Add android:id for your ScrollView in your layout
        final ScrollView sv = (ScrollView) findViewById(R.id.scrollview);
        final ViewPager vp = (ViewPager) findViewById(R.id.viewpager);
        vp.setAdapter(new MyPagerAdapter(this));

        // Use a page-change listener to respond to begin-drag events:
        vp.setOnPageChangeListener(new OnPageChangeListener() {
            @Override
            public void onPageSelected(int newState) {
                if (newState == ViewPager.SCROLL_STATE_DRAGGING) {
                    // Prevent the ScrollView from intercepting this event now that the page is changing.
                    // When this drag ends, the ScrollView will start accepting touch events again.
                    sv.requestDisallowInterceptTouchEvent(true);
                }
            }

            @Override
            public void onPageScrolled(int arg0, float arg1, int arg2) {
            }

            @Override
            public void onPageScrollStateChanged(int arg0) {
            }
        });

    }

Điều này phù hợp với tôi với thư viện Hỗ trợ Android v4 trên Android 2.3.4 và 4.2.1.


1
nó có phải là onPageScrollStateChanged thay vì onPageSelected không?
craigrs84

2

Tôi đã điều chỉnh giải pháp từ @Michael Herbig Ưu điểm của điều này là nó hoạt động trên mọi chế độ xem cho phép setOnTouchListener , không chỉ ViewPager (ví dụ: ViewPagerIndicator) và nó là một lớp độc lập.

Ví dụ sử dụng:

// runStatsPager is a android.support.v4.view.ViewPager;
runStatsPager.setOnTouchListener(new ViewInScrollViewTouchHelper(runStatsPager));

// runStatsPagerIndicator is a com.viewpagerindicator.TitlePageIndicator
runStatsPagerIndicator.setOnTouchListener(new ViewInScrollViewTouchHelper(runStatsPagerIndicator));

Và cả lớp:

class ViewInScrollViewTouchHelper implements View.OnTouchListener {

    private final ScrollView scrollView;
    private final View viewInScrollView;
    int dragthreshold = 30;
    int downX;
    int downY;

    public ViewInScrollViewTouchHelper(View viewInScrollView) {

        if (viewInScrollView == null) {
            throw new IllegalArgumentException("viewInScrollView cannot be null.");
        }

        ViewParent parent = viewInScrollView.getParent();
        ScrollView scrollView = null;
        do {
            if (parent instanceof ScrollView) {
                scrollView = (ScrollView) parent;
                break;
            }
        } while(parent != null && (parent = parent.getParent()) != null);

        if (scrollView == null) {
            throw new IllegalArgumentException("View does not have a ScrollView in its parent hierarchy.");
        }

        this.scrollView = scrollView;
        this.viewInScrollView = viewInScrollView;
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = (int) event.getRawX();
                downY = (int) event.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                int distanceX = Math.abs((int) event.getRawX() - downX);
                int distanceY = Math.abs((int) event.getRawY() - downY);

                if (distanceY > distanceX && distanceY > dragthreshold) {
                    viewInScrollView.getParent().requestDisallowInterceptTouchEvent(false);
                    scrollView.getParent().requestDisallowInterceptTouchEvent(true);
                } else if (distanceX > distanceY && distanceX > dragthreshold) {
                    viewInScrollView.getParent().requestDisallowInterceptTouchEvent(true);
                    scrollView.getParent().requestDisallowInterceptTouchEvent(false);
                }
                break;
            case MotionEvent.ACTION_UP:
                scrollView.getParent().requestDisallowInterceptTouchEvent(false);
                viewInScrollView.getParent().requestDisallowInterceptTouchEvent(false);
                break;
        }
        return false;
    }
}

1
public class WrapContentHeightViewPager extends ViewPager {

public WrapContentHeightViewPager(Context context) {
    super(context);
}

public WrapContentHeightViewPager(Context context, AttributeSet attrs) {
    super(context, attrs);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    int height = 0;
    for (int i = 0; i < getChildCount(); i++) {
        View child = getChildAt(i);
        child.measure(widthMeasureSpec, View.MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));

        int h = child.getMeasuredHeight();
        if (h > height) height = h;
    }

    heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

}

@Override public boolean onTouch (View v, sự kiện MotionEvent) {

    int dragthreshold = 30;
    int downX = 0;
    int downY = 0;

    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            downX = (int) event.getRawX();
            downY = (int) event.getRawY();
            break;
        case MotionEvent.ACTION_MOVE:
            int distanceX = Math.abs((int) event.getRawX() - downX);
            int distanceY = Math.abs((int) event.getRawY() - downY);

            if (distanceY > distanceX && distanceY > dragthreshold) {
                mViewPager.getParent().requestDisallowInterceptTouchEvent(false);
                mScrollView.getParent().requestDisallowInterceptTouchEvent(true);
            } else if (distanceX > distanceY && distanceX > dragthreshold) {
                mViewPager.getParent().requestDisallowInterceptTouchEvent(true);
                mScrollView.getParent().requestDisallowInterceptTouchEvent(false);
            }
            break;
        case MotionEvent.ACTION_UP:
            mScrollView.getParent().requestDisallowInterceptTouchEvent(false);
            mViewPager.getParent().requestDisallowInterceptTouchEvent(false);
            break;
    }
    return false;

}

Sử dụng ở trên hai và nó sẽ hoạt động rất tốt. Tôi cũng đã sử dụng mã của mình.


1

Vì vậy, với cách tiếp cận này, tôi để ViewPagercuộn Xtheo hướng mà không phải lo lắng về việc ScrollView(cha) đánh cắp sự kiện và hủy cuộn hiện tại. Quan trọng hơn, điều này cũng để lại khu vực có ViewPagervị trí có thể cuộn Ytheo hướng.

public class CustomViewPager extends ViewPager {

private GestureDetector     mGestureDetector;
View.OnTouchListener        mGestureListener;

public CustomViewPager(Context context, AttributeSet attrs) {
    super(context, attrs);
    mGestureDetector = new GestureDetector(context, new Detector());
}

@Override
public boolean onTouchEvent(MotionEvent motionEvent) {

    mGestureDetector.onTouchEvent(motionEvent);
    return super.onTouchEvent(motionEvent);
}

class Detector extends SimpleOnGestureListener {

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {

        requestDisallowInterceptTouchEvent(true);

        return false;
    }
}
}

Thật không may khi phân lớp ViewPager ở đây. Tôi tưởng tượng điều này có thể được điều chỉnh thành View.OnTouchListener. Tôi sẽ ủng hộ ngay bây giờ!
Jon Willis

0

Không có giải pháp nào ở đây hoạt động tốt đối với tôi bởi vì đó là sự sửa đổi của ViewPagerlogic cảm ứng hoặc logic ScrollViewcủa '. Tôi đã phải thực hiện cả hai, bây giờ nó hoạt động như một sự quyến rũ.

public class TouchGreedyViewPager extends ViewPager {
    private float xDistance, yDistance, lastX, lastY;

    public TouchGreedyViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                xDistance = yDistance = 0f;
                lastX = ev.getX();
                lastY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                final float curX = ev.getX();
                final float curY = ev.getY();
                xDistance += Math.abs(curX - lastX);
                yDistance += Math.abs(curY - lastY) / 3; // favor X events
                lastX = curX;
                lastY = curY;
                if (xDistance > yDistance) {
                    return true;
                }
        }

        return super.onInterceptTouchEvent(ev);
    }
}

public class TouchHumbleScrollView extends ScrollView {
    private float xDistance, yDistance, lastX, lastY;

    public TouchHumbleScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                xDistance = yDistance = 0f;
                lastX = ev.getX();
                lastY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                final float curX = ev.getX();
                final float curY = ev.getY();
                xDistance += Math.abs(curX - lastX);
                yDistance += Math.abs(curY - lastY) / 3; // favor X events
                lastX = curX;
                lastY = curY;
                if (xDistance > yDistance) {
                    return false;
                }
        }

        return super.onInterceptTouchEvent(ev);
    }
}

-1

Bạn có thể ghi đè phương thức InstantiateItem trong lớp PagerAdapter của mình bằng cách thêm một scrollView vào mỗi trang. Đây là một mã:

@Override
public Object instantiateItem(View collection, int position) {
    ScrollView sc = new ScrollView(cxt);
    sc.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
    sc.setFillViewport(true);
    TextView tv = new TextView(cxt);
    tv.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
    tv.setText(pages[position]);
    tv.setPadding(5,5,5,5);         
    sc.addView(tv);
    ((ViewPager) collection).addView(sc);

    return sc;
    }

Vui lòng tổng hợp hoặc trích dẫn những điểm quan trọng nhất ngay tại đây. Sự thối liên kết xảy ra.
ЯegDwight
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.