ScrollView Внутри ScrollView - PullRequest
       20

ScrollView Внутри ScrollView

30 голосов
/ 20 декабря 2010

Я знаю, что люди из Google просили нас не помещать представление Scrollable в другое представление Scrollable, но есть ли какое-либо официальное заявление от них, указывающее нам не делать этого?

Ответы [ 13 ]

62 голосов
/ 19 июля 2012

Попробуйте это

Примечание: Здесь parentScrollView означает Outer ScrollView, а childScrollView означает Innner ScrollView

parentScrollView.setOnTouchListener(new View.OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
        Log.v(TAG, "PARENT TOUCH");

        findViewById(R.id.child_scroll).getParent()
                .requestDisallowInterceptTouchEvent(false);
        return false;
    }
});

childScrollView.setOnTouchListener(new View.OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
        Log.v(TAG, "CHILD TOUCH");

        // Disallow the touch request for parent scroll on touch of  child view
        v.getParent().requestDisallowInterceptTouchEvent(true);
        return false;
    }
});
16 голосов
/ 30 ноября 2012

Ответ Атул Бхардваджа выше является правильным способом сделать это. Но в случае, если кому-то нужно применить его к ScrollView, где у вас меньше контроля над родителем, я думаю, что это достаточно гибко и просто так, как он должен работать:

private void makeMyScrollSmart() {
    myScroll.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View __v, MotionEvent __event) {
            if (__event.getAction() == MotionEvent.ACTION_DOWN) {
                //  Disallow the touch request for parent scroll on touch of child view
                requestDisallowParentInterceptTouchEvent(__v, true);
            } else if (__event.getAction() == MotionEvent.ACTION_UP || __event.getAction() == MotionEvent.ACTION_CANCEL) {
                // Re-allows parent events
                requestDisallowParentInterceptTouchEvent(__v, false);
            }
            return false;
        }
    });
}

private void requestDisallowParentInterceptTouchEvent(View __v, Boolean __disallowIntercept) {
    while (__v.getParent() != null && __v.getParent() instanceof View) {
        if (__v.getParent() instanceof ScrollView) {
            __v.getParent().requestDisallowInterceptTouchEvent(__disallowIntercept);
        }
        __v = (View) __v.getParent();
    }
}

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

На мой взгляд, лучшее из обоих миров.

16 голосов
/ 20 декабря 2010

Это достаточно близко?

Никогда не следует использовать HorizontalScrollView с ListView, поскольку ListView заботится о собственной прокрутке.Что наиболее важно, выполнение этого снимает все важные оптимизации в ListView для работы с большими списками, поскольку фактически заставляет ListView отображать весь список элементов, чтобы заполнить бесконечный контейнер, предоставленный HorizontalScrollView.

http://developer.android.com/reference/android/widget/HorizontalScrollView.html

ОБНОВЛЕНИЕ:

Поскольку вы можете быть вынуждены использовать двухмерное представление прокрутки, вы можете использовать это: Интернет-архив блога.gorges.us/2010/06/android-two-dimensional-scrollview/

Я не использовал это, но это может быть разумным подходом.

7 голосов
/ 17 октября 2014
childScrollView.setOnTouchListener(new View.OnTouchListener() {

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

        int action = event.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
           // Disallow ScrollView to intercept touch events.
          v.getParent().requestDisallowInterceptTouchEvent(true);
           break;

        case MotionEvent.ACTION_UP:
            // Allow ScrollView to intercept touch events.
            v.getParent().requestDisallowInterceptTouchEvent(false);
            break;
        }

        return false;
    }
});

v.getParent () = родительский scrollView.

3 голосов
/ 02 июля 2015

Вот возможное решение. По достижении конца дочернего ScrollView он передает управление родительскому ScrollView для прокрутки. Он работает с ScrollView и ListView внутри ScrollView.

Шаг 1 - установить родительский OnTouchListener

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

Шаг 2 - установить детский OnTouchListener (ScrollView или ListView)

aChildScrollView.setOnTouchListener(new View.OnTouchListener() {
    public boolean onTouch(View v, MotionEvent event)
    {
        v.getParent().requestDisallowInterceptTouchEvent(shouldRequestDisallowIntercept((ViewGroup) v, event));
        return false;
    }
});
aListView.setOnTouchListener(new View.OnTouchListener() {
    public boolean onTouch(View v, MotionEvent event) {
        v.getParent().requestDisallowInterceptTouchEvent(shouldRequestDisallowIntercept((ViewGroup) v, event));
        return false;
    }
});

