如何将顶视图缩小为较小的视图? [英] How to have a collapsing of top view into a smaller sized view?

查看:87
本文介绍了如何将顶视图缩小为较小的视图?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以前,这个问题以太笼统和不清楚的方式被问到了 此处 ,所以我做了更具体的说明,并提供了我尝试过的内容的完整说明和代码.

背景

我必须模仿Google日历在顶部具有视图的方式,该方法可以为底部的视图设置动画并向下推动视图,但是它具有其他不同的行为.我总结了我要在3个特征上做的事情:

  1. 按工具栏上的始终将起作用,以切换顶视图的展开/折叠,同时具有改变其旋转的箭头图标.就像在Google日历应用中一样.
  2. 与Google日历应用程序一样,顶视图将始终对齐.
  3. 折叠顶视图时,仅按工具栏上的即可将其展开.就像在Google日历应用程序上一样
  4. 展开顶视图时,在底视图上滚动将仅允许折叠.如果您尝试向另一个方向滚动,则什么也不会发生,甚至不会移至底视图.就像在Google日历应用程序上一样
  5. 折叠后,顶视图将替换为较小的视图.这意味着它将始终在底视图上方占用一些空间.这与Google日历应用程序不同,因为在日历"应用程序中,一旦将其折叠,顶视图便会完全消失.

这是Google日历应用的外观:

在底部视图上滚动也会慢慢将顶部的视图隐藏起来:

问题

使用过去发现的各种解决方案,我仅成功实现了所需行为的一部分:

  1. 在工具栏中具有一些UI是通过在其中包含一些视图(包括箭头视图)来完成的.对于手动展开/折叠,我在AppBarLayout视图上使用setExpanded.对于箭头的旋转,我使用了AppBarLayout大小的侦听器,并在其上使用了addOnOffsetChangedListener.

  2. 通过将snap值添加到CollapsingToolbarLayoutlayout_scrollFlags属性中,可以轻松完成捕捉.但是,要使其真正有效地工作而不会出现奇怪的问题(报告 此处 ),我使用了 该解决方案 .

  3. 通过使用我在#2上使用的相同代码,可以阻止滚动时影响顶视图( 此处 ),方法是在此处调用setExpandEnabled. 此功能适用于折叠顶视图时的情况.

  4. 与#3类似,但可悲的是,由于它使用了双向的setNestedScrollingEnabled,因此仅当俯视图折叠时,此方法才能很好地工作.展开后,与日历"应用相反,它仍然允许底视图向上滚动.展开后,我需要它只允许折叠而不允许真正滚动.

这里展示了好事和坏事:

  1. 我完全无法做到这一点.我尝试了很多我想过的解决方案,将视图放置在带有各种标志的不同位置.

简而言之,我已经成功完成了1-3次,但没有成功完成4-5次.

代码

这是当前代码(也可以作为整个项目 此处 使用):

ScrollingActivity.kt

class ScrollingActivity : AppCompatActivity(), AppBarTracking {

    private var mNestedView: MyRecyclerView? = null
    private var mAppBarOffset: Int = 0
    private var mAppBarIdle = false
    private var mAppBarMaxOffset: Int = 0

    private var isExpanded: Boolean = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_scrolling)
        val toolbar = findViewById<Toolbar>(R.id.toolbar)
        setSupportActionBar(toolbar)
        mNestedView = findViewById(R.id.nestedView)
        app_bar.addOnOffsetChangedListener({ appBarLayout, verticalOffset ->
            mAppBarOffset = verticalOffset
            val totalScrollRange = appBarLayout.totalScrollRange
            val progress = (-verticalOffset).toFloat() / totalScrollRange
            arrowImageView.rotation = 180 + progress * 180
            isExpanded = verticalOffset == 0;
            mAppBarIdle = mAppBarOffset >= 0 || mAppBarOffset <= mAppBarMaxOffset
            if (mAppBarIdle)
                setExpandAndCollapseEnabled(isExpanded)
        })

        app_bar.post(Runnable { mAppBarMaxOffset = -app_bar.totalScrollRange })

        mNestedView!!.setAppBarTracking(this)
        mNestedView!!.layoutManager = LinearLayoutManager(this)
        mNestedView!!.adapter = object : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
            override fun getItemCount(): Int = 100

            override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
                return object : ViewHolder(LayoutInflater.from(parent.context).inflate(android.R.layout.simple_list_item_1, parent, false)) {}
            }

            override fun onBindViewHolder(holder: ViewHolder, position: Int) {
                (holder.itemView.findViewById<View>(android.R.id.text1) as TextView).text = "item $position"
            }
        }

        expandCollapseButton.setOnClickListener({ v ->
            isExpanded = !isExpanded
            app_bar.setExpanded(isExpanded, true)
        })
    }

    private fun setExpandAndCollapseEnabled(enabled: Boolean) {
        mNestedView!!.isNestedScrollingEnabled = enabled
    }

    override fun isAppBarExpanded(): Boolean = mAppBarOffset == 0
    override fun isAppBarIdle(): Boolean = mAppBarIdle
}

MyRecyclerView.kt

/**A RecyclerView that allows temporary pausing of casuing its scroll to affect appBarLayout, based on https://stackoverflow.com/a/45338791/878126 */
class MyRecyclerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : RecyclerView(context, attrs, defStyle) {
    private var mAppBarTracking: AppBarTracking? = null
    private var mView: View? = null
    private var mTopPos: Int = 0
    private var mLayoutManager: LinearLayoutManager? = null

    interface AppBarTracking {
        fun isAppBarIdle(): Boolean
        fun isAppBarExpanded(): Boolean
    }

    override fun dispatchNestedPreScroll(dx: Int, dy: Int, consumed: IntArray?, offsetInWindow: IntArray?,
                                         type: Int): Boolean {
        if (type == ViewCompat.TYPE_NON_TOUCH && mAppBarTracking!!.isAppBarIdle()
                && isNestedScrollingEnabled) {
            if (dy > 0) {
                if (mAppBarTracking!!.isAppBarExpanded()) {
                    consumed!![1] = dy
                    return true
                }
            } else {
                mTopPos = mLayoutManager!!.findFirstVisibleItemPosition()
                if (mTopPos == 0) {
                    mView = mLayoutManager!!.findViewByPosition(mTopPos)
                    if (-mView!!.top + dy <= 0) {
                        consumed!![1] = dy - mView!!.top
                        return true
                    }
                }
            }
        }

        val returnValue = super.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, type)
        if (offsetInWindow != null && !isNestedScrollingEnabled && offsetInWindow[1] != 0)
            offsetInWindow[1] = 0
        return returnValue
    }

    override fun setLayoutManager(layout: RecyclerView.LayoutManager) {
        super.setLayoutManager(layout)
        mLayoutManager = layoutManager as LinearLayoutManager
    }

    fun setAppBarTracking(appBarTracking: AppBarTracking) {
        mAppBarTracking = appBarTracking
    }

}

ScrollingCalendarBehavior.kt

class ScrollingCalendarBehavior(context: Context, attrs: AttributeSet) : AppBarLayout.Behavior(context, attrs) {
    override fun onInterceptTouchEvent(parent: CoordinatorLayout?, child: AppBarLayout?, ev: MotionEvent): Boolean = false
}

activity_scrolling.xml

