визуальная индикация прокрутки в android

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

Я попытался расширить класс ViewPager с помощью следующего кода, но тост не отображается (ev.getOrientation() всегда возвращает 0). Я тоже пробовал то же самое с историческими точками, но ev.getHistorySize() также возвращает 0.

Что мне не хватает?

Пример класса:

public class CustomViewPager extends ViewPager {

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

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

    /**
     * @see android.support.v4.view.ViewPager#onTouchEvent(android.view.MotionEvent)
     */
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        boolean result = super.onTouchEvent(ev);

        switch (ev.getAction() & MotionEventCompat.ACTION_MASK) {
            case MotionEvent.ACTION_MOVE:
                if (ev.getOrientation() > 0) {
                    Toast.makeText(getContext(), "left", 0).show();
                }
        }

        return result;
    }
}

person ekramus    schedule 27.05.2012    source источник


Ответы (8)


Если вы посмотрите на библиотеку поддержки v4, вы увидите, что ViewPager использует класс под названием EdgeEffectCompat (это обеспечивает эффект свечения, когда вы дойдете до начала или конца пейджера представления в ICS +). Если вы посмотрите на реализацию в библиотеке совместимости, вы увидите, что в ней есть оператор if, чтобы узнать, является ли версия сборки 14+ (ICS) или нет. Если это так, то в конечном итоге (если вы отслеживаете достаточно долго) используется обычный EdgeEffect. класс, который был введен в ICS. В противном случае он использует BaseEdgeEffectImpl, в котором в основном ничего нет.

Если хотите, вы можете создать свой собственный ViewPager, который использует ваш собственный EdgeEffect. Вы можете посмотреть исходный код Android, чтобы узнать, как они реализовали EdgeEffect здесь, который вы можете в значительной степени скопировать (просто не забудьте скопировать overscroll_edge и чертежи overscroll_glow в каталогах AOSP / res / drawable в ваш собственный проект, поскольку они являются внутренними для Android) или создайте свою собственную версию.

Удачи.

(Кстати, именно так они создают крутой эффект наклона кромки в меню запуска на ICS ... так что вы можете быть настолько креативными, насколько захотите;)

person wnafee    schedule 15.07.2012
comment
Похоже, он должен это сделать! Спасибо. - person Aaron Dufour; 16.07.2012

Я пытался добиться того же эффекта, что и в этом вопросе. Я борюсь с этим, а затем я прочитал ответ @wnafee (я не мог этого сделать без него).

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

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

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

Чтобы выполнить эту задачу, вам нужно просто следовать моему решению из трех частей. (Это займет от 3 до 10 минут)

Часть I.

Как сказал Wnafee, я только что создал свой собственный класс EdgeEffect, скопировав и вставив исходный код из здесь,

(просто не забудьте скопировать чертежи overscroll_edge и overscroll_glow в каталогах AOSP / res / drawable в свой собственный проект, поскольку они являются внутренними для Android)

Я сделал только 2 очень маленьких изменения:

  1. Я заявляю, что класс расширяет EdgeEffectCompat (я назвал свой класс EdgeEffectForEarlyVersions). public class EdgeEffectForEarlyVersions extends EdgeEffectCompat. Причина внесения этого изменения в том, что mLeftEdge и mRightEdge относятся к типу EdgeEffectCompat.
  2. В первой строке конструктора «моего» нового класса я добавил вызов родительского конструктора super(context);. Поскольку для EdgeEffectCompat нет конструктора по умолчанию, вы должны явно вызвать конструктор.

Часть II

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

Это функция:

private static void changeEdgeEffectCompactOnEarlyVersions(ViewPager viewPager, Context context)
{
    /* In case that the version is earlier than 14 there is only empty implementation for the edge effect, therefore we change it.
     * for more information look on the following links:
     * 1. http://stackoverflow.com/questions/10773565/visual-indication-of-over-scroll-in-android
     * 2. http://grepcode.com/file/repo1.maven.org/maven2/com.google.android/support-v4/r7/android/support/v4/view/ViewPager.java#ViewPager.0mLeftEdge
     * 3. http://grepcode.com/file/repo1.maven.org/maven2/com.google.android/support-v4/r7/android/support/v4/widget/EdgeEffectCompat.java#EdgeEffectCompat
     */

    if (Build.VERSION.SDK_INT < 14)
    {
        try
        {
            Class<ViewPager> viewPagerClass = ViewPager.class;
            //Get the left edge field, since it is private we used getDeclaredField and not getDeclared 
            Field leftEdge = viewPagerClass.getDeclaredField("mLeftEdge");
            leftEdge.setAccessible(true);
            //Get the right edge field, since it is private we used getDeclaredField and not getDeclared
            Field rightEdge = viewPagerClass.getDeclaredField("mRightEdge");
            rightEdge.setAccessible(true);

            EdgeEffectForEarlyVersions leftEdgeEffect = new EdgeEffectForEarlyVersions(context);
            EdgeEffectForEarlyVersions rightEdgeEffect = new EdgeEffectForEarlyVersions(context);

            //Set the mLeftEdge memeber of viewPager not to be the default one, but to be "our" edgeEffect  
            leftEdge.set(viewPager, leftEdgeEffect);
            //Set the mRightEdge memeber of viewPager not to be the default one, but to be "our" edgeEffect
            rightEdge.set(viewPager, rightEdgeEffect);              
        }
        catch (Exception ex)
        {
            Log.e("refelection", ex.getMessage());
        }

    }
}

