Как имитировать приложение телефона Lollipop, с изменением размера верхних элементов перед прокручиваемым контентом?

Фон

В приложении Lollipop Phone, когда вы переходите на вкладку "Отреки" или "контакты", и вы пытаетесь прокручивать, первое, что происходит, это изменение размера верхней области, состоящей из недавнего события и окна поиска, как такие как:

enter image description here

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

То же самое касается прокрутки: пока верхняя область не полностью увеличена, RecyclerView не будет прокручиваться вверх.

Проблема

Я не могу найти лучший способ реализовать эту функцию.

В частности, у меня проблема с изменением размера верхней области, так что она будет такой же гладкой, как и на Lollipop.

Чтобы упростить, мне нужно изменить размер одной части экрана, а не 2, как показано в приложении для телефона ( "последнее событие" и поисковый запрос).

Что я пробовал

Есть несколько вещей, которые я пытался сделать:

  • сделать RecyclerView таким же большим, как и весь экран, имея поддельный, большой пустой заголовок. когда я прокручиваю, я также устанавливаю "translationY" в реальный контент сверху. У этого есть проблема, показывающая полосу прокрутки как часть верхней области (и я хочу показать полосу прокрутки в нужном месте). Кроме того, это делает его довольно сложным, так как вам приходится обрабатывать другие страницы, которые также прокручивают значения RecyclerViews. Это решение похоже на некоторые сторонние библиотеки, такие как этот (но здесь я использовал а не ImageView, но код почти идентичен).

  • RecyclerView находится ниже верхней области, а прокрутка изменяет layoutParams верхней области, аналогично тому, что я сделал на этом сообщении.

  • то же самое, что и 2, но вместо изменения LayoutParams я изменяю масштаб Y видов.

Для обоих # 2 и # 3 проблема заключается в том, что в некоторых случаях, как только я получаю событие прокрутки + Y в некотором значении, я также получаю прокрутку даже -Y одного и того же значения (и наоборот), делая обработку эффекта изменения размера "прыгающим" (вверх и вниз, даже если пользователь прокручивал красиво).

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

Я попытался использовать setOnTouchListener (даже вместе с GestureDetectorCompat) и setOnScrollListener. Все вызвали ту же странную проблему.

Вопрос

Как решить эту проблему? Почему это происходит, и почему это не всегда происходит?


EDIT: Я попытался изменить # 2, так что вместо обработки события касания RecyclerView я обработаю события касания его контейнера (некоторые LinearLayout). По какой-то причине контейнер не реагировал на события касания, поэтому я заменил его RelativeLayout и поместил представление поверх всего внутри этого макета, который будет реагировать на события касания (и соответственно изменить верхнюю область). Это сработало, но потом я не смог передать события дальше к дочерним представлениям, когда это необходимо.

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

Ответ 1

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

Контейнер вкладок viewPager & охватывает всю область и изменяет отступы.

это макет:   

        <LinearLayout
            android:id="@+id/header"
            android:layout_width="match_parent"
            android:orientation="vertical"
            android:layout_height="wrap_content">
            <!-- put upper content here -->

        </LinearLayout>

        <LinearLayout
            android:id="@+id/pagerAndTabsContainer"
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <com.astuetz.PagerSlidingTabStrip
                android:id="@+id/viewPagerSlidingTabsStrip"
                android:layout_width="match_parent"
                android:background="@color/default_background"
                android:layout_height="48dip"/>

            <android.support.v4.view.ViewPager
                android:background="@color/default_background"
                android:id="@+id/viewPager"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>
        </LinearLayout>


    </com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout>

Затем вам нужно иметь некоторый код для инициализации (когда выполняется этап макета, и вы можете получить реальные размеры просмотров):

// the upper area height, that can be shrunk to a size of 0 height when needed
mResizeableViewHeight = mHeaderView.getHeight(); 
// set initial padding if you wish to show the upper area by default
mPagerAndTabsContainer.setPadding(0, mResizeableViewHeight, 0, 0); 
prepareViewPager(mViewPager); // set the pages and fragments...
// init the "TouchInterceptionFrameLayout" to handle touch events
mContentView.setScrollInterceptionListener(mInterceptionListener);

в функции "shouldInterceptTouchEvent" от TouchInterceptionFrameLayout.TouchInterceptionListener, вы решаете, когда он должен блокировать касания, а на "onMoveMotionEvent" этого интерфейса вы должны обрабатывать заблокированные штрихи.

для каждого из фрагментов, которые имеют recyclerView, используйте ObservableRecyclerView и вызовите следующие строки:

    // get the fragment that shows the screen I've made (with the layout)
    Fragment fragment = getTargetFragment(); 
    // let the recyclerView handle filtered touches 
    mRecyclerView.setTouchInterceptionViewGroup(((IGetContainer) fragment).getContainer());
    // if you wish to put animations when you do a touch-up event, this may be handy (or use onUpOrCancelMotionEvent of the "TouchInterceptionListener" too/instead ) :
    if (fragment instanceof ObservableScrollViewCallbacks)                 
       mRecyclerView.setScrollViewCallbacks((ObservableScrollViewCallbacks) fragment);

Мне не нравится это решение, потому что оно охватывает весь экран с помощью обложки касаний, потому что это вызывает избыточное перемещение viewPager (и требует установки фона для него), а также потому, что оно немного сложнее по сравнению с что я имел в виду. Он также намного менее плавный, чем работает приложение "Телефон Lollipop", независимо от того, насколько я играю с правилами прокрутки.

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

Ответ 2

Я знаю, что это поздно. Но не могу использовать макет координатора с помощью CollapsingToolbarLayout внутри него.