gpt4 book ai didi

android - 如何将顶 View 折叠成较小尺寸的 View ?

转载 作者:塔克拉玛干 更新时间:2023-11-02 09:06:04 24 4
gpt4 key购买 nike

这个问题之前问的太笼统,不清楚here ,所以我已经使它更加具体,并提供了我尝试过的内容的完整解释和代码。

背景

我需要模仿 Google 日历在顶部有一个 View 的方式,它可以设置动画并向下推底部的 View ,但它有额外的不同行为。我总结了我正在尝试做的 3 个特征:

  • 按下工具栏上的总是有效的,可以切换顶部 View 的展开/折叠,同时有一个改变其旋转的箭头图标。这就像在 Google 日历应用上一样。
  • 顶 View 将始终对齐,就像在 Google 日历应用中一样。
  • 当顶 View 折叠时,只有按下工具栏才能展开它。这就像 Google 日历应用程序
  • 当顶 View 展开时,在底 View 滚动将只允许折叠。如果您尝试向另一个方向滚动,则不会发生任何事情,甚至不会滚动到底部 View 。这就像 Google 日历应用程序
  • 折叠后,顶 View 将替换为较小的 View 。这意味着它总是会在底部 View 上方占据一些空间。这与 Google 日历应用程序不同,因为在日历应用程序中,一旦折叠顶部 View ,它就会完全消失。

  • Google 日历应用程序如下所示:

    enter image description here

    在底部 View 上滚动也会慢慢隐藏顶部的 View :

    enter image description here

    问题

    使用我过去发现的各种解决方案,我成功地仅实现了所需行为的一部分:
  • 在工具栏中有一些 UI 是通过在其中包含一些 View 来完成的,包括箭头 View 。对于手动展开/折叠,我使用 setExpandedAppBarLayout看法。对于箭头的旋转,我使用了 AppBarLayout 的监听器。已调整大小,使用 addOnOffsetChangedListener在上面。
  • 通过添加 snap 可以轻松完成捕捉值(value)成layout_scrollFlags CollapsingToolbarLayout 的属性.然而,为了让它真正工作得很好,没有奇怪的问题(报告 here ),我使用了 this solution .
  • 可以通过调用 setExpandEnabled 使用我在 #2 ( here ) 上使用的相同代码来阻止滚动时影响顶 View 。那里。
    这适用于顶 View 折叠时。
  • 与#3 类似,但遗憾的是,因为它使用 setNestedScrollingEnabled ,这是在两个方向上,这仅在顶 View 折叠时才有效。当它展开时,它仍然允许底部 View 向上滚动,而不是日历应用程序。展开时,我需要它只允许折叠,而不允许真正滚动。

  • 这是好的和坏的示范:

    enter image description here
  • 这是我完全没有做到的。我尝试了很多我想过的解决方案,将 View 放置在带有各种标志的不同位置。

  • 简而言之,我成功地做到了 1-3,而不是 4-5。

    编码

    这是当前代码(也可作为整个项目 here ):

    滚动 Activity .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>

    问题
  • 如何在展开顶部 View 时阻止滚动,但允许在滚动时折叠?
  • 如何在折叠时将顶 View 替换为较小的 View (展开时返回大 View ),而不是完全消失?


  • 更新

    尽管我已经了解了我所询问的基本内容,但当前代码仍然存在 2 个问题(可在 Github 上获得, here ):
  • 小 View (您在折叠状态下看到的那个)具有需要对它们产生点击效果的内部 View 。使用 android:background="?attr/selectableItemBackgroundBorderless" 时在它们上,并在展开时单击此区域,单击是在小 View 上完成的。我已经通过将小 View 放在不同的工具栏上来处理它,但是点击效果根本没有显示。我写过这个 here ,包括示例项目。

  • 这是修复:
    <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>
  • Google 日历允许在工具栏本身上执行向下滚动手势,以触发显示月 View 。我只成功地在那里添加了一个点击事件,但没有滚动。这是它的样子:

  • enter image description here

    最佳答案

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

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



    问题 #1: RecyclerView当应用栏未折叠时,根本不应该滚动。要解决此问题,请添加 enterAlwaysCollapsingToolbarLayout 的滚动标志如下:
    <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);
    }
    }

    这是测试应用程序对上述更改的行为方式:

    enter image description here

    有上述改动的改动模块在文末。

    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 ?



    [更新] 使较小的 View 成为 CollapsingToolbarLayout 的直接 subview 所以它是 Toolbar的兄弟.下面是这种方法的演示。 collapseMode较小 View 的设置为 pin .调整较小 View 的边距以及工具栏的边距,以便较小的 View 直接位于工具栏下方。自 CollapsingToolbarLayoutFrameLayout , View 堆栈和高度 FrameLayout只是成为最高 subview 的高度。这种结构将避免插入需要调整的问题和缺少点击效果的问题。

    最后一个问题仍然存在,向下拖动应用栏应该打开它,假设向下拖动较小的 View 不应该打开应用栏。允许应用栏在拖动时打开是通过 setDragCallback 完成的的 AppBarLayout.Behavior .由于较小的 View 已合并到 appBar 中,因此将其向下拖动将打开 appbar。为了防止这种情况,一个名为 MyAppBarBehavior 的新行为附加到应用栏。此行为与 MainActivity 中的代码一起使用阻止拖动较小的 View 以打开应用程序栏,但允许拖动工具栏。

    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>

    最后,在 addOnOffsetChangedListener添加以下代码以在应用栏扩展和收缩时在较小的 View 中淡出/淡出。一旦 View 的 alpha 为零(不可见),将其可见性设置为 View.INVISIBLE所以无法点击。一旦 View 的 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);
    }

    结果如下:

    enter image description here

    以下是包含上述所有更改的新模块。

    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;
    }
    }

    关于android - 如何将顶 View 折叠成较小尺寸的 View ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47856522/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com