Expanding And Collapsing Toolbar In Android

后端 未结 4 753
遇见更好的自我
遇见更好的自我 2020-12-14 12:55

I am implementing expanding and collapsing toolbar with the help of collapsing toolbar but I am stuck when my toolbar is collapsed I want to show different toolbar. I have s

相关标签:
4条回答
  • 2020-12-14 13:04

    Here's another approach that doesn't use a custom CoordinatorLayoutBehavior.

    It uses an OnOffsetChangedListener which comes from AppBarLayout.

    Here's a snippet:

    class OnOffsetChangedListener implements AppBarLayout.OnOffsetChangedListener {
    
            @Override
            public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
    
                final int scrollRange = appBarLayout.getTotalScrollRange();
                float offsetFactor = (float) (-verticalOffset) / (float) scrollRange;
                ...
    

    This shows you how to find the total scroll range and then find the ratio between the total scroll range and the current scroll position. This is what you need to figure out how to scale and position your toolbar views.

    For a custom layout (like I did), you can override onAttachedToWindow and add the listener there:

            // Add an OnOffsetChangedListener if possible
            final ViewParent parent = getParent();
            if (parent instanceof AppBarLayout) {
                if (mOnOffsetChangedListener == null) {
                    mOnOffsetChangedListener = new OnOffsetChangedListener();
                }
                ((AppBarLayout) parent).addOnOffsetChangedListener(mOnOffsetChangedListener);
            }
    

    I found this approach to be a little simpler than creating a custom behavior.

    I created an example project on GitHub. The app looks like this:

    You can see the whole project at https://github.com/klarson2/Collapsing-Image

    0 讨论(0)
  • 2020-12-14 13:05

    you should add Line #33

    <?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"
        android:fitsSystemWindows="true">
    
        <com.google.android.material.appbar.AppBarLayout
            android:id="@+id/appbar_layout"
            android:layout_width="match_parent"
            android:layout_height="192dp"
            android:fitsSystemWindows="true"
            android:theme="@style/AppTheme">
    
            <com.google.android.material.appbar.CollapsingToolbarLayout
                android:id="@+id/collapsing_toolbar_layout"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:fitsSystemWindows="true"
                app:contentScrim="?attr/colorPrimary"
                app:layout_scrollFlags="scroll|snap|exitUntilCollapsed"
                app:title="Collapsing"
                app:toolbarId="@+id/toolbar">
            <ImageView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerCrop"
                android:src="@drawable/nana"
                app:layout_collapseMode="parallax" />
    
            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/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:padding="16dp"
        app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
    
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@drawable/ax" />
    
    </androidx.core.widget.NestedScrollView>
    
    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/floating_action_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:baselineAlignBottom="false"
        android:clickable="true"
        android:src="@drawable/possetive"
        app:fabSize="normal"
        app:layout_anchor="@id/appbar_layout"
        app:layout_anchorGravity="bottom|right"
        app:rippleColor="#E4D6D6" />
    
    
    </androidx.coordinatorlayout.widget.CoordinatorLayout>
    
    0 讨论(0)
  • 2020-12-14 13:21

    I have preperead two amaizing avatar collapsing demo samples with approach that doesn’t use a custom CoordinatorLayoutBehavior!

    To view my samples native code: "Collapsing Avatar Toolbar Sample"

    To read my "Animation Collapsing Toolbar Android" post on Medium.


    demo 1 demo 2


    Instead of use use a custom CoordinatorLayoutBehavior i use an OnOffsetChangedListener which comes from AppBarLayout.

    private lateinit var appBarLayout: AppBarLayout
    
    override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_demo_1)
            ...
            appBarLayout = findViewById(R.id.app_bar_layout)
    
            /**/
            appBarLayout.addOnOffsetChangedListener(
                    AppBarLayout.OnOffsetChangedListener { appBarLayout, i ->
                       ...
                        /**/
                        updateViews(Math.abs(i / appBarLayout.totalScrollRange.toFloat()))
                    })
        }
    

    Demo 1


    in updateViews method avatar changes the size and changes avatar’s X, Y position translation in first demo.

    private fun updateViews(offset: Float) {
    
            ...
    
            /* Collapse avatar img*/
            ivUserAvatar.apply {
                when {
                    offset > avatarAnimateStartPointY -> {
                        val avatarCollapseAnimateOffset = (offset - avatarAnimateStartPointY) * avatarCollapseAnimationChangeWeight
                        val avatarSize = EXPAND_AVATAR_SIZE - (EXPAND_AVATAR_SIZE - COLLAPSE_IMAGE_SIZE) * avatarCollapseAnimateOffset
                        this.layoutParams.also {
                            it.height = Math.round(avatarSize)
                            it.width = Math.round(avatarSize)
                        }
                        invisibleTextViewWorkAround.setTextSize(TypedValue.COMPLEX_UNIT_PX, offset)
    
                        this.translationX = ((appBarLayout.width - horizontalToolbarAvatarMargin - avatarSize) / 2) * avatarCollapseAnimateOffset
                        this.translationY = ((toolbar.height  - verticalToolbarAvatarMargin - avatarSize ) / 2) * avatarCollapseAnimateOffset
                    }
                    else -> this.layoutParams.also {
                        if (it.height != EXPAND_AVATAR_SIZE.toInt()) {
                            it.height = EXPAND_AVATAR_SIZE.toInt()
                            it.width = EXPAND_AVATAR_SIZE.toInt()
                            this.layoutParams = it
                        }
                        translationX = 0f
                    }
                }
            }
        }
    

    to find avatarAnimateStartPointY and avatarCollapseAnimationChangeWeight (for convert general offset to avatar animate offset):

    private var avatarAnimateStartPointY: Float = 0F
     private var avatarCollapseAnimationChangeWeight: Float = 0F
     private var isCalculated = false
     private var verticalToolbarAvatarMargin =0F
    ...
    if (isCalculated.not()) {
        avatarAnimateStartPointY = 
                     Math.abs((appBarLayout.height - (EXPAND_AVATAR_SIZE + horizontalToolbarAvatarMargin)) / appBarLayout.totalScrollRange)
    
        avatarCollapseAnimationChangeWeight = 1 / (1 - avatarAnimateStartPointY)
    
        verticalToolbarAvatarMargin = (toolbar.height - COLLAPSE_IMAGE_SIZE) * 2
        isCalculated = true
     }
    

    Demo 2


    avatar change his size and than animate move to right at one moment with top toolbar text became to show and moving to left.

    You need to track states: TO_EXPANDED_STATE changing, TO_COLLAPSED_STATE changing, WAIT_FOR_SWITCH.

     /*Collapsed/expended sizes for views*/
                val result: Pair<Int, Int> = when {
                    percentOffset < ABROAD -> {
                        Pair(TO_EXPANDED_STATE, cashCollapseState?.second ?: WAIT_FOR_SWITCH)
                    }
                    else -> {
                        Pair(TO_COLLAPSED_STATE, cashCollapseState?.second ?: WAIT_FOR_SWITCH)
                    }
                }
    

    Create animation for avatar on state switch change:

       result.apply {
            var translationY = 0f
            var headContainerHeight = 0f
            val translationX: Float
            var currentImageSize = 0
            when {
                cashCollapseState != null && cashCollapseState != this -> {
                    when (first) {
                        TO_EXPANDED_STATE -> {
                            translationY = toolbar.height.toFloat()
                            headContainerHeight = appBarLayout.totalScrollRange.toFloat()
                            currentImageSize = EXPAND_AVATAR_SIZE.toInt()
                            /**/
                            titleToolbarText.visibility = View.VISIBLE
                            titleToolbarTextSingle.visibility = View.INVISIBLE
                            background.setBackgroundColor(ContextCompat.getColor(this@Demo2Activity, R.color.color_transparent))
                            /**/
                            ivAvatar.translationX = 0f
                        }
    
                        TO_COLLAPSED_STATE -> {
                            background.setBackgroundColor(ContextCompat.getColor(this@Demo2Activity, R.color.colorPrimary))
                            currentImageSize = COLLAPSE_IMAGE_SIZE.toInt()
                            translationY = appBarLayout.totalScrollRange.toFloat() - (toolbar.height - COLLAPSE_IMAGE_SIZE) / 2
                            headContainerHeight = toolbar.height.toFloat()
                            translationX = appBarLayout.width / 2f - COLLAPSE_IMAGE_SIZE / 2 - margin * 2
                            /**/
                            ValueAnimator.ofFloat(ivAvatar.translationX, translationX).apply {
                                addUpdateListener {
                                    if (cashCollapseState!!.first == TO_COLLAPSED_STATE) {
                                        ivAvatar.translationX = it.animatedValue as Float
                                    }
                                }
                                interpolator = AnticipateOvershootInterpolator()
                                startDelay = 69
                                duration = 350
                                start()
                            }
                           ...
                        }
                    }
    
                    ivAvatar.apply {
                        layoutParams.height = currentImageSize
                        layoutParams.width = currentImageSize
                    }
                    collapsingAvatarContainer.apply {
                        layoutParams.height = headContainerHeight.toInt()
                        this.translationY = translationY
                        requestLayout()
                    }
                    /**/
                    cashCollapseState = Pair(first, SWITCHED)
                }
    

    To view my samples native code: "Collapsing Avatar Toolbar Sample"

    0 讨论(0)
  • 2020-12-14 13:21

    To achieve this, we must have to create custom behavior using CoordinatorLayout.Behavior

    Take into account two core elements: child and dependency:

    The child is the view that enhances behavior, dependency who will serve as a trigger to interact with the child element. In your requirement the child is the ImageView and the dependency is the Toolbar, in that way, if the Toolbar moves, the ImageView will move too.

    Please check some below links for custome behaviour toolbar demos

    • http://www.devexchanges.info/2016/03/android-tip-custom-coordinatorlayout.html
    • https://medium.com/google-developers/intercepting-everything-with-coordinatorlayout-behaviors-8c6adc140c26#.tfsd7ftkl
    0 讨论(0)
提交回复
热议问题