Bir ScrollView içindeki ViewPager doğru şekilde kaydırılmıyor


88

Üzerinde birkaç bileşen bulunan ve içeriği cihazın yüksekliğinden daha uzun olan bir "sayfam" var. Güzel, sadece tüm düzeni (tüm sayfayı) a içine koyun ScrollView, sorun değil.

Bileşenlerden biri a ViewPager. Bu doğru şekilde işliyor, ancak bir kaydırma / sallama tepkisi doğru performans göstermiyor, titriyor ve her zaman çalışmıyor. Görünüşe göre 'kafası karışıyor' ScrollView, bu nedenle yalnızca tam yatay bir çizgide fırlattığınızda% 100 çalışır.

ScrollViewÖğesini kaldırırsam, ViewPager mükemmel yanıt verir.

Etrafta bir arama yaptım ve bunu bilinen bir kusur olarak bulamadım. Bunu başka biri deneyimledi mi?

  • Platform Sürümü: 1.6
  • Uyumluluk Kitaplığı v4.0
  • Cihaz: HTC Incredible S

Aşağıda, test edebileceğiniz, ScrollViewdüzgün çalıştığını görmek için yorum yapabileceğiniz bazı örnek kodlar verilmiştir .

Aktivite:

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) {
    }
}

Yerleşim:

<?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>


Lütfen cevabımı bu bağlantıda gösterin: stackoverflow.com/questions/7381360/…
Geral Alexander Torres

Yanıtlar:


60

Ben de aynı sorunu yaşadım. Benim çözümüm aramaya oldu requestDisallowInterceptTouchEventViewPager başladı ilerleyin zaman.

İşte kodum:

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
bu bir çeşit çalışma. Hareket "çoğunlukla" dikey ise, görüntüleme çağrı cihazında dokunma başladığında bile kaydırma görünümünün dikey olarak gitmesini istiyorum
Nemanja Kovacevic

3
hem onTouch hem de onPageScrolled'de requestDisallowInterceptTouchEvent'e sahip olmak gerekli midir yoksa bu fazla mı? RequestDisallowInterceptTouchEvent'i yalnızca onPageScrollStateChanged'e taşıyarak aynı sonuçları elde edebilir miyiz?
craigrs84

1
Aynı problemim var ve bu en iyi çözümü buldum, umarım bu sizin için çalışır. bitbucket.org/NxAlex/swipes-navigation-demo/src/…
Jitendra Nath

onClick olayını hâlâ nasıl geçersiz kılabilirim? Touch on ile çakışıyor mu?
Kiddo

4
buradaki çağrı cihazı ve mpager nedir girdiyi vermem gerekiyor?
Sujithrao

30

Daha fazla okuma, kaydırma bileşenlerinin içinde kaydırma bileşenleriyle ilgili sorunlar olduğunu ortaya koymuştur.

Çözümlerden biri, benim durumumda bir ViewPager olmak üzere, içerilen kaydırılabilir bileşen alanında ScrollView'ın dikey kaydırmasını 'devre dışı bırakmaktır'.

Bu çözümün kodu burada ayrıntılı olarak verilmiştir (ve tek kelimeyle mükemmel!)


o sayfadaki hangi çözüme başvurmalıyım?
Harsha MV

1
requestDisallowInterceptTouchEvent aradığınız şeydir.
yahya

2
Aynı problemim var ve bu en iyi çözümü buldum, umarım bu sizin için çalışır. bitbucket.org/NxAlex/swipes-navigation-demo/src/…
Jitendra Nath

bağlantı için teşekkürler, orada bulduğum çözüm hala bana görüntüleyicide mükemmel yatay kaydırma yapmama sorunları veriyordu, bu yüzden çalışmasını sağlamak için biraz değiştirdim: stackoverflow.com/a/33696740/2093236
Dmide

15

İşte bir çözüm:

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

Temel olarak, aşağıya bastığınızda X, Y değerlerini ayarlamak ve hangi yöne gitmek istediğimizi belirlemek için sürüklerken mesafeyi hesaplamak. Durumunuza göre optimize etmek için sürükleme eşiğiyle oynayın.


Ben de aynı sorunu yaşadım, işe yarayan tek çözüm bu. İyi iş ... :)
rawcoder064

Bu benim için en iyi çözüm. ScrollView'ın yukarı ve aşağı kaymasını rahatsız etmez.
Lei Guo

Bu benim için en iyi çözüm. Ayrıca bu çözümde eşik değerini yapılandırma seçeneğimiz de var.
Vinayak

benim için çalışmıyor, çünkü
kaydırma görünümü

Harika iş, ve bunu Wrapcontentviewpager ile kullanmak istiyorsanız, o zaman da çok iyi çalışıyor
Silvans Solanki

4

Bir ViewPager ile, sayfa değişikliği olaylarını yakalayabilir ve ScrollView'ın sayfa değişikliğine neden olan dokunma olayına müdahale etmesini önleyebilirsiniz.

Bu kullanımı çok basit ViewGroup.requestDisallowInterceptTouchEvent(boolean). Ayrıca, kullanıcının ViewPager'da bir sürüklemeye başlasa bile ScrollView'ı sürüklemesini sağlar, ancak sayfalayıcı üzerinde yatay sürükleme ScrollView müdahalesi olmadan yine de çalışacaktır.

    @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) {
            }
        });

    }

Bu benim için Android 2.3.4 ve 4.2.1'deki Android Destek v4 kitaplığıyla çalışıyor.


1
onPageSelected yerine onPageScrollStateChanged olması mı gerekiyor?
craigrs84

2

Çözümü @Michael Herbig'den uyarladım Bunun avantajı setOnTouchListener, sadece bir ViewPager (örneğin ViewPagerIndicator) değil , izin veren herhangi bir görünümde çalışması ve kendi kendine yeten bir sınıf olması.

Örnek kullanım:

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

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

Ve sınıf:

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, MotionEvent olayı) {

    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;

}

İkinin üstünü kullanın ve çok güzel çalışacaktır. Kodumda da kullandım.


1

Bu nedenle, bu yaklaşımla , (ebeveynin) olayı çalması ve mevcut parşömeni iptal etmesi konusunda endişelenmenize gerek kalmadan yöne doğru ViewPagerkaydırmaya izin verdim . Daha da önemlisi, bu aynı zamanda konumun bulunduğu alanı yönünde kaydırılabilir bir şekilde bırakır .XScrollViewViewPagerY

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

ViewPager'ı burada alt sınıflara ayırmak biraz talihsiz bir durum. Bunun bir View.OnTouchListener'a uyarlanabileceğini hayal ediyorum. Şimdilik oy vereceğim!
Jon Willis

0

Buradaki çözümlerin hiçbiri benim için iyi sonuç vermedi çünkü bu ya ViewPagerdokunmatik mantığın ya da ScrollViewmantığının değiştirilmesi. İkisini de uygulamak zorundaydım, şimdi bir cazibe gibi çalışıyor.

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

Her sayfaya bir scrollView ekleyerek PagerAdapter sınıfınızdaki instantiateItem yöntemini geçersiz kılabilirsiniz. İşte bir kod:

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

Lütfen en önemli noktaları burada özetleyin veya alıntı yapın. Bağlantı çürümesi olur.
ЯegDwight
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.