<android.support.design.widget.CoordinatorLayout
    android:id="@+id/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:context=".ScrollingActivity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar" android:layout_width="match_parent" android:layout_height="wrap_content"
        android:fitsSystemWindows="true" android:stateListAnimator="@null" android:theme="@style/AppTheme.AppBarOverlay"
        app:expanded="false" app:layout_behavior="com.example.user.expandingtopviewtest.ScrollingCalendarBehavior"
        tools:targetApi="lollipop">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsingToolbarLayout" android:layout_width="match_parent"
            android:layout_height="match_parent" android:fitsSystemWindows="true"
            android:minHeight="?attr/actionBarSize" app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap" app:statusBarScrim="?attr/colorPrimaryDark">

            <LinearLayout
                android:layout_width="match_parent" android:layout_height="250dp"
                android:layout_marginTop="?attr/actionBarSize" app:layout_collapseMode="parallax"
                app:layout_collapseParallaxMultiplier="1.0">

                <TextView
                    android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="10dp"
                    android:paddingRight="10dp" android:text="some large, expanded view"/>
            </LinearLayout>

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar" android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize" app:layout_collapseMode="pin"
                app:popupTheme="@style/AppTheme.PopupOverlay">

                <android.support.constraint.ConstraintLayout
                    android:id="@+id/expandCollapseButton" android:layout_width="match_parent"
                    android:layout_height="?attr/actionBarSize" android:background="?android:selectableItemBackground"
                    android:clickable="true" android:focusable="true" android:orientation="vertical">

                    <TextView
                        android:id="@+id/titleTextView" android:layout_width="wrap_content"
                        android:layout_height="wrap_content" android:layout_marginBottom="8dp"
                        android:layout_marginLeft="8dp" android:layout_marginStart="8dp" android:ellipsize="end"
                        android:gravity="center" android:maxLines="1" android:text="title"
                        android:textAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title"
                        android:textColor="@android:color/white" app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintStart_toStartOf="parent"/>

                    <ImageView
                        android:id="@+id/arrowImageView" android:layout_width="wrap_content" android:layout_height="0dp"
                        android:layout_marginLeft="8dp" android:layout_marginStart="8dp"
                        app:layout_constraintBottom_toBottomOf="@+id/titleTextView"
                        app:layout_constraintStart_toEndOf="@+id/titleTextView"
                        app:layout_constraintTop_toTopOf="@+id/titleTextView"
                        app:srcCompat="@android:drawable/arrow_down_float"
                        tools:ignore="ContentDescription,RtlHardcoded"/>

                </android.support.constraint.ConstraintLayout>
            </android.support.v7.widget.Toolbar>

        </android.support.design.widget.CollapsingToolbarLayout>

    </android.support.design.widget.AppBarLayout>

    <com.example.user.expandingtopviewtest.MyRecyclerView
        android:id="@+id/nestedView" android:layout_width="match_parent" android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context=".ScrollingActivity"/>

</android.support.design.widget.CoordinatorLayout>

问题

  1. 如何扩展顶视图时阻止滚动,但在滚动时允许折叠?

  2. 在折叠时,如何使顶视图替换为较小的视图(在展开时,如何恢复为大视图),而不是完全消失?


更新

即使我已经基本了解了我所要求的内容,但当前代码仍然存在2个问题(可在Github上找到, 此处 ):

  1. 小视图(在折叠状态下看到的视图)具有需要对其单击的效果的内部视图.在它们上使用android:background="?attr/selectableItemBackgroundBorderless"并在扩展时单击该区域时,将在小视图上单击.我已经通过将小视图放在不同的工具栏上进行了处理,但是单击效果根本不会显示.我已经在 此处 上写过,包括示例项目.

解决方法:

<android.support.design.widget.CoordinatorLayout
    android:id="@+id/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:context=".MainActivity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar" android:layout_width="match_parent" android:layout_height="wrap_content"
        android:fitsSystemWindows="true" android:stateListAnimator="@null" android:theme="@style/AppTheme.AppBarOverlay"
        app:expanded="false" app:layout_behavior="com.example.expandedtopviewtestupdate.ScrollingCalendarBehavior"
        tools:targetApi="lollipop">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsingToolbarLayout" android:layout_width="match_parent"
            android:layout_height="match_parent" android:clipChildren="false" android:clipToPadding="false"
            android:fitsSystemWindows="true" app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap|enterAlways"
            app:statusBarScrim="?attr/colorPrimaryDark">

            <!--large view -->
            <LinearLayout
                android:id="@+id/largeView" android:layout_width="match_parent" android:layout_height="280dp"
                android:layout_marginTop="?attr/actionBarSize" android:orientation="vertical"
                app:layout_collapseMode="parallax" app:layout_collapseParallaxMultiplier="1.0">

                <TextView
                    android:id="@+id/largeTextView" android:layout_width="match_parent"
                    android:layout_height="match_parent" android:layout_gravity="center"
                    android:background="?attr/selectableItemBackgroundBorderless" android:clickable="true"
                    android:focusable="true" android:focusableInTouchMode="false" android:gravity="center"
                    android:text="largeView" android:textSize="14dp" tools:background="?attr/colorPrimary"
                    tools:layout_gravity="top|center_horizontal" tools:layout_height="40dp" tools:layout_width="40dp"
                    tools:text="1"/>

            </LinearLayout>

            <!--top toolbar-->
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content"
                android:layout_marginBottom="@dimen/small_view_height" app:contentInsetStart="0dp"
                app:layout_collapseMode="pin" app:popupTheme="@style/AppTheme.PopupOverlay">

                <android.support.constraint.ConstraintLayout
                    android:layout_width="match_parent" android:layout_height="wrap_content" android:clickable="true"
                    android:focusable="true">

                    <LinearLayout
                        android:id="@+id/expandCollapseButton" android:layout_width="match_parent"
                        android:layout_height="?attr/actionBarSize"
                        android:background="?android:selectableItemBackground" android:gravity="center_vertical"
                        android:orientation="horizontal" app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent">

                        <TextView
                            android:id="@+id/titleTextView" android:layout_width="wrap_content"
                            android:layout_height="wrap_content" android:ellipsize="end" android:gravity="center"
                            android:maxLines="1" android:text="title"
                            android:textAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title"
                            android:textColor="@android:color/white"/>

                        <ImageView
                            android:id="@+id/arrowImageView" android:layout_width="wrap_content"
                            android:layout_height="wrap_content" android:layout_marginLeft="8dp"
                            android:layout_marginStart="8dp" app:srcCompat="@android:drawable/arrow_up_float"
                            tools:ignore="ContentDescription,RtlHardcoded"/>
                    </LinearLayout>

                </android.support.constraint.ConstraintLayout>

            </android.support.v7.widget.Toolbar>

            <android.support.v7.widget.Toolbar
                android:id="@+id/smallLayoutContainer" android:layout_width="match_parent"
                android:layout_height="wrap_content" android:layout_marginTop="?attr/actionBarSize"
                android:clipChildren="false" android:clipToPadding="false" app:contentInsetStart="0dp"
                app:layout_collapseMode="pin">
                <!--small view-->
                <LinearLayout
                    android:id="@+id/smallLayout" android:layout_width="match_parent"
                    android:layout_height="@dimen/small_view_height" android:clipChildren="false"
                    android:clipToPadding="false" android:orientation="horizontal" tools:background="#ff330000"
                    tools:layout_height="@dimen/small_view_height">

                    <TextView
                        android:id="@+id/smallTextView" android:layout_width="match_parent"
                        android:layout_height="match_parent" android:layout_gravity="center"
                        android:background="?attr/selectableItemBackgroundBorderless" android:clickable="true"
                        android:focusable="true" android:focusableInTouchMode="false" android:gravity="center"
                        android:text="smallView" android:textSize="14dp" tools:background="?attr/colorPrimary"
                        tools:layout_gravity="top|center_horizontal" tools:layout_height="40dp"
                        tools:layout_width="40dp" tools:text="1"/>

                </LinearLayout>
            </android.support.v7.widget.Toolbar>
        </android.support.design.widget.CollapsingToolbarLayout>

    </android.support.design.widget.AppBarLayout>

    <com.example.expandedtopviewtestupdate.MyRecyclerView
        android:id="@+id/nestedView" android:layout_width="match_parent" android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context=".ScrollingActivity"/>

</android.support.design.widget.CoordinatorLayout>

  1. Google日历允许在工具栏本身上执行向下滚动手势,以触发显示月视图.我仅在其中成功添加了单击事件,但没有滚动.外观如下:

解决方案

注意:完整的更新项目可用 此处 .

在展开顶视图时如何使滚动被阻止,而在滚动时却允许折叠?

问题1::当应用栏未折叠时,RecyclerView应该完全无法滚动.要解决此问题,请在CollapsingToolbarLayout的滚动标志中添加enterAlways,如下所示:

<android.support.design.widget.CollapsingToolbarLayout
    android:id="@+id/collapsingToolbarLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipChildren="false"
    android:clipToPadding="false"
    android:fitsSystemWindows="true"
    app:layout_scrollFlags="scroll|exitUntilCollapsed|snap|enterAlways"
    app:statusBarScrim="?attr/colorPrimaryDark">

enterAlways不会在关闭状态下导致应用栏打开,因为您要禁止该功能,但可以按预期运行.

问题2::应用栏完全展开后,不允许RecyclerView向上滚动.这恰好是与问题1截然不同的问题.

