【问题标题】:Is it possible to easily center the content of the view below AppBarLayout, inside CoordinatorLayout?是否可以在 CoordinatorLayout 内轻松地将 AppBarLayout 下方的视图内容居中?
【发布时间】:2017-07-27 13:48:00
【问题描述】:

背景

假设您有一个滚动活动,其中包含 CoordinatorLayout 和 AppBarLayout,其中您有一个顶部标题,可以在底部滚动时折叠(可能有 RecyclerView 或 NestedScrollView)。

类似这样的:

现在,您需要在内容出现之前,在底部区域(显示文本的位置)的中心放置一个加载视图(例如,一个 ProgressBar 和一个 LinearLayout 内的 TextView)。

问题

我使用 ViewAnimator 在状态之间切换,但是如果我选择将加载视图居中,它会显示在中心下方,因为底部区域占用了更多实际显示的空间(因为您可以滚动它)。

我尝试了什么

我执行的 2 个解决方案效果很好: 1.获取加载视图及其父级的大小,并用它计算加载视图的上边距 2. 将加载视图的底部填充设置为上部区域大小的一半(本例中为 90dp,即上部区域 180dp 的一半)。这有点容易,但如果 Activity 中的任何 UI 或底部区域的父级发生更改,它将破坏位于中心的修复。

代码

与 IDE 向导中的几乎相同,但在这里:

ScrollingActivity.java

public class ScrollingActivity extends AppCompatActivity {
    private ViewAnimator mViewAnimator;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scrolling);
        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
        mViewAnimator = findViewById(R.id.viewAnimator);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_scrolling, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            mViewAnimator.showNext();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

res/layout/activity_scrolling.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:fitsSystemWindows="true"
    tools:context="com.example.user.myapplication.ScrollingActivity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar" android:layout_width="match_parent" android:layout_height="@dimen/app_bar_height"
        android:fitsSystemWindows="true" android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/toolbar_layout" android:layout_width="match_parent" android:layout_height="match_parent"
            android:fitsSystemWindows="true" app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed" app:toolbarId="@+id/toolbar">

            <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.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>

    <ViewAnimator
        android:id="@+id/viewAnimator" android:layout_width="match_parent" android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <LinearLayout
            android:id="@+id/loader" android:layout_width="match_parent" android:layout_height="match_parent"
            android:gravity="center" android:orientation="vertical">

            <ProgressBar
                android:layout_width="wrap_content" android:layout_height="wrap_content"/>

            <TextView
                android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="loading"/>

        </LinearLayout>

        <android.support.v4.widget.NestedScrollView
            android:id="@+id/contentView" android:layout_width="match_parent" android:layout_height="match_parent">

            <TextView
                android:layout_width="wrap_content" android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin" android:text="@string/large_text"/>

        </android.support.v4.widget.NestedScrollView>
    </ViewAnimator>

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

res/menu/menu_scrolling.xml

<menu 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" tools:context="com.example.user.myapplication.ScrollingActivity">
    <item
        android:id="@+id/action_settings" android:orderInCategory="100" android:title="click"
        app:showAsAction="always"/>
</menu>

问题

问题是,我可以自己计算,但我想知道是否有其他简单的方法可以做到这一点。也许是某种我不知道的标志?

我问这个是因为实际应用程序的底部区域更复杂(上面的示例用于演示问题),有一个片段甚至是带有多个片段的 TabLayout&ViewPager,所以计算父级的东西有点奇怪用于此目的的这些片段中的活动。

不仅如此,我还在底部有一个tabLayout(属于活动),这使得考虑起来更加烦人。

【问题讨论】:

  • 我没有检查代码,因为我很着急……你使用了重力属性/layout_centerVertical 吗?或 layout_centerInParent ?抱歉,您的回复很垃圾..
  • @IonutJ.Bejan 是的,它在上面的代码中。居中是整个布局,只有滚动后才能看到整体(上部区域的折叠模式),所以你会看到我在第二张截图中得到了什么。

