Android: ViewPager и HorizontalScrollVIew - PullRequest
30 голосов
/ 03 августа 2011

У меня есть HorizontalScrollView внутри моего ViewPager.Я установил requestDisallowInterceptTouchEvent(true); для HorizontalScrollView, но ViewPager все еще иногда перехватывает события касания.Есть ли другая команда, которую я могу использовать для предотвращения перехвата событий касания родителями и предками View?

примечание: HorizontalScrollView занимает только половину экрана.

Ответы [ 11 ]

35 голосов
/ 31 августа 2011

У меня была такая же проблема.Мое решение было:

  1. Создайте подкласс ViewPager и добавьте свойство с именем childId.
  2. Создайте установщик для свойства childId и задайте идентификатор HorizontalScrollView.
  3. Переопределить onInterceptTouchEvent() в подклассе ViewPager, и если свойство childId имеет значениеболее 0 получают этого потомка, а если событие находится в HorizontalScrollView области, возвращают false.

Код

public class CustomViewPager extends ViewPager {

    private int childId;    

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

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        if (childId > 0) {
            View scroll = findViewById(childId);
            if (scroll != null) {
                Rect rect = new Rect();
                scroll.getHitRect(rect);
                if (rect.contains((int) event.getX(), (int) event.getY())) {
                    return false;
                }
            }
        }
        return super.onInterceptTouchEvent(event);
    }

    public void setChildId(int id) {
        this.childId = id;
    }
}

В onCreate() метод

viewPager.setChildId(R.id.horizontalScrollViewId);
adapter = new ViewPagerAdapter(this);
viewPager.setAdapter(adapter);

Надеюсь, это поможет

8 голосов
/ 24 сентября 2011

Спасибо за ответ.Я немного изменил ваше решение и сумел заставить работать вложенные ViewPager:

public class CustomViewPager extends ViewPager {
    private int childId;

    public CustomViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
     @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        if (childId > 0) {          
            ViewPager pager = (ViewPager)findViewById(childId);

            if (pager != null) {           
                pager.requestDisallowInterceptTouchEvent(true);
            }

        }

        return super.onInterceptTouchEvent(event);
    }

    public void setChildId(int id) {
        this.childId = id;
    }
}
7 голосов
/ 29 июля 2012

Итак, я знаю, что этот вопрос довольно старый, но у меня есть решение, которое, я думаю, имеет некоторые дополнительные преимущества по сравнению с тем, которое было здесь опубликовано. Одна из ситуаций, которая может произойти, заключается в том, что внутри моего ViewPager (что я делаю) может быть более одного HorizontalScrollView, а затем мне потребуется предоставить тонны идентификаторов ViewPager, чтобы он всегда мог проверять наличие дочерних конфликтов касания.

Я проанализировал источник из ViewPager и обнаружил, что они используют этот метод, называемый canScroll, чтобы определить, можно ли прокрутить дочернее представление. К счастью, он не был статичным или окончательным, поэтому я мог просто переопределить его. Внутри этого метода был вызван «логический ViewCompat.canScrollHorizontally (View, int)» (тот, который всегда возвращает false для SDK <14). Мое решение тогда было: </p>

public class CustomViewPager extends ViewPager {

    public CustomViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public CustomViewPager(Context context) {
        super(context);
    }
    protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
        return super.canScroll(v, checkV, dx, x, y) || (checkV && customCanScroll(v));
    }
    protected boolean customCanScroll(View v) {
        if (v instanceof HorizontalScrollView) {
            View hsvChild = ((HorizontalScrollView) v).getChildAt(0);
            if (hsvChild.getWidth() > v.getWidth())
                return true;
        }
        return false;
   }
}

Итак, если у вас есть другой класс View внутри вашего ViewPager, который также должен получать горизонтальные перетаскивания, просто измените customCanScroll (View), чтобы вы могли вернуться, если касание не должно быть перехвачено или нет.

Логическое checkV истинно, если текущее представление также должно быть проверено на «прокручиваемость», поэтому мы должны также проверить это.

Надеюсь, что это поможет в будущем решить подобные проблемы (или, если есть лучшее решение или официальное решение на платформе Android, дайте мне знать).

6 голосов
/ 12 сентября 2013

Я не знаю, уже решили проблему или нет.Но ни один из ответов выше не работает должным образом.Поэтому я думаю, что это необходимо для тех людей, которые могут искать решения со страданием.

На самом деле, решение довольно простое, если вы прочитаете официальный документ об обработке сенсорных событий во ViewGroup.

Сначала вам нужно понять, как использовать

ViewParent.requestDisallowInterceptTouchEvent (boolean) . Вызывать это для родительского представления, чтобы указать, что он не должен перехватывать события касания с onInterceptTouchEvent (MotionEvent).

Затем вам нужно перехватить события и убедиться, что родительская ViewGroup вашего HorizontalScrollView не будет отправлять события дальше.

Итак, установите OnToushListener для вас HorizontalScrollView,Обнаружив события касания и перемещения, затем установите requestDisallowInterceptTouchEvent (true) на его родительский элемент (ViewPager или другое), чтобы убедиться, что события будут обрабатываться HorizontalScrollView.

Конечно, вы можете поместить приведенный ниже код в свои CustomHor HorizontalScrollView компоненты, чтобы его можно было повторно использовать.

Надеемся, что это поможет.; -)