[已更新] 要更正此问题,请修改RecyclerView的行为,以便在RecyclerView尝试向上滚动且应用程序栏完全展开或在之后完全展开时消耗滚动滚动(dy)被消耗了. RecyclerView可以向上滚动,但是由于它的行为SlidingPanelBehavior占用了滚动,因此它从未看到该动作.如果应用程序栏没有完全展开,但是将在使用当前滚动条之后展开,则该行为通过在完全使用滚动条之前调用修改dy并调用super来强制应用栏完全展开. (请参见SlidingPanelBehavior#onNestedPreScroll()). (在上一个答案中,修改了appBar的行为.将行为更改放在RecyclerView上是更好的选择.)

问题3::当嵌套滚动已处于所需状态时,将RecyclerView的嵌套滚动设置为启用/禁用即可.为避免这些问题,请仅在使用ScrollingActivity中的以下代码更改进行真正更改时,才更改嵌套滚动的状态:

private void setExpandAndCollapseEnabled(boolean enabled) {
    if (mNestedView.isNestedScrollingEnabled() != enabled) {
        mNestedView.setNestedScrollingEnabled(enabled);
    }
}

这是测试应用对上面所做的更改的行为:

具有上述更改的已更改模块位于文章末尾.

当折叠时,如何使顶视图替换为较小的视图(展开时又变为大视图),而不是完全消失?

[更新] 使较小的视图成为CollapsingToolbarLayout的直接子级,因此它是Toolbar的同级.以下是此方法的演示.较小视图的collapseMode设置为pin.较小的视图的边距以及工具栏的边距均已调整,因此较小的视图就位于工具栏的正下方.由于CollapsingToolbarLayoutFrameLayout,因此视图堆栈和FrameLayout的高度正好成为最高子视图的高度. 这种结构将避免需要调整插图的问题以及缺少点击效果的问题.

最后一个问题仍然存在,并假定向下拖动较小的视图不应打开应用程序栏,而将其向下拖动应将其打开.允许在拖动时打开应用栏是通过

最后,在addOnOffsetChangedListener中添加以下代码,以随着应用栏的扩展和收缩而在较小的视图中淡入/淡出.视图的Alpha为零(不可见)后,将其可见性设置为View.INVISIBLE,使其无法单击.一旦视图的Alpha值增加到零以上,请将其可见性设置为View.VISIBLE,使其可见并可以点击.

mSmallLayout.setAlpha((float) -verticalOffset / totalScrollRange);
// If the small layout is not visible, make it officially invisible so
// it can't receive clicks.
if (alpha == 0) {
    mSmallLayout.setVisibility(View.INVISIBLE);
} else if (mSmallLayout.getVisibility() == View.INVISIBLE) {
    mSmallLayout.setVisibility(View.VISIBLE);
}

以下是结果:

以下是结合了以上所有更改的新模块.

MainActivity.java

public class MainActivity extends AppCompatActivity
    implements MyRecyclerView.AppBarTracking {
    private MyRecyclerView mNestedView;
    private int mAppBarOffset = 0;
    private boolean mAppBarIdle = true;
    private int mAppBarMaxOffset = 0;
    private AppBarLayout mAppBar;
    private boolean mIsExpanded = false;
    private ImageView mArrowImageView;
    private LinearLayout mSmallLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        LinearLayout expandCollapse;

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = findViewById(R.id.toolbar);
        expandCollapse = findViewById(R.id.expandCollapseButton);
        mArrowImageView = findViewById(R.id.arrowImageView);
        mNestedView = findViewById(R.id.nestedView);
        mAppBar = findViewById(R.id.app_bar);
        mSmallLayout = findViewById(R.id.smallLayout);

        // Log when the small text view is clicked
        findViewById(R.id.smallTextView).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d(TAG, "<<<<click small layout");
            }
        });

        // Log when the big text view is clicked.
        findViewById(R.id.largeTextView).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d(TAG, "<<<<click big view");
            }
        });

        setSupportActionBar(toolbar);
        ActionBar ab = getSupportActionBar();
        if (ab != null) {
            getSupportActionBar().setDisplayShowTitleEnabled(false);
        }

        mAppBar.post(new Runnable() {
            @Override
            public void run() {
                mAppBarMaxOffset = -mAppBar.getTotalScrollRange();

                CoordinatorLayout.LayoutParams lp =
                    (CoordinatorLayout.LayoutParams) mAppBar.getLayoutParams();
                MyAppBarBehavior behavior = (MyAppBarBehavior) lp.getBehavior();
                // Only allow drag-to-open if the drag touch is on the toolbar.
                // Once open, all drags are allowed.
                if (behavior != null) {
                    behavior.setCanOpenBottom(findViewById(R.id.toolbar).getHeight());
                }
            }
        });

        mNestedView.setAppBarTracking(this);
        mNestedView.setLayoutManager(new LinearLayoutManager(this));
        mNestedView.setAdapter(new RecyclerView.Adapter<RecyclerView.ViewHolder>() {
            @Override
            public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                return new ViewHolder(
                    LayoutInflater.from(parent.getContext())
                        .inflate(android.R.layout.simple_list_item_1, parent, false));
            }

            @SuppressLint("SetTextI18n")
            @Override
            public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
                ((TextView) holder.itemView.findViewById(android.R.id.text1))
                    .setText("Item " + position);
            }

            @Override
            public int getItemCount() {
                return 200;
            }

            class ViewHolder extends RecyclerView.ViewHolder {
                public ViewHolder(View view) {
                    super(view);
                }
            }
        });

        mAppBar.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
            @Override
            public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
                mAppBarOffset = verticalOffset;
                int totalScrollRange = appBarLayout.getTotalScrollRange();
                float progress = (float) (-verticalOffset) / (float) totalScrollRange;
                mArrowImageView.setRotation(-progress * 180);
                mIsExpanded = verticalOffset == 0;
                mAppBarIdle = mAppBarOffset >= 0 || mAppBarOffset <= mAppBarMaxOffset;
                float alpha = (float) -verticalOffset / totalScrollRange;
                mSmallLayout.setAlpha(alpha);

                // If the small layout is not visible, make it officially invisible so
                // it can't receive clicks.
                if (alpha == 0) {
                    mSmallLayout.setVisibility(View.INVISIBLE);
                } else if (mSmallLayout.getVisibility() == View.INVISIBLE) {
                    mSmallLayout.setVisibility(View.VISIBLE);
                }
            }
        });

        expandCollapse.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                setExpandAndCollapseEnabled(true);
                if (mIsExpanded) {
                    setExpandAndCollapseEnabled(false);
                }
                mIsExpanded = !mIsExpanded;
                mNestedView.stopScroll();
                mAppBar.setExpanded(mIsExpanded, true);
            }
        });
    }

    private void setExpandAndCollapseEnabled(boolean enabled) {
        if (mNestedView.isNestedScrollingEnabled() != enabled) {
            mNestedView.setNestedScrollingEnabled(enabled);
        }
    }

    @Override
    public boolean isAppBarExpanded() {
        return mAppBarOffset == 0;
    }

    @Override
    public boolean isAppBarIdle() {
        return mAppBarIdle;
    }

    private static final String TAG = "MainActivity";
}

SlidingPanelBehavior.java

public class SlidingPanelBehavior extends AppBarLayout.ScrollingViewBehavior {
    private AppBarLayout mAppBar;

    public SlidingPanelBehavior() {
        super();
    }

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

    @Override
    public boolean layoutDependsOn(final CoordinatorLayout parent, View child, View dependency) {
        if (mAppBar == null && dependency instanceof AppBarLayout) {
            // Capture our appbar for later use.
            mAppBar = (AppBarLayout) dependency;
        }
        return dependency instanceof AppBarLayout;
    }

    @Override
    public boolean onInterceptTouchEvent(CoordinatorLayout parent, View child, MotionEvent event) {
        int action = event.getAction();

        if (event.getAction() != MotionEvent.ACTION_DOWN) { // Only want "down" events
            return false;
        }
        if (getAppBarLayoutOffset(mAppBar) == -mAppBar.getTotalScrollRange()) {
            // When appbar is collapsed, don't let it open through nested scrolling.
            setNestedScrollingEnabledWithTest((NestedScrollingChild2) child, false);
        } else {
            // Appbar is partially to fully expanded. Set nested scrolling enabled to activate
            // the methods within this behavior.
            setNestedScrollingEnabledWithTest((NestedScrollingChild2) child, true);
        }
        return false;
    }