标签: android android-collapsingtoolbarlayout android-appbarlayout


【解决方案1】:

尝试自定义行为。

class ViewBelowAppBarBehavior @JvmOverloads constructor(
        context: Context? = null, attrs: AttributeSet? = null) : CoordinatorLayout.Behavior<View>(context, attrs) {

    override fun layoutDependsOn(parent: CoordinatorLayout?, child: View?, dependency: View?) = dependency is AppBarLayout

    override fun onDependentViewChanged(parent: CoordinatorLayout?, child: View?, dependency: View?): Boolean {
        val appBar = dependency as AppBarLayout
        // т.к. y - отрицательное число
        val currentAppBarHeight = appBar.height + appBar.y 
        val parentHeight = parent?.height ?: 0
        val placeHolderHeight = (parentHeight - currentAppBarHeight).toInt()
        child?.layoutParams?.height = placeHolderHeight
        child?.requestLayout()
        return false
    }
}

【讨论】:

  • 看起来很有希望?能否请您在 github 上发布一个 POC,以便我可以尝试一下?
【解决方案2】:

我认为从 ViewAnimator 中删除 app:layout_behavior="@string/appbar_scrolling_view_behavior" 应该可以解决问题。 或者,如果在没有行为的情况下将 ViewAnimator 包装在 RelativeLayout 中不起作用,以便 ViewGroup 位于 AppBarLayout 后面,因此您的视图将居中。

更新 1(不工作)

测试了我对 2 个视图的建议,它奏效了:

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:fitsSystemWindows="true"
tools:context="com.example.user.myapplication.ScrollingActivity">

<android.support.design.widget.AppBarLayout
    android:id="@+id/app_bar" android:layout_width="match_parent" android:layout_height="@dimen/app_bar_height"
    android:fitsSystemWindows="true" android:theme="@style/AppTheme.AppBarOverlay">

    <android.support.design.widget.CollapsingToolbarLayout
        android:id="@+id/toolbar_layout" android:layout_width="match_parent" android:layout_height="match_parent"
        android:fitsSystemWindows="true" app:contentScrim="?attr/colorPrimary"
        app:layout_scrollFlags="scroll|exitUntilCollapsed" app:toolbarId="@+id/toolbar">

        <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.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>

<android.support.v4.widget.NestedScrollView
    android:id="@+id/contentView" android:layout_width="match_parent" android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <TextView
        android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:layout_margin="@dimen/text_margin" android:text="@string/large_text"/>

</android.support.v4.widget.NestedScrollView>

<LinearLayout
    android:id="@+id/loader" android:layout_width="match_parent" android:layout_height="match_parent"
    android:gravity="center" android:orientation="vertical" android:visibility="gone">

    <ProgressBar
        android:layout_width="wrap_content" android:layout_height="wrap_content"/>

    <TextView
        android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="loading"/>

</LinearLayout>

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

只需根据您的逻辑在您的视图中交换 @+id/loader 和 @+id/contentView 的可见性,这应该可以工作,但遗憾的是您错过了 ViewAnimator 的动画。

更新 2

我完全用这段代码测试了它(只是改变了你的安卓自定义样式),它使加载器在整个视图中居中。只需尝试在“@+id/loader”中添加/删除app:layout_behavior="@string/appbar_scrolling_view_behavior",这样您就可以看到加载器的位置发生了变化:

对于行为,它以 AppBarLayout 下方的区域为中心。

没有它,它在屏幕上居中。

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools" 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.user.myapplication.ScrollingActivity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar" 
        android:layout_width="match_parent" 
        android:layout_height="180dp" 
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/toolbar_layout" 
            android:layout_width="match_parent" 
            android:layout_height="match_parent"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed" 
            app:toolbarId="@+id/toolbar">

            <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/ThemeOverlay.AppCompat.Light"/>

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

    <android.support.v4.widget.NestedScrollView
        android:id="@+id/contentView" 
        android:layout_width="match_parent" 
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <TextView
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content"
            android:layout_margin="10dp" 
            android:text="@string/large_text"/>

    </android.support.v4.widget.NestedScrollView>

    <LinearLayout
        android:id="@+id/loader" 
        android:layout_width="match_parent" 
        android:layout_height="match_parent"
        android:gravity="center" 
        android:orientation="vertical" 
        android:visibility="gone">

        <ProgressBar
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content"/>

        <TextView
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content" 
            android:text="loading"/>

    </LinearLayout>

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