Шаг 3 - вот необходимые магические методы для правильной функциональности

protected boolean shouldRequestDisallowIntercept(ViewGroup scrollView, MotionEvent event) {
    boolean disallowIntercept = true;
    float yOffset = getYOffset(event);

    if (scrollView instanceof ListView) {
        ListView listView = (ListView) scrollView;
        if (yOffset < 0 && listView.getFirstVisiblePosition() == 0 && listView.getChildAt(0).getTop() >= 0) {
            disallowIntercept = false;
        }
        else if (yOffset > 0 && listView.getLastVisiblePosition() == listView.getAdapter().getCount() - 1 && listView.getChildAt(listView.getChildCount() - 1).getBottom() <= listView.getHeight()) {
            disallowIntercept = false;
        }
    }
    else {
        float scrollY = scrollView.getScrollY();
        disallowIntercept = !((scrollY == 0 && yOffset < 0) || (scrollView.getHeight() + scrollY == scrollView.getChildAt(0).getHeight() && yOffset >= 0));

    }

    return disallowIntercept;
}

protected float getYOffset(MotionEvent ev) {
    final int historySize = ev.getHistorySize();
    final int pointerCount = ev.getPointerCount();

    if (historySize > 0 && pointerCount > 0) {
        float lastYOffset = ev.getHistoricalY(pointerCount - 1, historySize - 1);
        float currentYOffset = ev.getY(pointerCount - 1);

        float dY = lastYOffset - currentYOffset;

        return dY;
    }

    return 0;
}
2 голосов
/ 16 июля 2015

Я нашел очень хорошее решение.Пожалуйста, используйте этот код.

    parentScrollView.setOnTouchListener(new View.OnTouchListener() {

        public boolean onTouch(View v, MotionEvent event) {

            Utils.showLog("PARENT TOUCH");
            findViewById(R.id.activity_mesh_child_scrollView).getParent().requestDisallowInterceptTouchEvent(false);
            return false;
        }
    });

    childScrollView.setOnTouchListener(new View.OnTouchListener() {

        public boolean onTouch(View v, MotionEvent event) {

            Utils.showLog("CHILD TOUCH");
            // Disallow the touch request for parent scroll on touch of child view
            v.getParent().requestDisallowInterceptTouchEvent(true);
            return false;
        }
    });

Это обязательно сработает.Пожалуйста, попробуйте и дайте мне знать, если не работает.

2 голосов
/ 20 декабря 2010

[...] есть ли официальное заявление от них, предписывающее нам не делать этого?

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

В конце концов, в любом случае, лучше рассмотреть один прокручиваемый вид для удобства пользователя. Это похоже на наличие полос прокрутки внутри полос прокрутки на HTML-странице; это может быть законно, но это ужасный пользовательский опыт.

1 голос
/ 06 ноября 2015

В библиотеке поддержки Android v4 имеется класс NestedScrollView.

Попробуйте вложенный просмотр прокрутки: http://ivankocijan.xyz/android-nestedscrollview/

1 голос
/ 01 мая 2015

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

Однако теперь у нас есть новый вид, который позволяет обоим представлениям прокручиваться одновременно, возможно, чтобы показатькрутой эффект:

https://developer.android.com/reference/android/support/v4/widget/NestedScrollView.html

Я не нашел ни одного примера для этого, так что то, что я написал, это лишь предположение о том, что он делает и для чего он используется.

0 голосов
/ 11 ноября 2016

Если кто-то ищет ответ на этот вопрос, у меня была немного другая реализация.Я расширил класс ScrollView и реализовал onTouchListener в дочернем элементе и установил его в self в конструкторе.

В обратном вызове onTouch, если объект события движения пришел со значением для счетчика указателя как 2, я вернул true, в противном случае false.Таким образом, если бы два пальца двигались по экрану, он рассматривал бы это как щепотку для увеличения, иначе рассматривал бы это как обычную прокрутку.Я не запрашивал отключение родительского касания и т. Д.

@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
    if(motionEvent.getPointerCount() == 2){
        mCallbacks.onPinchZoomAction(motionEvent);
        return true;
    }
    return false;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...