    @Override
    public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child,
                                       @NonNull View directTargetChild, @NonNull View target,
                                       int axes, int type) {
        //noinspection RedundantCast
        return ((NestedScrollingChild2) child).isNestedScrollingEnabled();
    }

    @Override
    public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child,
                                  @NonNull View target, int dx, int dy, @NonNull int[] consumed,
                                  int type) {
        // How many pixels we must scroll to fully expand the appbar. This value is <= 0.
        final int appBarOffset = getAppBarLayoutOffset(mAppBar);

        // Check to see if this scroll will expand the appbar 100% or collapse it fully.
        if (dy <= appBarOffset) {
            // Scroll by the amount that will fully expand the appbar and dispose of the rest (dy).
            super.onNestedPreScroll(coordinatorLayout, mAppBar, target, dx,
                                    appBarOffset, consumed, type);
            consumed[1] += dy;
        } else if (dy >= (mAppBar.getTotalScrollRange() + appBarOffset)) {
            // This scroll will collapse the appbar. Collapse it and dispose of the rest.
            super.onNestedPreScroll(coordinatorLayout, mAppBar, target, dx,
                                    mAppBar.getTotalScrollRange() + appBarOffset,
                                    consumed, type);
            consumed[1] += dy;
        } else {
            // This scroll will leave the appbar partially open. Just do normal stuff.
            super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
        }
    }

    /**
     * {@code onNestedPreFling()} is overriden to address a nested scrolling defect that was
     * introduced in API 26. This method prevent the appbar from misbehaving when scrolled/flung.
     * <p>
     * Refer to <a href="https://issuetracker.google.com/issues/65448468"  target="_blank">"Bug in design support library"</a>
     */

    @Override
    public boolean onNestedPreFling(@NonNull CoordinatorLayout coordinatorLayout,
                                    @NonNull View child, @NonNull View target,
                                    float velocityX, float velocityY) {
        //noinspection RedundantCast
        if (((NestedScrollingChild2) child).isNestedScrollingEnabled()) {
            // Just stop the nested fling and let the appbar settle into place.
            ((NestedScrollingChild2) child).stopNestedScroll(ViewCompat.TYPE_NON_TOUCH);
            return true;
        }
        return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
    }

    private static int getAppBarLayoutOffset(AppBarLayout appBar) {
        final CoordinatorLayout.Behavior behavior =
            ((CoordinatorLayout.LayoutParams) appBar.getLayoutParams()).getBehavior();
        if (behavior instanceof AppBarLayout.Behavior) {
            return ((AppBarLayout.Behavior) behavior).getTopAndBottomOffset();
        }
        return 0;
    }

    // Something goes amiss when the flag it set to its current value, so only call
    // setNestedScrollingEnabled() if it will result in a change.
    private void setNestedScrollingEnabledWithTest(NestedScrollingChild2 child, boolean enabled) {
        if (child.isNestedScrollingEnabled() != enabled) {
            child.setNestedScrollingEnabled(enabled);
        }
    }

    @SuppressWarnings("unused")
    private static final String TAG = "SlidingPanelBehavior";
}

MyRecyclerView.kt

/**A RecyclerView that allows temporary pausing of casuing its scroll to affect appBarLayout, based on https://stackoverflow.com/a/45338791/878126 */
class MyRecyclerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : RecyclerView(context, attrs, defStyle) {
    private var mAppBarTracking: AppBarTracking? = null
    private var mView: View? = null
    private var mTopPos: Int = 0
    private var mLayoutManager: LinearLayoutManager? = null

    interface AppBarTracking {
        fun isAppBarIdle(): Boolean
        fun isAppBarExpanded(): Boolean
    }

    override fun dispatchNestedPreScroll(dx: Int, dy: Int, consumed: IntArray?, offsetInWindow: IntArray?, type: Int): Boolean {
        if (type == ViewCompat.TYPE_NON_TOUCH && mAppBarTracking!!.isAppBarIdle()
                && isNestedScrollingEnabled) {
            if (dy > 0) {
                if (mAppBarTracking!!.isAppBarExpanded()) {
                    consumed!![1] = dy
                    return true
                }
            } else {
                mTopPos = mLayoutManager!!.findFirstVisibleItemPosition()
                if (mTopPos == 0) {
                    mView = mLayoutManager!!.findViewByPosition(mTopPos)
                    if (-mView!!.top + dy <= 0) {
                        consumed!![1] = dy - mView!!.top
                        return true
                    }
                }
            }
        }
        if (dy < 0 && type == ViewCompat.TYPE_TOUCH && mAppBarTracking!!.isAppBarExpanded()) {
            consumed!![1] = dy
            return true
        }

        val returnValue = super.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, type)
        if (offsetInWindow != null && !isNestedScrollingEnabled && offsetInWindow[1] != 0)
            offsetInWindow[1] = 0
        return returnValue
    }

    override fun setLayoutManager(layout: RecyclerView.LayoutManager) {
        super.setLayoutManager(layout)
        mLayoutManager = layoutManager as LinearLayoutManager
    }

    fun setAppBarTracking(appBarTracking: AppBarTracking) {
        mAppBarTracking = appBarTracking
    }

    override fun fling(velocityX: Int, velocityY: Int): Boolean {
        var velocityY = velocityY
        if (!mAppBarTracking!!.isAppBarIdle()) {
            val vc = ViewConfiguration.get(context)
            velocityY = if (velocityY < 0) -vc.scaledMinimumFlingVelocity
            else vc.scaledMinimumFlingVelocity
        }

        return super.fling(velocityX, velocityY)
    }
}

MyAppBarBehavior.java

/**
 * Attach this behavior to AppBarLayout to disable the bottom portion of a closed appBar
 * so it cannot be touched to open the appBar. This behavior is helpful if there is some
 * portion of the appBar that displays when the appBar is closed, but should not open the appBar
 * when the appBar is closed.
 */
public class MyAppBarBehavior extends AppBarLayout.Behavior {

    // Touch above this y-axis value can open the appBar.
    private int mCanOpenBottom;

    // Determines if the appBar can be dragged open or not via direct touch on the appBar.
    private boolean mCanDrag = true;

    @SuppressWarnings("unused")
    public MyAppBarBehavior() {
        init();
    }

    @SuppressWarnings("unused")
    public MyAppBarBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        setDragCallback(new AppBarLayout.Behavior.DragCallback() {
            @Override
            public boolean canDrag(@NonNull AppBarLayout appBarLayout) {
                return mCanDrag;
            }
        });
    }

    @Override
    public boolean onInterceptTouchEvent(CoordinatorLayout parent,
                                         AppBarLayout child,
                                         MotionEvent event) {

        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            // If appBar is closed. Only allow scrolling in defined area.
            if (child.getTop() <= -child.getTotalScrollRange()) {
                mCanDrag = event.getY() < mCanOpenBottom;
            }
        }
        return super.onInterceptTouchEvent(parent, child, event);
    }

    public void setCanOpenBottom(int bottom) {
        mCanOpenBottom = bottom;
    }
}

This question was asked before in a too broad and unclear way here, so I've made it much more specific, with full explanation and code of what I've tried.

Background

I'm required to mimic the way the Google Calendar has a view at the top, that can animate and push down the view at the bottom, yet it has extra, different behavior. I've summarized what I'm trying to do on 3 characteristics:

  1. Pressing on the toolbar will always work, to toggle expanding/collapsing of the top view, while having an arrow icon that changes its rotation. This is like on Google Calendar app.
  2. The top view will always snap, just like on Google Calendar app.
  3. When the top view is collapsed, only pressing on the toolbar will allow to expand it. This is like on Google Calendar app
  4. When the top view is expanded, scrolling at the bottom view will only allow to collapse. If you try to scroll the other direction, nothing occurs, not even to the bottom view. This is like on Google Calendar app
  5. Once collapsed, the top view will be replaced with a smaller view. This means it will always take some space, above the bottom view. This is not like on Google Calendar app, because on the Calendar app, the top view completely disappears once you collapse it.

Here's how Google Calendar app look like:

Scrolling on the bottom view also slowly hides the view at the top:

The problem

Using various solutions I've found in the past, I've succeeded to implement only a part of the needed behavior:

  1. Having some UI in the toolbar is done by having some views in it, including the arrow view. For manual expanding/collapsing I use setExpanded on the AppBarLayout view. For the rotation of the arrow, I use a listener of how much the AppBarLayout has resized, using addOnOffsetChangedListener on it.

  2. Snapping is easily done by adding snap value into layout_scrollFlags attribute of the CollapsingToolbarLayout. However, to make it really work well, without weird issues (reported here), I used this solution.

  3. Blocking of affecting the top view when scrolling can be done by using the same code I've used on #2 (here), by calling setExpandEnabled there. This works fine for when the top view is collapsed.

  4. Similar to #3, but sadly, since it uses setNestedScrollingEnabled, which is in both directions, this works well only when the top view is collapsed. When it's expanded, it still allows the bottom view to scroll up, as opposed to Calendar app. When expanded, I need it to only allow to collapse, without allowing to really scroll.