更新 3

显然,当 CollapsingToolbarLayout 被扩展时,它会将其下方的视图向下推(如果它有app:layout_behavior="@string/appbar_scrolling_view_behavior")同时考虑到内部工具栏的高度,这就是为什么当它在在这种状态下,该视图的内容未居中。

因此,在这种情况下,解决此问题的一种方法是,我们想要居中的视图具有透明背景并且仅占据区域的中心,是利用 app:behavior_overlapTop / setOverlayTop(int) 并将该视图与 AppBarLayout 重叠,高度为工具栏(?attr/actionBarSize):

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.user.myapplication.ScrollingActivity">
    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/toolbar_layout"
            android:layout_width="match_parent"
            android:layout_height="180dp"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
            app:toolbarId="@+id/toolbar">
            <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/ThemeOverlay.AppCompat.Light" />
        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>
    <android.support.v4.widget.NestedScrollView
        android:id="@+id/contentView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:text="@string/large_text" />
    </android.support.v4.widget.NestedScrollView>
    <LinearLayout
        android:id="@+id/loader"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical"
        android:visibility="gone"
        app:behavior_overlapTop="?attr/actionBarSize"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">
        <ProgressBar
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content"/>

        <TextView
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content" 
            android:text="loading"/>
    </LinearLayout>
</android.support.design.widget.CoordinatorLayout>

希望对你有帮助

【讨论】:

  • 不起作用,而且 ViewAnimatior 中的视图的滚动效果不佳,因为内容(本示例中的文本)将与顶部区域一起显示。该行为必须存在于 CoordinatorLayout 的子项上,这样才能正常工作。我试过了:(
  • 您是否尝试将行为移至 NestedScrollView?
  • 是的,很遗憾,它被忽略了。
  • 现在可以了,但是很遗憾因为我们有内部片段和视图,并且我们使用 ViewSwitcher(或 ViewAnimator)在视图之间切换,这是不可能使用的。无论如何,我现在会接受这个答案,因为对于简单的情况以及更复杂的情况下的计算,我认为没有比这更好的解决方案了。
  • 是的,可以使用 setOverlayTop(...),这里有 stackoverflow.com/a/31039075/6248510
【解决方案3】:

对@Yarh 的回答进行更详细的解释,非常完美:

首先创建您自己的布局行为(在我的情况下,我从 AppBarLayout.ScrollingViewBehavior 派生以保留原始行为)

class AdaptiveHeightScrollingViewBehavior(
    context: Context,
    attrs: AttributeSet
) : AppBarLayout.ScrollingViewBehavior(context, attrs) {

    override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
        (dependency as? AppBarLayout)?.let { appBar ->
            val appBarHeight = appBar.height + appBar.y
            child.layoutParams.height = (parent.height - appBarHeight).toInt()
            child.requestLayout()
        }
        return super.onDependentViewChanged(parent, child, dependency)
    }
}

然后在你的布局中使用你的 ViewPager/FragmentContainer/ViewGroup 或其他的行为,只要确保它是 AppBarLayout 的兄弟

<androidx.viewpager.widget.ViewPager
    android:id="@+id/viewPager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="com.sample.behavior.AdaptiveHeightScrollingViewBehavior" />

【讨论】:

  • 如果你用它快速滚动它会产生不必要的效果,例如一个巨大的空白重叠视图
猜你喜欢
  • 1970-01-01
  • 2015-12-27
  • 2015-10-13
  • 1970-01-01
  • 2015-09-18
  • 2016-08-18
  • 1970-01-01
  • 2015-12-22
  • 2015-11-15
相关资源
最近更新 更多