Часть III

Теперь все, что осталось сделать, - это вызвать эту функцию после того, как у вас будет экземпляр ViewPager, и не более того.

Надеюсь, это кому-то поможет.

person nheimann1    schedule 11.08.2012
comment
Это не будет работать с API ‹= 8. (в последней версии 11) с использованием v4 compat lib. Потому что он использует BaseViewCompatImpl, который всегда возвращает OVER_SCROLL_NEVER при запросе режима перескроллинга. Затем в onDraw () он пропускает рисование прокрутки ... - person Nick; 24.12.2012

wnafee хорошо объяснил решение, но для ленивых из нас довольно давно я сделал реальную рабочую реализацию.

https://github.com/inovex/ViewPager3D

И если вы просто хотите перескролить, посмотрите здесь:

https://github.com/inovex/ViewPager3D/issues/1

person Renard    schedule 11.12.2012

У вас есть много вариантов, вы можете показать тост, отобразить диалог, сделать TextView или изображение, чтобы они отображались поверх пользовательского интерфейса и т. Д. Или, поскольку вы знаете количество элементов View в ViewPager, вы можете добавить разные View в позиции 0 и / или n + 1 с сообщением и заставить его перейти к последнему представлению, которое действительно содержит ваши данные.

Вы можете реализовать:

viewPager.setOnPageChangeListener(new OnPageChangeListener() {
    public void onPageSelected(int position) {
        //TODO If position is the 0 or n item, add a view at 0 or at n+1 to indicate there is no more pages with data.
    }

    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        // TODO Show a Toast, View or do anything you want when position = your first/last  item;
    }

    public void onPageScrollStateChanged(int state) {
    }
});
person Oscar Salguero    schedule 11.07.2012
comment
При попытке прокрутки за конец ViewPager появляется стандартный для системы эффект свечения, который аналогичен прокрутке вверху или внизу списка. Я бы хотел что-то подобное - но он должен работать на всех версиях (текущее свечение - только для ICS). Ваши предложения сделают неуклюжий пользовательский интерфейс, и если это действительно единственные варианты, я буду придерживаться никаких указаний. - person Aaron Dufour; 12.07.2012
comment
Я знаю, и этого достаточно для моего приложения, но если @ekramus спросил, я полагаю, это потому, что ему нужно что-то большее. :) - person Oscar Salguero; 12.07.2012

просто чтобы дополнить ответ разработчика @goBeepit, когда вы создаете свой собственный класс edgeffect и расширяете EdgeEffectCompat, некоторые методы должны быть логическими. вы можете изменить эти методы на логический тип и в любом случае вернуть значение true, таким образом все работает нормально

person BigBen3216    schedule 04.09.2012

Вы можете перегрузить функцию setUserVisibleHint (boolean) в своих фрагментах. Псевдокод:

    void setUserVisibleHint(boolean isVisibleToUser) {
        // If this fragment is becoming visible
        if (isVisibleToUser == true) {
             // Check if it is the last fragment in the viewpager
             if (indexOfThis == getActivity().indexOfLast) {
                // Display right limit reached
                Toast(..., "No more Frags to right",...)
                }
             // Check if it is the first fragment in the viewpager
             else if (indexOfThis == getActivity().indexOfFirst) {
                // Display Left Limit reached
                Toast(..., "No more Frags to left",...)
             }
        }
    }

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

person Aviral    schedule 13.07.2012
comment
Это имеет те же последствия для пользовательского интерфейса, что и ответ Оскара, за исключением того, что он переопределяет метод, который не предназначен для переопределения, вместо использования прослушивателя, что кажется опасным. - person Aaron Dufour; 13.07.2012

Я реализовал эффект возврата на основе ViewPager3D Ренарда: https://stackoverflow.com/a/17425468/973379

person Piotr Zawadzki    schedule 02.07.2013

Обычно с ViewPager используется PagerAdapter, такой как FragmentPagerAdapter или FragmentStatePagerAdapter, чтобы заполнить ViewPager содержимым (ваш контент будет представлениями).

Теперь, когда вы используете PagerAdapter, у вас есть один метод с именем getCount(), http://developer.android.com/reference/android/support/v4/view/PagerAdapter.html#getCount%28%29, что даст вам размер содержания.

Поскольку теперь вы знаете размер, вы можете легко отобразить сообщение с оператором управления if.

Попробуйте этот код: http://developer.android.com/reference/android/support/v4/view/ViewPager.html

Примечание: я не думаю, что вам нужен собственный ViewPager. Вам также необходимо понимать фрагменты для ViewPager. Посмотрите образцы в ApiDemos. Это отличный источник.

person Harshal Kshatriya    schedule 15.06.2012
comment
Он не об этом спросил. Он хотел знать конкретно, как сделать эффект перескроллинга, такой как резинка на iOS, которой не было во ViewPager, по крайней мере, до ICS. - person Fabian Zeindl; 30.09.2012