Here's a demonstration of the good, and the bad:

  1. This I've failed completely to do. I've tried a lot of solutions I've thought about, putting views in various places with various flags.

In short, I've succeeded doing 1-3, but not 4-5.

The code

Here's the current code (also available as whole project here) :

ScrollingActivity.kt

class ScrollingActivity : AppCompatActivity(), AppBarTracking {

    private var mNestedView: MyRecyclerView? = null
    private var mAppBarOffset: Int = 0
    private var mAppBarIdle = false
    private var mAppBarMaxOffset: Int = 0

    private var isExpanded: Boolean = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_scrolling)
        val toolbar = findViewById<Toolbar>(R.id.toolbar)
        setSupportActionBar(toolbar)
        mNestedView = findViewById(R.id.nestedView)
        app_bar.addOnOffsetChangedListener({ appBarLayout, verticalOffset ->
            mAppBarOffset = verticalOffset
            val totalScrollRange = appBarLayout.totalScrollRange
            val progress = (-verticalOffset).toFloat() / totalScrollRange
            arrowImageView.rotation = 180 + progress * 180
            isExpanded = verticalOffset == 0;
            mAppBarIdle = mAppBarOffset >= 0 || mAppBarOffset <= mAppBarMaxOffset
            if (mAppBarIdle)
                setExpandAndCollapseEnabled(isExpanded)
        })

        app_bar.post(Runnable { mAppBarMaxOffset = -app_bar.totalScrollRange })

        mNestedView!!.setAppBarTracking(this)
        mNestedView!!.layoutManager = LinearLayoutManager(this)
        mNestedView!!.adapter = object : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
            override fun getItemCount(): Int = 100

            override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
                return object : ViewHolder(LayoutInflater.from(parent.context).inflate(android.R.layout.simple_list_item_1, parent, false)) {}
            }

            override fun onBindViewHolder(holder: ViewHolder, position: Int) {
                (holder.itemView.findViewById<View>(android.R.id.text1) as TextView).text = "item $position"
            }
        }

        expandCollapseButton.setOnClickListener({ v ->
            isExpanded = !isExpanded
            app_bar.setExpanded(isExpanded, true)
        })
    }

    private fun setExpandAndCollapseEnabled(enabled: Boolean) {
        mNestedView!!.isNestedScrollingEnabled = enabled
    }

    override fun isAppBarExpanded(): Boolean = mAppBarOffset == 0
    override fun isAppBarIdle(): Boolean = mAppBarIdle
}

MyRecyclerView.kt

/**A RecyclerView that allows temporary pausing of casuing its scroll to affect appBarLayout, based on https://stackoverflow.com/a/45338791/878126 */
class MyRecyclerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : RecyclerView(context, attrs, defStyle) {
    private var mAppBarTracking: AppBarTracking? = null
    private var mView: View? = null
    private var mTopPos: Int = 0
    private var mLayoutManager: LinearLayoutManager? = null

    interface AppBarTracking {
        fun isAppBarIdle(): Boolean
        fun isAppBarExpanded(): Boolean
    }

    override fun dispatchNestedPreScroll(dx: Int, dy: Int, consumed: IntArray?, offsetInWindow: IntArray?,
                                         type: Int): Boolean {
        if (type == ViewCompat.TYPE_NON_TOUCH && mAppBarTracking!!.isAppBarIdle()
                && isNestedScrollingEnabled) {
            if (dy > 0) {
                if (mAppBarTracking!!.isAppBarExpanded()) {
                    consumed!![1] = dy
                    return true
                }
            } else {
                mTopPos = mLayoutManager!!.findFirstVisibleItemPosition()
                if (mTopPos == 0) {
                    mView = mLayoutManager!!.findViewByPosition(mTopPos)
                    if (-mView!!.top + dy <= 0) {
                        consumed!![1] = dy - mView!!.top
                        return true
                    }
                }
            }
        }

        val returnValue = super.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, type)
        if (offsetInWindow != null && !isNestedScrollingEnabled && offsetInWindow[1] != 0)
            offsetInWindow[1] = 0
        return returnValue
    }

    override fun setLayoutManager(layout: RecyclerView.LayoutManager) {
        super.setLayoutManager(layout)
        mLayoutManager = layoutManager as LinearLayoutManager
    }

    fun setAppBarTracking(appBarTracking: AppBarTracking) {
        mAppBarTracking = appBarTracking
    }

}

ScrollingCalendarBehavior.kt

class ScrollingCalendarBehavior(context: Context, attrs: AttributeSet) : AppBarLayout.Behavior(context, attrs) {
    override fun onInterceptTouchEvent(parent: CoordinatorLayout?, child: AppBarLayout?, ev: MotionEvent): Boolean = false
}

activity_scrolling.xml

<android.support.design.widget.CoordinatorLayout
    android:id="@+id/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:context=".ScrollingActivity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar" android:layout_width="match_parent" android:layout_height="wrap_content"
        android:fitsSystemWindows="true" android:stateListAnimator="@null" android:theme="@style/AppTheme.AppBarOverlay"
        app:expanded="false" app:layout_behavior="com.example.user.expandingtopviewtest.ScrollingCalendarBehavior"
        tools:targetApi="lollipop">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsingToolbarLayout" android:layout_width="match_parent"
            android:layout_height="match_parent" android:fitsSystemWindows="true"
            android:minHeight="?attr/actionBarSize" app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap" app:statusBarScrim="?attr/colorPrimaryDark">

            <LinearLayout
                android:layout_width="match_parent" android:layout_height="250dp"
                android:layout_marginTop="?attr/actionBarSize" app:layout_collapseMode="parallax"
                app:layout_collapseParallaxMultiplier="1.0">

                <TextView
                    android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="10dp"
                    android:paddingRight="10dp" android:text="some large, expanded view"/>
            </LinearLayout>

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar" android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize" app:layout_collapseMode="pin"
                app:popupTheme="@style/AppTheme.PopupOverlay">

                <android.support.constraint.ConstraintLayout
                    android:id="@+id/expandCollapseButton" android:layout_width="match_parent"
                    android:layout_height="?attr/actionBarSize" android:background="?android:selectableItemBackground"
                    android:clickable="true" android:focusable="true" android:orientation="vertical">

                    <TextView
                        android:id="@+id/titleTextView" android:layout_width="wrap_content"
                        android:layout_height="wrap_content" android:layout_marginBottom="8dp"
                        android:layout_marginLeft="8dp" android:layout_marginStart="8dp" android:ellipsize="end"
                        android:gravity="center" android:maxLines="1" android:text="title"
                        android:textAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title"
                        android:textColor="@android:color/white" app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintStart_toStartOf="parent"/>

                    <ImageView
                        android:id="@+id/arrowImageView" android:layout_width="wrap_content" android:layout_height="0dp"
                        android:layout_marginLeft="8dp" android:layout_marginStart="8dp"
                        app:layout_constraintBottom_toBottomOf="@+id/titleTextView"
                        app:layout_constraintStart_toEndOf="@+id/titleTextView"
                        app:layout_constraintTop_toTopOf="@+id/titleTextView"
                        app:srcCompat="@android:drawable/arrow_down_float"
                        tools:ignore="ContentDescription,RtlHardcoded"/>

                </android.support.constraint.ConstraintLayout>
            </android.support.v7.widget.Toolbar>

        </android.support.design.widget.CollapsingToolbarLayout>

    </android.support.design.widget.AppBarLayout>

    <com.example.user.expandingtopviewtest.MyRecyclerView
        android:id="@+id/nestedView" android:layout_width="match_parent" android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context=".ScrollingActivity"/>

</android.support.design.widget.CoordinatorLayout>

The questions

  1. How can I make the scrolling being blocked when the top view is expanded, yet allow to collapse while scrolling ?

  2. How can I make the top view be replaced with a smaller one when collapsed (and back to large one when expanded), instead of completely disappear ?


Update

Even though I've got the basic of what I asked about, there are still 2 issues with the current code (available on Github, here) :

  1. The small view (the one you see on collapsed state) has inner views that need to have a clicking effect on them. When using the android:background="?attr/selectableItemBackgroundBorderless" on them, and clicking on this area while being expanded, the clicking is done on the small view. I've handled it by putting the small view on a different toolbar, but then the clicking effect doesn't get shown at all. I've written about this here, including sample project.

