Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Part of the content of a CollapsingToolbarLayout is getting sticky on top, when used with viewpager and recyclerview

I am trying to implement a layout like this:

enter image description here

The issue is that when scrolling up the card touches the toolbar and it does not go up anymore like this:

enter image description here

I want it to scroll up till the viewpager to fills the screen ie, at least the rectangle card, should scroll up beyond the toolbar(Even the tabLayout can also scroll up). But I don't want it to stay sticky at the top.

The main layout is here:

 <?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:showIn="@layout/activity_main">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/background_dark"
        app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:id="@+id/toolbarCollapse"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="190dp"
                android:minHeight="190dp"
                android:src="@drawable/ic_launcher_foreground"
                app:layout_collapseMode="parallax" />


            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?actionBarSize"
                app:layout_collapseMode="pin" />

        </com.google.android.material.appbar.CollapsingToolbarLayout>


    </com.google.android.material.appbar.AppBarLayout>


    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true"
        app:behavior_overlapTop="90dp"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <LinearLayout
            android:id="@+id/lin"
            android:nestedScrollingEnabled="false"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <include layout="@layout/debit_card_item" />


            <com.google.android.material.tabs.TabLayout
                android:id="@+id/tabLayout"
                android:layout_width="match_parent"
                android:layout_height="?actionBarSize"
                android:layout_marginTop="40dp"
                android:background="?attr/colorPrimary" />


            <androidx.viewpager.widget.ViewPager
                android:id="@+id/viewPager"
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1" />

        </LinearLayout>
    </androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

The fragments of the pager are inside a NestedScrollView like this:

<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fillViewport="true">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rcView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</androidx.core.widget.NestedScrollView>

Things that I tried:

  1. I have tried to remove the NestedScrollView above the linear layout and added the app:behavior_overlapTop="90dp" app:layout_behavior="@string/appbar_scrolling_view_behavior" to the linear layout. but it's still giving me the same output.
  2. Set the height of the view pager statically(gave the same result).

-->>>EDIT<<< 3. I thought of a workaround and I am trying to add custom behavior to my LinearLayout in my mainActivity. so as to reduce the margin-bottom and height of the rectangular card. It is giving good results when I fling and close the collapsing toolbar. But when I hold down and scroll, the screen is flickering as shown below:

enter image description here

My Custom behavior is

class CustomHeaderBehavior : AppBarLayout.ScrollingViewBehavior {
    private var mContext: Context

    var height = 0
    var width = 0
    var marginBottom = 0
    var firstTime = true

    constructor(
        context: Context,
        attrs: AttributeSet?
    ) : super(context, attrs) {
        mContext = context
    }

    constructor(
        context: Context?,
        attrs: AttributeSet?,
        mContext: Context
    ) : super(context, attrs) {
        this.mContext = mContext
    }

    override fun layoutDependsOn(
        parent: CoordinatorLayout,
        child: View,
        dependency: View
    ): Boolean {
        return super.layoutDependsOn(parent, child, dependency)
    }

    override fun onDependentViewChanged(
        parent: CoordinatorLayout,
        childP: View,
        dependency: View
    ): Boolean {
        val child = childP.findViewById<RelativeLayout>(R.id.cardParent)
        val maxScroll: Int = (dependency as AppBarLayout).totalScrollRange
        val percentage =
            abs(dependency.y) / maxScroll.toFloat()
        Log.d("Behavx","D.y "+abs(dependency.y)+ " m s "+maxScroll+ " p "+percentage)
        val lp: LinearLayout.LayoutParams =
            child.layoutParams as LinearLayout.LayoutParams
        if(firstTime){
            height = child.height
            width = child.width
            marginBottom = lp.bottomMargin
            firstTime = false
        }
        Log.d("Behaviour", "P "+ ((1-(percentage))*100).toString() +" H " +height+ " U H " +(height*(((1-(percentage))*100))/100) )
//        lp.bottomMargin = ((marginBottom*(((1-(percentage))*100))/100) - ((height*((((percentage))*100))/100))).toInt()
        lp.bottomMargin = ((marginBottom*(((1-(percentage))*100))/100)).toInt() //updating margin bottom
        lp.height = ((height*(((1-(percentage))*100))/100)).toInt() //updating height

        child.layoutParams = lp
        child.alpha = 1 - (percentage * 4)
        return super.onDependentViewChanged(parent, childP, dependency)

    }


    companion object {
        fun getToolbarHeight(context: Context): Int {
            var result = 0
            val tv = TypedValue()
            if (context.theme.resolveAttribute(R.attr.actionBarSize, tv, true)) {
                result = TypedValue.complexToDimensionPixelSize(
                    tv.data,
                    context.resources.displayMetrics
                )
            }
            return result
        }
    }
}

The flicker seems to be happening because the dependency.y is becoming wrong randomly, while I hold down and scroll and update the height of the card(if I only update the margin flicker is not occurring). For some reason controlling the width is happening smoothly. But unfortunately, that's not helping me here.

  1. I also tried to do something similar by adding an offsetChangedListener to the app bar like this:

    appBar.addOnOffsetChangedListener(OnOffsetChangedListener { appBarLayout, verticalOffset -> if (abs(verticalOffset) == appBarLayout.totalScrollRange) { // Collapsed cardParent.visibility = View.GONE } else { //Expanded cardParent.visibility = View.VISIBLE } })

But I am still getting the flicker

I have added the sample project here: github link The custom behavior is pushed to a new branch custom_behaviour

like image 920
hushed_voice Avatar asked May 06 '20 16:05

hushed_voice


1 Answers

Finally Solved my issue. I added the card inside the collapsing layout like Kalyans answer and added a dummy view and a -ve margin to the card to have the overlap behavior effect like

<com.google.android.material.appbar.CollapsingToolbarLayout
        android:id="@+id/toolbarCollapse"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        app:contentScrim="?attr/colorPrimary"
        app:layout_scrollFlags="scroll|exitUntilCollapsed">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?actionBarSize"
            app:layout_collapseMode="pin" />

        <View
            android:layout_width="match_parent"
            android:layout_height="200dp"
            android:background="#000" />

        <include
            layout="@layout/debit_card_item"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="-90dp"
            app:layout_collapseMode="parallax" />
    </LinearLayout>

</com.google.android.material.appbar.CollapsingToolbarLayout>

I have also pushed my code to the github.

like image 174
hushed_voice Avatar answered Oct 21 '22 14:10

hushed_voice