Как да имитирате телефонното приложение на Lollipop с преоразмеряване на горните елементи преди превъртащото се съдържание?

Заден план

В приложението Phone на Lollipop, когато стигнете до раздела „скорошни“ или „контакти“ и се опитате да превъртите, първото нещо, което се случва, е преоразмеряването на горната област, която се състои от скорошно събитие и поле за търсене като такова:

въведете описание на изображението тук

Ако имаше много повече от 3 елемента в RecyclerView, RecyclerView няма да превърти надолу, докато горната област може да бъде свита до минималния си размер.

Същото важи и за превъртането нагоре: докато горната област не е напълно увеличена, RecyclerView няма да превърта нагоре.

Проблемът

Не мога да намеря най-добрия начин за прилагане на тази функция.

По-конкретно, имам проблем с преоразмеряването на горната област, така че да бъде толкова гладко, колкото работи на Lollipop.

За да опростя, трябва да преоразмеря само една част от екрана, а не 2, както е показано в приложението на телефона („скорошното събитие“ и полето за търсене).

Това, което съм пробвал

Има няколко неща, които се опитах да направя:

  1. направете RecyclerView да стане толкова голям, колкото целия екран, като същевременно има фалшив, голям, празен хедър. когато превъртам, също задавам "translationY" на истинското съдържание в горната част. Това има проблем с показването на лентата за превъртане като част от горната област (и искам да покажа лентата за превъртане на правилното място). Плюс това, това го прави доста сложно, тъй като трябва да обработвате и стойностите за превъртане на RecyclerViews на другите страници. Това е решение, подобно на някои библиотеки на трети страни, като тази (все пак тук използвах оформление, а не ImageView, но кодът е почти идентичен).

  2. RecyclerView е под горната област и превъртането променя layoutParams на горната област, подобно на това, което направих в тази публикация< /силен>.

  3. същото като 2, но вместо да променя LayoutParams, променям scaleY на изгледите.

Както за #2, така и за #3 проблемът е, че в някои случаи, веднага щом получа събитие за превъртане на +Y в някаква стойност, получавам също превъртане дори на -Y със същата стойност (и обратно) , правейки обработката на ефекта на преоразмеряване "скоклив" (нагоре и надолу, дори ако потребителят е превъртял добре).

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

Опитах да използвам и двата < strong>setOnTouchListener (дори заедно с GestureDetectorCompat) и setOnScrollListener. Всички причиниха един и същ странен проблем.

Въпроса

Как да разреша този проблем? Защо се случва и защо не се случва винаги?


РЕДАКТИРАНЕ: Опитах се да променя #2, така че вместо да обработвам събитието за докосване на RecyclerView, ще обработвам събитията за докосване на неговия контейнер (някои LinearLayout). По някаква причина контейнерът не реагира на събития при докосване, така че го замених с RelativeLayout и поставих изглед върху всичко в това оформление, което ще реагира на събития при докосване (и съответно ще промени горната област). Това проработи, но след това не можах да предам събитията по-нататък към дъщерните изгледи, когато е необходимо.

Мисля, че най-доброто нещо е да персонализирате RecyclerView (да го разширите) при докосване. Може би добавете слушател за превъртане, че когато RecyclerView е на път да превърти, той ще попита дали е добре и ако не е, няма да се опита да превърти, докато не се задейства събитието за коригиране и не се нулира състоянието му.


person android developer    schedule 17.01.2015    source източник


Отговори (2)


Добре, едно решение (което все още не ми харесва, но работи) е да се използва клас „TouchInterceptionFrameLayout“ (от библиотеката, която намерих), която ще ви позволи да решите кога да разрешите превъртане и кога не, и кога не трябва да превъртате , вие променяте подложката (или това, което желаете) на оформлението, което има както пейджър-табовете, така и ViewPager.

Контейнерът на viewPager&tabs покрива цялата област и има подложка, която се променя.

това е оформлението:

        <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 (и изисква да зададете фон за него) и защото е малко сложно в сравнение с това, което имах се има предвид. Също така е много по-малко гладко от това, което работи приложението Phone на Lollipop, независимо колко си играя с правилата за превъртане.

Бих могъл да накарам TouchInterceptionListener да започне да обработва събитие за докосване само ако произхождат от RecyclerView, но това го прави още по-лош като сложност.

person android developer    schedule 19.01.2015

Знам, че е късно. Но не можете ли просто да използвате Coordinator Layout със CollapsingToolbarLayout вътре в него.

person Jackspicer    schedule 06.04.2016
comment
Не изглежда като това, което виждам, но може и да греша. Можете ли да покажете пример? - person android developer; 07.04.2016