Here's the fix:

<android.support.design.widget.CoordinatorLayout
    android:id="@+id/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:context=".MainActivity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar" android:layout_width="match_parent" android:layout_height="wrap_content"
        android:fitsSystemWindows="true" android:stateListAnimator="@null" android:theme="@style/AppTheme.AppBarOverlay"
        app:expanded="false" app:layout_behavior="com.example.expandedtopviewtestupdate.ScrollingCalendarBehavior"
        tools:targetApi="lollipop">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsingToolbarLayout" android:layout_width="match_parent"
            android:layout_height="match_parent" android:clipChildren="false" android:clipToPadding="false"
            android:fitsSystemWindows="true" app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap|enterAlways"
            app:statusBarScrim="?attr/colorPrimaryDark">

            <!--large view -->
            <LinearLayout
                android:id="@+id/largeView" android:layout_width="match_parent" android:layout_height="280dp"
                android:layout_marginTop="?attr/actionBarSize" android:orientation="vertical"
                app:layout_collapseMode="parallax" app:layout_collapseParallaxMultiplier="1.0">

                <TextView
                    android:id="@+id/largeTextView" android:layout_width="match_parent"
                    android:layout_height="match_parent" android:layout_gravity="center"
                    android:background="?attr/selectableItemBackgroundBorderless" android:clickable="true"
                    android:focusable="true" android:focusableInTouchMode="false" android:gravity="center"
                    android:text="largeView" android:textSize="14dp" tools:background="?attr/colorPrimary"
                    tools:layout_gravity="top|center_horizontal" tools:layout_height="40dp" tools:layout_width="40dp"
                    tools:text="1"/>

            </LinearLayout>

            <!--top toolbar-->
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content"
                android:layout_marginBottom="@dimen/small_view_height" app:contentInsetStart="0dp"
                app:layout_collapseMode="pin" app:popupTheme="@style/AppTheme.PopupOverlay">

                <android.support.constraint.ConstraintLayout
                    android:layout_width="match_parent" android:layout_height="wrap_content" android:clickable="true"
                    android:focusable="true">

                    <LinearLayout
                        android:id="@+id/expandCollapseButton" android:layout_width="match_parent"
                        android:layout_height="?attr/actionBarSize"
                        android:background="?android:selectableItemBackground" android:gravity="center_vertical"
                        android:orientation="horizontal" app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent">

                        <TextView
                            android:id="@+id/titleTextView" android:layout_width="wrap_content"
                            android:layout_height="wrap_content" android:ellipsize="end" android:gravity="center"
                            android:maxLines="1" android:text="title"
                            android:textAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title"
                            android:textColor="@android:color/white"/>

                        <ImageView
                            android:id="@+id/arrowImageView" android:layout_width="wrap_content"
                            android:layout_height="wrap_content" android:layout_marginLeft="8dp"
                            android:layout_marginStart="8dp" app:srcCompat="@android:drawable/arrow_up_float"
                            tools:ignore="ContentDescription,RtlHardcoded"/>
                    </LinearLayout>

                </android.support.constraint.ConstraintLayout>

            </android.support.v7.widget.Toolbar>

            <android.support.v7.widget.Toolbar
                android:id="@+id/smallLayoutContainer" android:layout_width="match_parent"
                android:layout_height="wrap_content" android:layout_marginTop="?attr/actionBarSize"
                android:clipChildren="false" android:clipToPadding="false" app:contentInsetStart="0dp"
                app:layout_collapseMode="pin">
                <!--small view-->
                <LinearLayout
                    android:id="@+id/smallLayout" android:layout_width="match_parent"
                    android:layout_height="@dimen/small_view_height" android:clipChildren="false"
                    android:clipToPadding="false" android:orientation="horizontal" tools:background="#ff330000"
                    tools:layout_height="@dimen/small_view_height">

                    <TextView
                        android:id="@+id/smallTextView" android:layout_width="match_parent"
                        android:layout_height="match_parent" android:layout_gravity="center"
                        android:background="?attr/selectableItemBackgroundBorderless" android:clickable="true"
                        android:focusable="true" android:focusableInTouchMode="false" android:gravity="center"
                        android:text="smallView" android:textSize="14dp" tools:background="?attr/colorPrimary"
                        tools:layout_gravity="top|center_horizontal" tools:layout_height="40dp"
                        tools:layout_width="40dp" tools:text="1"/>

                </LinearLayout>
            </android.support.v7.widget.Toolbar>
        </android.support.design.widget.CollapsingToolbarLayout>

    </android.support.design.widget.AppBarLayout>

    <com.example.expandedtopviewtestupdate.MyRecyclerView
        android:id="@+id/nestedView" android:layout_width="match_parent" android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context=".ScrollingActivity"/>

</android.support.design.widget.CoordinatorLayout>

  1. The Google Calendar allows to perform a scroll-down gesture on the toolbar itself, to trigger showing the month view. I've succeeded only adding a clicking event there, but not scrolling. Here's how it looks like:

解决方案

Note: Full updated project is available here.

How can I make the scrolling being blocked when the top view is expanded, yet allow to collapse while scrolling ?

Issue #1: The RecyclerView should not be able to scroll at all when the app bar is not collapsed. To fix this, add enterAlways to the scroll flags for the CollapsingToolbarLayout as follows:

<android.support.design.widget.CollapsingToolbarLayout
    android:id="@+id/collapsingToolbarLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipChildren="false"
    android:clipToPadding="false"
    android:fitsSystemWindows="true"
    app:layout_scrollFlags="scroll|exitUntilCollapsed|snap|enterAlways"
    app:statusBarScrim="?attr/colorPrimaryDark">

enterAlways will not cause the app bar to open when closed since you are suppressing that functionality but works as desired otherwise.

Issue #2: When the app bar is fully expanded, the RecyclerView should not be allowed to scroll up. This happens to be a distinct issue from issue #1.