horizontalScrollView.setOnTouchListener(new View.OnTouchListener() {
float rawX;
int mTouchSlop =  ViewConfiguration.get(getActivity()).getScaledTouchSlop();

@Override
public boolean onTouch(View v, MotionEvent event) {
    switch (event.getActionMasked()) {
        case MotionEvent.ACTION_DOWN:
            v.getParent().requestDisallowInterceptTouchEvent(true);
            rawX = event.getRawX();
            break;
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:
            v.getParent().requestDisallowInterceptTouchEvent(false);
            rawX = 0f;
            break;
        case MotionEvent.ACTION_MOVE:
            if (Math.abs(rawX - event.getRawX()) > mTouchSlop)
                v.getParent().requestDisallowInterceptTouchEvent(true);
            break;
    }
    return false;
}
5 голосов
/ 30 июля 2012

Вот еще одно решение, которое у меня сработало,

Создание пользовательского ViewPager

public class NoScrollViewPager extends ViewPager {

    private boolean scrollDisable = false;


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

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if(scrollDisable) {
            return false;
        } else {
            return super.onInterceptTouchEvent(ev);
        }
    }

    public void disableScroll() {
        scrollDisable = true;
    }

    public void enableScroll() {
        scrollDisable = false;
    }



}

А затем добавьте OnTouchListener к вашему элементу, будь то объект HorizontalScrollView или Gallery, или что-то еще, что вы не хотите, чтобы панель просмотра прокручивалась при касании.

gf.setOnTouchListener(new OnTouchListener() {

    @Override
    public boolean onTouch(View arg0,
            MotionEvent ev) {
        switch(ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            viewPager.disableScroll();
            break;
        case MotionEvent.ACTION_UP:
            viewPager.enableScroll();
            break;
        }
        return false;
    }

});
2 голосов
/ 08 июля 2013

Решение Феде не сработало для меня. У моего HorizontalScrollVIew были области, где он не улавливал какие-либо входные данные (щелчок, пролистывание ...). Я думаю, getHitRect () должен что-то делать с этим. После замены на следующий код все работало нормально.

Можно добавить несколько представлений HorizontalScrollVIew или любых других представлений, которые также требуют фокусировки.

public class CustomViewPager extends ViewPager {

    private ArrayList<Integer> children; 

    public CustomViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        children = new ArrayList<Integer>();
    }   

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        if (children.size() > 0) {
            for(int i=0; i<children.size(); i++){
                View scroll = findViewById(children.get(i));
                if (scroll != null & scroll.isPressed()) {
                    return false;
                }
            }
        }
        return super.onInterceptTouchEvent(event);
    }

    public void addChildId(int id) {
       children.add(id);
    }

}

В XML у вас будет что-то вроде этого:

<form.paginator.lib.CustomViewPager
      android:id="@+id/pager"
      android:layout_width="fill_parent"
      android:layout_height="150dp" />

В методе onCreate ():

mPager = (CustomViewPager) findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
mPager.addChildId(R.id.avatarImageView);

В методе OnTouchListener () для представления, которое вы добавили в дочерние элементы CustomViewPager:

case MotionEvent.ACTION_DOWN:
     v.setPressed(true);
     .
     .
     .
case MotionEvent.ACTION_UP:
     v.setPressed(false);
1 голос
/ 28 ноября 2012

Я обнаружил, что если у вас есть ViewPager в ScrollView, и вы хотите, чтобы он всегда оставался в фокусе, когда вы касаетесь его, а ScrollView никогда не фокусируется, это происходит.

1 голос
/ 24 января 2012

Это также будет работать для viewpager в пределах прокрутки: https://stackoverflow.com/a/2655740/969325

1 голос
/ 25 октября 2011

У меня были проблемы с небольшим представлением галереи в нижней части моей первой страницы в группе просмотра.При перелистывании фокус галереи будет переключаться между перемещением элементов галереи или окна просмотра.

Переопределение ViewPager, как указано выше, и установка requestDisallowInterceptTouchEvent решили мою проблему.Страницы больше не меняются при просмотре галереи.Считайте, что это важное решение для тех, кто использует галереи с viewpager.

0 голосов
/ 18 февраля 2015

Итак, я изменил ответ Феде, потому что он отключал касание в той же области на страницах вокруг того, что я хотел.Кроме того, я сделал возможным добавлять несколько представлений, потому что я имею дело с большим количеством графики и интерактивных представлений, которые используют интерактивность горизонтального перетаскивания.Таким образом, вы можете добавить

public class CustomViewPager extends ViewPager {

private List<ViewFocus> lateralTouchViews = new ArrayList<>();
public CustomViewPager(Context context, AttributeSet attrs) {
    super(context, attrs);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    for(int i = 0; i< lateralTouchViews.size(); i++){
        View scroll = findViewById(lateralTouchViews.get(i).id);
        if (scroll != null && getCurrentItem() == lateralTouchViews.get(i).itemPosition) {
            Rect rect = new Rect();
            scroll.getHitRect(rect);
            if (rect.contains((int) event.getX(), (int) event.getY())) {
                return false;
            }
        }
    }
    return super.onInterceptTouchEvent(event);
}

public void addConflict(int id, int pageIndex) {
    lateralTouchViews.add(new ViewFocus(id, pageIndex));
}
public class ViewFocus{
    int id;
    int itemPosition;

    public ViewFocus(int id, int itemPosition){
        this.id = id;
        this.itemPosition = itemPosition;
    }
}

}

...