ViewPager 在 Coordinator 布局中的高度超出了可用范围

问题描述 投票:0回答:5

我有一个

CoordinatorLayout
,其中有
Toolbar
TabLayout
内的
AppBarLayout
。此外,我在
ViewPager
内部但在
CoordinatorLayout
外部有一个
ViewPager

问题是

ViewPager's
的高度比实际可用的要大,导致我的
Fragment
的一些视图被剪掉。

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/lightGray"
    android:fitsSystemWindows="true">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar2"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:popupTheme="@style/AppTheme.PopupOverlay"
            app:layout_scrollFlags="scroll|enterAlways"/>

        <android.support.design.widget.TabLayout
            android:id="@+id/tabLayout"
            android:scrollbars="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:fillViewport="false"/>

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

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

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

这就是我说

ViewPager
高度错误时的意思。
enter image description here

android android-layout android-viewpager android-coordinatorlayout
5个回答
14
投票

UDPATE:我不再推荐这个了。我发现它导致无限循环调用

onDependentViewChanged

根据上面 Yevhenii 的回答,我想我设法以更简单的方式解决了这个问题:

class KeepWithinParentBoundsScrollingBehavior : AppBarLayout.ScrollingViewBehavior {

    constructor() : super()

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)


    override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
        if (dependency !is AppBarLayout) {
            return super.onDependentViewChanged(parent, child, dependency)
        }

        val layoutParams = child.layoutParams as CoordinatorLayout.LayoutParams
        layoutParams.height = parent.height - dependency.bottom
        child.layoutParams = layoutParams
        return super.onDependentViewChanged(parent, child, dependency)
    }
}

然后将

app:layout_behavior="your.package.KeepWithinParentBoundsScrollingBehavior"
设置在您的
ViewPager
AppBar
下面的任何视图上。

请注意,这并不是适用于所有

CoordinatorLayout
的通用解决方案,但当您在应用程序栏下方有一个视图,而您不想让它延伸到父 CoordinatorLayout 的底部之外时,它似乎可以工作。

更新: 您还应该在 ViewPager 上设置

app:layout_anchor="@id/app_bar"
,以应对键盘消失的情况。如果不这样做,当键盘消失时,
ViewPager
布局将不会刷新,并且
ViewPager
将显示为被切断。


10
投票

我花了很多时间谷歌搜索并应用建议的答案,但我没有成功,所以决定更深入地研究这个问题并找到一个完整的答案,我想在这里分享。也许它会对某人有所帮助。

所以问题是,当 ViewPagerCollapsing Toolbar 一起使用时,第一个无法正确计算其高度。如果您希望 ViewPager 填充屏幕底部的所有空间,但不是更多 - 那就有问题了。仅添加

layout_marginBottom
并不能解决问题,因为当用户滚动时,折叠工具栏会改变其高度。

因此,如果您希望 ViewPager 相应地调整其高度以适应 Collapsing Toolbar 高度变化,您需要两件事:

  1. 自定义滚动行为。
  2. GlobalLayoutListener,它将在第一次绘制时修复ViewPager的高度。

它们是(用 Kotlin 编写的,但它只是一个单独的文件,可以直接放入 Java 项目中):

import android.app.Activity
import android.content.Context
import android.graphics.Point
import android.support.design.widget.AppBarLayout
import android.support.design.widget.CoordinatorLayout
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver

/**
 * Custom extension of AppBarLayout.ScrollingViewBehavior to deal with ViewPager height
 * in a bunch with Collapsing Toolbar Layout. Works dynamically when AppBar Layout height is changing.
 */
class ViewPagerScrollingBehavior(context: Context, attributeSet: AttributeSet? = null) :
  AppBarLayout.ScrollingViewBehavior(context, attributeSet) {

  override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
    val layoutParams = child.layoutParams as CoordinatorLayout.LayoutParams
    layoutParams.height = child.height - (dependency.bottom - child.top)
    child.layoutParams = layoutParams
    child.requestLayout()
    return super.onDependentViewChanged(parent, child, dependency)
  }

}

/**
 * Custom implementation of ViewTreeObserver.OnGlobalLayoutListener to fix the View height
 * in a bunch with Collapsing Toolbar Layout. Works when View is drawn on the screen for first time.
 * To be used with ViewPagerScrollingBehavior.
 */
class FixHeightGlobalLayoutListener(val activity: Activity, val view: View) : ViewTreeObserver.OnGlobalLayoutListener {

  override fun onGlobalLayout() {
    val display = activity.windowManager.defaultDisplay
    val size = Point()
    display.getSize(size)
    val height = size.y

    val location = IntArray(2)
    view.getLocationOnScreen(location)

    view.post {
      val layoutParams = view.layoutParams as ViewGroup.LayoutParams
      layoutParams.height = height - location[1]
      view.layoutParams = layoutParams
      view.requestLayout()
    }

    view.viewTreeObserver.removeOnGlobalLayoutListener(this)
  }

}

并在您的代码中使用它们:

  1. 将自定义行为添加到您的ViewPager

    app:layout_behavior="your.package.ViewPagerScrollingBehavior"

  2. 将自定义 OnGlobalLayoutListener 添加到您的 ViewPager

    viewPager.getViewTreeObserver().addOnGlobalLayoutListener(new FixHeightGlobalLayoutListener(this, viewPager));


7
投票

一个可能的黑客可以添加与工具栏相同的下边距,即

?attr/actionBarSize。您甚至可以尝试其他可能的 ui 边距修改,以获得最佳结果。


0
投票

您不应将行为类型“appbar_scrolling_view_behavior”设置为 ViewPager。但将其设置为外部布局。类似下面的内容

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    >
    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/tabbed_fragments_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />
</RelativeLayout>

0
投票

由于不再推荐最多支持的解决方案,我找到了另一个建议。引用原作者的话:

我只是更新应保持固定的视图的底部填充,例如就像下面这样:

binding.appbar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset ->
    // llBottomContent is my view that should be resized instead of moved
    binding.llBottomContent.updatePadding(bottom = appBarLayout.totalScrollRange+ verticalOffset)
})
© www.soinside.com 2019 - 2024. All rights reserved.