[Updated] To correct this, modify the behavior for the RecyclerView to consume scroll when the RecyclerView tries to scroll up and the app bar is fully expanded or will be fully expanded after the scroll(dy) is consumed . The RecyclerView can scroll up, but it never sees that action since its behavior, SlidingPanelBehavior, consumes the scroll. If the app bar is not fully expanded but will be expanded after the current scroll is consumed, the behavior forces the app bar to fully expand by calling modifying dy and calling the super before fully consuming the scroll. (See SlidingPanelBehavior#onNestedPreScroll()). (In the previous answer, the appBar behavior was modified. Putting the behavior change on RecyclerView is a better choice.)

Issue #3: Setting nested scrolling for the RecyclerView to enable/disabled when nested scrolling is already in the required state causes problems. To avoid these issues, only change the state of nested scrolling when a change is really being made with the following code change in ScrollingActivity:

private void setExpandAndCollapseEnabled(boolean enabled) {
    if (mNestedView.isNestedScrollingEnabled() != enabled) {
        mNestedView.setNestedScrollingEnabled(enabled);
    }
}

This is how the test app behaves with the changes from above:

The changed modules with the above-mentioned changes are at the end of the post.

How can I make the top view be replaced with a smaller one when collapsed (and back to large one when expanded), instead of completely disappear ?

[Update] Make the smaller view a direct child of CollapsingToolbarLayout so it is a sibling of Toolbar. The following is a demonstration of this approach. The collapseMode of the smaller view is set to pin. The smaller view's margins as well as the margins of the toolbar are adjusted so the smaller view falls immediately below the toolbar. Since CollapsingToolbarLayout is a FrameLayout, views stack and the height of the FrameLayout just becomes the height of the tallest child view. This structure will avoid the issue where the insets needed adjustment and the problem with the missing click effect.

One final issue remains and that dragging the appbar down should open it with the assumption that dragging the smaller view down should not open the appbar. Permitting the appbar to open upon dragging is accomplished with setDragCallback of AppBarLayout.Behavior. Since the smaller view is incorporated into the appBar, dragging it down will open the appbar. To prevent this, a new behavior called MyAppBarBehavior is attached to the appbar. This behavior, in conjunction with code in the MainActivity prevents dragging of the smaller view to open the appbar but will permit the toolbar to be dragged.

activity_main.xml

<android.support.design.widget.CoordinatorLayout 
    android:id="@+id/coordinatorLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fitsSystemWindows="true"
        android:stateListAnimator="@null"
        android:theme="@style/AppTheme.AppBarOverlay"
        app:expanded="false"
        app:layout_behavior=".MyAppBarBehavior"
        tools:targetApi="lollipop">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsingToolbarLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clipChildren="false"
            android:clipToPadding="false"
            android:fitsSystemWindows="true"
            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap|enterAlways"
            app:statusBarScrim="?attr/colorPrimaryDark">

            <!--large view -->
            <LinearLayout
                android:id="@+id/largeView"
                android:layout_width="match_parent"
                android:layout_height="280dp"
                android:layout_marginTop="?attr/actionBarSize"
                android:orientation="vertical"
                app:layout_collapseMode="parallax"
                app:layout_collapseParallaxMultiplier="1.0">

                <TextView
                    android:id="@+id/largeTextView"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_gravity="center"
                    android:background="?attr/selectableItemBackgroundBorderless"
                    android:clickable="true"
                    android:focusable="true"
                    android:focusableInTouchMode="false"
                    android:gravity="center"
                    android:text="largeView"
                    android:textSize="14dp"
                    tools:background="?attr/colorPrimary"
                    tools:layout_gravity="top|center_horizontal"
                    tools:layout_height="40dp"
                    tools:layout_width="40dp"
                    tools:text="1" />

            </LinearLayout>

            <!--top toolbar-->
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="@dimen/small_view_height"
                app:contentInsetStart="0dp"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/AppTheme.PopupOverlay">

                <android.support.constraint.ConstraintLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:clickable="true"
                    android:focusable="true">

                    <LinearLayout
                        android:id="@+id/expandCollapseButton"
                        android:layout_width="match_parent"
                        android:layout_height="?attr/actionBarSize"
                        android:background="?android:selectableItemBackground"
                        android:gravity="center_vertical"
                        android:orientation="horizontal"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent">

                        <TextView
                            android:id="@+id/titleTextView"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:ellipsize="end"
                            android:gravity="center"
                            android:maxLines="1"
                            android:text="title"
                            android:textAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title"
                            android:textColor="@android:color/white" />

                        <ImageView
                            android:id="@+id/arrowImageView"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginLeft="8dp"
                            android:layout_marginStart="8dp"
                            app:srcCompat="@android:drawable/arrow_up_float"
                            tools:ignore="ContentDescription,RtlHardcoded" />
                    </LinearLayout>

                </android.support.constraint.ConstraintLayout>

            </android.support.v7.widget.Toolbar>

            <!--small view-->
            <LinearLayout
                android:id="@+id/smallLayout"
                android:layout_width="match_parent"
                android:layout_height="@dimen/small_view_height"
                android:layout_marginTop="?attr/actionBarSize"
                android:clipChildren="false"
                android:clipToPadding="false"
                android:orientation="horizontal"
                app:layout_collapseMode="pin"
                tools:background="#ff330000"
                tools:layout_height="@dimen/small_view_height">

                <TextView
                    android:id="@+id/smallTextView"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_gravity="center"
                    android:background="?attr/selectableItemBackgroundBorderless"
                    android:clickable="true"
                    android:focusable="true"
                    android:focusableInTouchMode="false"
                    android:gravity="center"
                    android:text="smallView"
                    android:textSize="14dp"
                    tools:background="?attr/colorPrimary"
                    tools:layout_gravity="top|center_horizontal"
                    tools:layout_height="40dp"
                    tools:layout_width="40dp"
                    tools:text="1" />

            </LinearLayout>
        </android.support.design.widget.CollapsingToolbarLayout>

    </android.support.design.widget.AppBarLayout>

    <com.example.expandedtopviewtestupdate.MyRecyclerView
        android:id="@+id/nestedView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:context=".SlidingPanelBehavior" />

</android.support.design.widget.CoordinatorLayout>

Finally, in the addOnOffsetChangedListener add the following code to fade out/fade in the smaller view as the app bar expands and contracts. Once the view's alpha is zero (invisible), set its visibility to View.INVISIBLE so it can't be clicked. Once the view's alpha increases above zero, make it visible and clickable by setting its visibility to View.VISIBLE.

mSmallLayout.setAlpha((float) -verticalOffset / totalScrollRange);
// If the small layout is not visible, make it officially invisible so
// it can't receive clicks.
if (alpha == 0) {
    mSmallLayout.setVisibility(View.INVISIBLE);
} else if (mSmallLayout.getVisibility() == View.INVISIBLE) {
    mSmallLayout.setVisibility(View.VISIBLE);
}

Here are the results:

Here are the new modules with all of the above changes incorporated.

MainActivity.java

public class MainActivity extends AppCompatActivity
    implements MyRecyclerView.AppBarTracking {
    private MyRecyclerView mNestedView;
    private int mAppBarOffset = 0;
    private boolean mAppBarIdle = true;
    private int mAppBarMaxOffset = 0;
    private AppBarLayout mAppBar;
    private boolean mIsExpanded = false;
    private ImageView mArrowImageView;
    private LinearLayout mSmallLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        LinearLayout expandCollapse;

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = findViewById(R.id.toolbar);
        expandCollapse = findViewById(R.id.expandCollapseButton);
        mArrowImageView = findViewById(R.id.arrowImageView);
        mNestedView = findViewById(R.id.nestedView);
        mAppBar = findViewById(R.id.app_bar);
        mSmallLayout = findViewById(R.id.smallLayout);

        // Log when the small text view is clicked
        findViewById(R.id.smallTextView).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d(TAG, "<<<<click small layout");
            }
        });

        // Log when the big text view is clicked.
        findViewById(R.id.largeTextView).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d(TAG, "<<<<click big view");
            }
        });

        setSupportActionBar(toolbar);
        ActionBar ab = getSupportActionBar();
        if (ab != null) {
            getSupportActionBar().setDisplayShowTitleEnabled(false);
        }

        mAppBar.post(new Runnable() {
            @Override
            public void run() {
                mAppBarMaxOffset = -mAppBar.getTotalScrollRange();

                CoordinatorLayout.LayoutParams lp =
                    (CoordinatorLayout.LayoutParams) mAppBar.getLayoutParams();
                MyAppBarBehavior behavior = (MyAppBarBehavior) lp.getBehavior();
                // Only allow drag-to-open if the drag touch is on the toolbar.
                // Once open, all drags are allowed.
                if (behavior != null) {
                    behavior.setCanOpenBottom(findViewById(R.id.toolbar).getHeight());
                }
            }
        });

        mNestedView.setAppBarTracking(this);
        mNestedView.setLayoutManager(new LinearLayoutManager(this));
        mNestedView.setAdapter(new RecyclerView.Adapter<RecyclerView.ViewHolder>() {
            @Override
            public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                return new ViewHolder(
                    LayoutInflater.from(parent.getContext())
                        .inflate(android.R.layout.simple_list_item_1, parent, false));
            }

            @SuppressLint("SetTextI18n")
            @Override
            public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
                ((TextView) holder.itemView.findViewById(android.R.id.text1))
                    .setText("Item " + position);
            }

            @Override
            public int getItemCount() {
                return 200;
            }

            class ViewHolder extends RecyclerView.ViewHolder {
                public ViewHolder(View view) {
                    super(view);
                }
            }
        });

        mAppBar.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
            @Override
            public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
                mAppBarOffset = verticalOffset;
                int totalScrollRange = appBarLayout.getTotalScrollRange();
                float progress = (float) (-verticalOffset) / (float) totalScrollRange;
                mArrowImageView.setRotation(-progress * 180);
                mIsExpanded = verticalOffset == 0;
                mAppBarIdle = mAppBarOffset >= 0 || mAppBarOffset <= mAppBarMaxOffset;
                float alpha = (float) -verticalOffset / totalScrollRange;
                mSmallLayout.setAlpha(alpha);

                // If the small layout is not visible, make it officially invisible so
                // it can't receive clicks.
                if (alpha == 0) {
                    mSmallLayout.setVisibility(View.INVISIBLE);
                } else if (mSmallLayout.getVisibility() == View.INVISIBLE) {
                    mSmallLayout.setVisibility(View.VISIBLE);
                }
            }
        });

        expandCollapse.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                setExpandAndCollapseEnabled(true);
                if (mIsExpanded) {
                    setExpandAndCollapseEnabled(false);
                }
                mIsExpanded = !mIsExpanded;
                mNestedView.stopScroll();
                mAppBar.setExpanded(mIsExpanded, true);
            }
        });
    }

    private void setExpandAndCollapseEnabled(boolean enabled) {
        if (mNestedView.isNestedScrollingEnabled() != enabled) {
            mNestedView.setNestedScrollingEnabled(enabled);
        }
    }

    @Override
    public boolean isAppBarExpanded() {
        return mAppBarOffset == 0;
    }

    @Override
    public boolean isAppBarIdle() {
        return mAppBarIdle;
    }

    private static final String TAG = "MainActivity";
}

SlidingPanelBehavior.java

public class SlidingPanelBehavior extends AppBarLayout.ScrollingViewBehavior {
    private AppBarLayout mAppBar;

    public SlidingPanelBehavior() {
        super();
    }

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

    @Override
    public boolean layoutDependsOn(final CoordinatorLayout parent, View child, View dependency) {
        if (mAppBar == null && dependency instanceof AppBarLayout) {
            // Capture our appbar for later use.
            mAppBar = (AppBarLayout) dependency;
        }
        return dependency instanceof AppBarLayout;
    }

    @Override
    public boolean onInterceptTouchEvent(CoordinatorLayout parent, View child, MotionEvent event) {
        int action = event.getAction();

        if (event.getAction() != MotionEvent.ACTION_DOWN) { // Only want "down" events
            return false;
        }
        if (getAppBarLayoutOffset(mAppBar) == -mAppBar.getTotalScrollRange()) {
            // When appbar is collapsed, don't let it open through nested scrolling.
            setNestedScrollingEnabledWithTest((NestedScrollingChild2) child, false);
        } else {
            // Appbar is partially to fully expanded. Set nested scrolling enabled to activate
            // the methods within this behavior.
            setNestedScrollingEnabledWithTest((NestedScrollingChild2) child, true);
        }
        return false;
    }

    @Override
    public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child,
                                       @NonNull View directTargetChild, @NonNull View target,
                                       int axes, int type) {
        //noinspection RedundantCast
        return ((NestedScrollingChild2) child).isNestedScrollingEnabled();
    }

    @Override
    public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child,
                                  @NonNull View target, int dx, int dy, @NonNull int[] consumed,
                                  int type) {
        // How many pixels we must scroll to fully expand the appbar. This value is <= 0.
        final int appBarOffset = getAppBarLayoutOffset(mAppBar);

        // Check to see if this scroll will expand the appbar 100% or collapse it fully.
        if (dy <= appBarOffset) {
            // Scroll by the amount that will fully expand the appbar and dispose of the rest (dy).
            super.onNestedPreScroll(coordinatorLayout, mAppBar, target, dx,
                                    appBarOffset, consumed, type);
            consumed[1] += dy;
        } else if (dy >= (mAppBar.getTotalScrollRange() + appBarOffset)) {
            // This scroll will collapse the appbar. Collapse it and dispose of the rest.
            super.onNestedPreScroll(coordinatorLayout, mAppBar, target, dx,
                                    mAppBar.getTotalScrollRange() + appBarOffset,
                                    consumed, type);
            consumed[1] += dy;
        } else {
            // This scroll will leave the appbar partially open. Just do normal stuff.
            super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
        }
    }

    /**
     * {@code onNestedPreFling()} is overriden to address a nested scrolling defect that was
     * introduced in API 26. This method prevent the appbar from misbehaving when scrolled/flung.
     * <p>
     * Refer to <a href="https://issuetracker.google.com/issues/65448468"  target="_blank">"Bug in design support library"</a>
     */

    @Override
    public boolean onNestedPreFling(@NonNull CoordinatorLayout coordinatorLayout,
                                    @NonNull View child, @NonNull View target,
                                    float velocityX, float velocityY) {
        //noinspection RedundantCast
        if (((NestedScrollingChild2) child).isNestedScrollingEnabled()) {
            // Just stop the nested fling and let the appbar settle into place.
            ((NestedScrollingChild2) child).stopNestedScroll(ViewCompat.TYPE_NON_TOUCH);
            return true;
        }
        return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
    }

    private static int getAppBarLayoutOffset(AppBarLayout appBar) {
        final CoordinatorLayout.Behavior behavior =
            ((CoordinatorLayout.LayoutParams) appBar.getLayoutParams()).getBehavior();
        if (behavior instanceof AppBarLayout.Behavior) {
            return ((AppBarLayout.Behavior) behavior).getTopAndBottomOffset();
        }
        return 0;
    }

    // Something goes amiss when the flag it set to its current value, so only call
    // setNestedScrollingEnabled() if it will result in a change.
    private void setNestedScrollingEnabledWithTest(NestedScrollingChild2 child, boolean enabled) {
        if (child.isNestedScrollingEnabled() != enabled) {
            child.setNestedScrollingEnabled(enabled);
        }
    }

    @SuppressWarnings("unused")
    private static final String TAG = "SlidingPanelBehavior";
}

MyRecyclerView.kt

/**A RecyclerView that allows temporary pausing of casuing its scroll to affect appBarLayout, based on https://stackoverflow.com/a/45338791/878126 */
class MyRecyclerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : RecyclerView(context, attrs, defStyle) {
    private var mAppBarTracking: AppBarTracking? = null
    private var mView: View? = null
    private var mTopPos: Int = 0
    private var mLayoutManager: LinearLayoutManager? = null

    interface AppBarTracking {
        fun isAppBarIdle(): Boolean
        fun isAppBarExpanded(): Boolean
    }

    override fun dispatchNestedPreScroll(dx: Int, dy: Int, consumed: IntArray?, offsetInWindow: IntArray?, type: Int): Boolean {
        if (type == ViewCompat.TYPE_NON_TOUCH && mAppBarTracking!!.isAppBarIdle()
                && isNestedScrollingEnabled) {
            if (dy > 0) {
                if (mAppBarTracking!!.isAppBarExpanded()) {
                    consumed!![1] = dy
                    return true
                }
            } else {
                mTopPos = mLayoutManager!!.findFirstVisibleItemPosition()
                if (mTopPos == 0) {
                    mView = mLayoutManager!!.findViewByPosition(mTopPos)
                    if (-mView!!.top + dy <= 0) {
                        consumed!![1] = dy - mView!!.top
                        return true
                    }
                }
            }
        }
        if (dy < 0 && type == ViewCompat.TYPE_TOUCH && mAppBarTracking!!.isAppBarExpanded()) {
            consumed!![1] = dy
            return true
        }

        val returnValue = super.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, type)
        if (offsetInWindow != null && !isNestedScrollingEnabled && offsetInWindow[1] != 0)
            offsetInWindow[1] = 0
        return returnValue
    }

    override fun setLayoutManager(layout: RecyclerView.LayoutManager) {
        super.setLayoutManager(layout)
        mLayoutManager = layoutManager as LinearLayoutManager
    }

    fun setAppBarTracking(appBarTracking: AppBarTracking) {
        mAppBarTracking = appBarTracking
    }

    override fun fling(velocityX: Int, velocityY: Int): Boolean {
        var velocityY = velocityY
        if (!mAppBarTracking!!.isAppBarIdle()) {
            val vc = ViewConfiguration.get(context)
            velocityY = if (velocityY < 0) -vc.scaledMinimumFlingVelocity
            else vc.scaledMinimumFlingVelocity
        }

        return super.fling(velocityX, velocityY)
    }
}

MyAppBarBehavior.java

/**
 * Attach this behavior to AppBarLayout to disable the bottom portion of a closed appBar
 * so it cannot be touched to open the appBar. This behavior is helpful if there is some
 * portion of the appBar that displays when the appBar is closed, but should not open the appBar
 * when the appBar is closed.
 */
public class MyAppBarBehavior extends AppBarLayout.Behavior {

    // Touch above this y-axis value can open the appBar.
    private int mCanOpenBottom;

    // Determines if the appBar can be dragged open or not via direct touch on the appBar.
    private boolean mCanDrag = true;

    @SuppressWarnings("unused")
    public MyAppBarBehavior() {
        init();
    }

    @SuppressWarnings("unused")
    public MyAppBarBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        setDragCallback(new AppBarLayout.Behavior.DragCallback() {
            @Override
            public boolean canDrag(@NonNull AppBarLayout appBarLayout) {
                return mCanDrag;
            }
        });
    }

    @Override
    public boolean onInterceptTouchEvent(CoordinatorLayout parent,
                                         AppBarLayout child,
                                         MotionEvent event) {

        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            // If appBar is closed. Only allow scrolling in defined area.
            if (child.getTop() <= -child.getTotalScrollRange()) {
                mCanDrag = event.getY() < mCanOpenBottom;
            }
        }
        return super.onInterceptTouchEvent(parent, child, event);
    }

    public void setCanOpenBottom(int bottom) {
        mCanOpenBottom = bottom;
    }
}

这篇关于如何将顶视图缩小为较小的视图?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