Android - 工具栏在列表视图滚动时高于状态栏

Android - Toolbar goes above Statusbar on Listview scroll

我试图在滚动列表视图时使用协调器布局隐藏工具栏。我在我的工具栏上使用了 app:layout_scrollFlags="scroll|enterAlways",在我的列表视图中使用了 android:nestedScrollingEnabled="true"

当我向下滚动我的列表视图时,工具栏正确隐藏,但 'above' 状态栏。在网络开发术语中,看起来工具栏的 'Z-Index' 高于状态栏。这是它的样子 -

这是我的activity_main.xml

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/main_content"
    android:layout_width="match_parent" android:layout_height="match_parent"
    android:fitsSystemWindows="true" tools:context=".MainActivity"
    android:background="#FFFFFF" >

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

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

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

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

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

这是我的 fragment_main.xml 代码

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context=".MainActivity$PlaceholderFragment">

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollingCache="false"
        android:nestedScrollingEnabled="true" />

</RelativeLayout>

如何解决这个问题?提前致谢!

更新:我已经在下面发布了我的答案!检查已接受的答案。

如果您不想要可扩展和收缩的工具栏,为什么要使用 AppBarlayouttoolbar 布局?基本上 AppBarlayout 是在人们希望它扩展和收缩时使用的,它与 android.support.v4.widget.NestedScrollView 一起使用,所以问题很可能来自那里,它们不兼容,你可以使用 android.support.v7.widget.RecyclerViewin listviewviewpager 的位置具有触发 appbarlayout 开始侦听滚动输入的属性 appbar_scrolling_view_behavior,我通过没有 AppBarlayout 但只有 toolbar 所以你可以删除 AppBarlayout,看看它不是真正需要的东西,因为你碰巧希望它扩展和收缩下面是你的代码应该是什么样子的示例

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_height="match_parent"
android:layout_width="match_parent">




    <android.support.v7.widget.RecyclerView
    android:id="@+id/scrollableview"
    android:layout_width="match_parent"
    android:visibility="gone"
    android:background="#FFFFFF"
    android:paddingTop="?attr/actionBarSize"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    <!-- Your content, maybe a ListView? -->
    <android.support.design.widget.FloatingActionButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="@dimen/fab_margin"
    android:clickable="true"
    android:id="@+id/fabAddItem"
    android:src="@drawable/ic_action_add"
    app:backgroundTint="@color/google_lightblue"
    app:layout_anchor="@+id/toolbar"
    app:layout_anchorGravity="bottom|right|end" />

    <aubry.chromio.com.dressup.view.CollapsingTitleLayout
    android:id="@+id/backdrop_toolbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textAppearance="@style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse"
    app:expandedTextSize="40dp"
    app:expandedMargin="16dp">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_height="?attr/actionBarSize"
        android:layout_width="match_parent" />

</aubry.chromio.com.dressup.view.CollapsingTitleLayout>

</FrameLayout>

这是我的 CollapingsTitleLayout

 import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.Bitmap;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Rect;
    import android.os.Build;
    import android.support.v4.view.ViewCompat;
    import android.support.v7.widget.Toolbar;
    import android.text.TextPaint;
    import android.text.TextUtils;
    import android.util.AttributeSet;
    import android.util.DisplayMetrics;
    import android.util.TypedValue;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.animation.AnimationUtils;
    import android.view.animation.Interpolator;
    import android.widget.FrameLayout;

    import aubry.chromio.com.dressup.R;


 public class CollapsingTitleLayout extends FrameLayout {

// Pre-JB-MR2 doesn't support HW accelerated canvas scaled text so we will workaround it
// by using our own texture
private static final boolean USE_SCALING_TEXTURE = Build.VERSION.SDK_INT < 18;

private static final boolean DEBUG_DRAW = false;
private static final Paint DEBUG_DRAW_PAINT;
static {
    DEBUG_DRAW_PAINT = DEBUG_DRAW ? new Paint() : null;
    if (DEBUG_DRAW_PAINT != null) {
        DEBUG_DRAW_PAINT.setAntiAlias(true);
        DEBUG_DRAW_PAINT.setColor(Color.MAGENTA);
    }
}

private Toolbar mToolbar;
private View mDummyView;

private float mScrollOffset;

private final Rect mToolbarContentBounds;

private float mExpandedMarginLeft;
private float mExpandedMarginRight;
private float mExpandedMarginBottom;

private int mRequestedExpandedTitleTextSize;
private int mExpandedTitleTextSize;
private int mCollapsedTitleTextSize;

private float mExpandedTop;
private float mCollapsedTop;

private String mTitle;
private String mTitleToDraw;
private boolean mUseTexture;
private Bitmap mExpandedTitleTexture;

private float mTextLeft;
private float mTextRight;
private float mTextTop;

private float mScale;

private final TextPaint mTextPaint;
private Paint mTexturePaint;

private Interpolator mTextSizeInterpolator;

public CollapsingTitleLayout(Context context) {
    this(context, null);
}

public CollapsingTitleLayout(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

public CollapsingTitleLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);

    mTextPaint = new TextPaint();
    mTextPaint.setAntiAlias(true);

    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CollapsingTitleLayout);

    mExpandedMarginLeft = mExpandedMarginRight = mExpandedMarginBottom =
            a.getDimensionPixelSize(R.styleable.CollapsingTitleLayout_expandedMargin, 0);

    final boolean isRtl = ViewCompat.getLayoutDirection(this)
            == ViewCompat.LAYOUT_DIRECTION_RTL;
    if (a.hasValue(R.styleable.CollapsingTitleLayout_expandedMarginStart)) {
        final int marginStart = a.getDimensionPixelSize(
                R.styleable.CollapsingTitleLayout_expandedMarginStart, 0);
        if (isRtl) {
            mExpandedMarginRight = marginStart;
        } else {
            mExpandedMarginLeft = marginStart;
        }
    }
    if (a.hasValue(R.styleable.CollapsingTitleLayout_expandedMarginEnd)) {
        final int marginEnd = a.getDimensionPixelSize(
                R.styleable.CollapsingTitleLayout_expandedMarginEnd, 0);
        if (isRtl) {
            mExpandedMarginLeft = marginEnd;
        } else {
            mExpandedMarginRight = marginEnd;
        }
    }
    if (a.hasValue(R.styleable.CollapsingTitleLayout_expandedMarginBottom)) {
        mExpandedMarginBottom = a.getDimensionPixelSize(
                R.styleable.CollapsingTitleLayout_expandedMarginBottom, 0);
    }

    final int tp = a.getResourceId(R.styleable.CollapsingTitleLayout_android_textAppearance,
            android.R.style.TextAppearance);
    setTextAppearance(tp);

    if (a.hasValue(R.styleable.CollapsingTitleLayout_collapsedTextSize)) {
        mCollapsedTitleTextSize = a.getDimensionPixelSize(
                R.styleable.CollapsingTitleLayout_collapsedTextSize, 0);
    }

    mRequestedExpandedTitleTextSize = a.getDimensionPixelSize(
            R.styleable.CollapsingTitleLayout_expandedTextSize, mCollapsedTitleTextSize);

    final int interpolatorId = a
            .getResourceId(R.styleable.CollapsingTitleLayout_textSizeInterpolator,
                    android.R.anim.accelerate_interpolator);
    mTextSizeInterpolator = AnimationUtils.loadInterpolator(context, interpolatorId);

    a.recycle();

    mToolbarContentBounds = new Rect();

    setWillNotDraw(false);
}

public void setTextAppearance(int resId) {
    TypedArray atp = getContext().obtainStyledAttributes(resId,
            R.styleable.CollapsingTextAppearance);
    mTextPaint.setColor(atp.getColor(
            R.styleable.CollapsingTextAppearance_android_textColor, Color.WHITE));
    mCollapsedTitleTextSize = atp.getDimensionPixelSize(
            R.styleable.CollapsingTextAppearance_android_textSize, 0);
    atp.recycle();

    recalculate();
}

@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
    super.addView(child, index, params);

    if (child instanceof Toolbar) {
        mToolbar = (Toolbar) child;
        mDummyView = new View(getContext());
        mToolbar.addView(mDummyView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    }
}

/**
 * Set the value indicating the current scroll value. This decides how much of the
 * background will be displayed, as well as the title metrics/positioning.
 *
 * A value of {@code 0.0} indicates that the layout is fully expanded.
 * A value of {@code 1.0} indicates that the layout is fully collapsed.
 */
public void setScrollOffset(float offset) {
    if (offset != mScrollOffset) {
        mScrollOffset = offset;
        calculateOffsets();
    }
}

private void calculateOffsets() {
    final float offset = mScrollOffset;
    final float textSizeOffset = mTextSizeInterpolator != null
            ? mTextSizeInterpolator.getInterpolation(mScrollOffset)
            : offset;

    mTextLeft = interpolate(mExpandedMarginLeft, mToolbarContentBounds.left, offset);
    mTextTop = interpolate(mExpandedTop, mCollapsedTop, offset);
    mTextRight = interpolate(getWidth() - mExpandedMarginRight, mToolbarContentBounds.right, offset);

    setInterpolatedTextSize(
            interpolate(mExpandedTitleTextSize, mCollapsedTitleTextSize, textSizeOffset));

    ViewCompat.postInvalidateOnAnimation(this);
}

private void calculateTextBounds() {
    final DisplayMetrics metrics = getResources().getDisplayMetrics();

    // We then calculate the collapsed text size, using the same logic
    mTextPaint.setTextSize(mCollapsedTitleTextSize);
    float textHeight = mTextPaint.descent() - mTextPaint.ascent();
    float textOffset = (textHeight / 2) - mTextPaint.descent();
    mCollapsedTop = mToolbarContentBounds.centerY() + textOffset;

    // First, let's calculate the expanded text size so that it fit within the bounds
    // We make sure this value is at least our minimum text size
    mExpandedTitleTextSize = (int) Math.max(mCollapsedTitleTextSize,
            getSingleLineTextSize(mTitle, mTextPaint,
                    getWidth() - mExpandedMarginLeft -mExpandedMarginRight, 0f,
                    mRequestedExpandedTitleTextSize, 0.5f, metrics));
    mExpandedTop = getHeight() - mExpandedMarginBottom;

    // The bounds have changed so we need to clear the texture
    clearTexture();
}

@Override
public void draw(Canvas canvas) {
    final int saveCount = canvas.save();

    final int toolbarHeight = mToolbar.getHeight();
    canvas.clipRect(0, 0, canvas.getWidth(),
            interpolate(canvas.getHeight(), toolbarHeight, mScrollOffset));

    // Now call super and let it draw the background, etc
    super.draw(canvas);

    if (mTitleToDraw != null) {
        float x = mTextLeft;
        float y = mTextTop;

        final float ascent = mTextPaint.ascent() * mScale;
        final float descent = mTextPaint.descent() * mScale;
        final float h = descent - ascent;

        if (DEBUG_DRAW) {
            // Just a debug tool, which drawn a Magneta rect in the text bounds
            canvas.drawRect(mTextLeft,
                    y - h + descent,
                    mTextRight,
                    y + descent,
                    DEBUG_DRAW_PAINT);
        }

        if (mUseTexture) {
            y = y - h + descent;
        }

        if (mScale != 1f) {
            canvas.scale(mScale, mScale, x, y);
        }

        if (mUseTexture && mExpandedTitleTexture != null) {
            // If we should use a texture, draw it instead of text
            canvas.drawBitmap(mExpandedTitleTexture, x, y, mTexturePaint);
        } else {
            canvas.drawText(mTitleToDraw, x, y, mTextPaint);
        }
    }

    canvas.restoreToCount(saveCount);
}

private void setInterpolatedTextSize(final float textSize) {
    if (mTitle == null) return;

    if (isClose(textSize, mCollapsedTitleTextSize) || isClose(textSize, mExpandedTitleTextSize)
            || mTitleToDraw == null) {
        // If the text size is 'close' to being a decimal, then we use this as a sync-point.
        // We disable our manual scaling and set the paint's text size.
        mTextPaint.setTextSize(textSize);
        mScale = 1f;

        // We also use this as an opportunity to ellipsize the string
        final CharSequence title = TextUtils.ellipsize(mTitle, mTextPaint,
                mTextRight - mTextLeft,
                TextUtils.TruncateAt.END);
        if (title != mTitleToDraw) {
            // If the title has changed, turn it into a string
            mTitleToDraw = title.toString();
        }

        if (USE_SCALING_TEXTURE && isClose(textSize, mExpandedTitleTextSize)) {
            ensureExpandedTexture();
        }
        mUseTexture = false;
    } else {
        // We're not close to a decimal so use our canvas scaling method
        if (mExpandedTitleTexture != null) {
            mScale = textSize / mExpandedTitleTextSize;
        } else {
            mScale = textSize / mTextPaint.getTextSize();
        }

        mUseTexture = USE_SCALING_TEXTURE;
    }

    ViewCompat.postInvalidateOnAnimation(this);
}

private void ensureExpandedTexture() {
    if (mExpandedTitleTexture != null) return;

    int w = (int) (getWidth() - mExpandedMarginLeft - mExpandedMarginRight);
    int h = (int) (mTextPaint.descent() - mTextPaint.ascent());

    mExpandedTitleTexture = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);

    Canvas c = new Canvas(mExpandedTitleTexture);
    c.drawText(mTitleToDraw, 0, h - mTextPaint.descent(), mTextPaint);

    if (mTexturePaint == null) {
        // Make sure we have a paint
        mTexturePaint = new Paint();
        mTexturePaint.setAntiAlias(true);
        mTexturePaint.setFilterBitmap(true);
    }
}

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);

    mToolbarContentBounds.left = mDummyView.getLeft();
    mToolbarContentBounds.top = mDummyView.getTop();
    mToolbarContentBounds.right = mDummyView.getRight();
    mToolbarContentBounds.bottom = mDummyView.getBottom();

    if (changed && mTitle != null) {
        // If we've changed and we have a title, re-calculate everything!
        recalculate();
    }
}

private void recalculate() {
    if (getHeight() > 0) {
        calculateTextBounds();
        calculateOffsets();
    }
}

/**
 * Set the title to display
 *
 * @param title
 */
public void setTitle(String title) {
    if (title == null || !title.equals(mTitle)) {
        mTitle = title;

        clearTexture();

        if (getHeight() > 0) {
            // If we've already been laid out, calculate everything now otherwise we'll wait
            // until a layout
            recalculate();
        }
    }
}

private void clearTexture() {
    if (mExpandedTitleTexture != null) {
        mExpandedTitleTexture.recycle();
        mExpandedTitleTexture = null;
    }
}

/**
 * Recursive binary search to find the best size for the text
 *
 * Adapted from https://github.com/grantland/android-autofittextview
 */
private static float getSingleLineTextSize(String text, TextPaint paint, float targetWidth,
                                           float low, float high, float precision, DisplayMetrics metrics) {
    final float mid = (low + high) / 2.0f;

    paint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, mid, metrics));
    final float maxLineWidth = paint.measureText(text);

    if ((high - low) < precision) {
        return low;
    } else if (maxLineWidth > targetWidth) {
        return getSingleLineTextSize(text, paint, targetWidth, low, mid, precision, metrics);
    } else if (maxLineWidth < targetWidth) {
        return getSingleLineTextSize(text, paint, targetWidth, mid, high, precision, metrics);
    } else {
        return mid;
    }
}

/**
 * Returns true if {@code value} is 'close' to it's closest decimal value. Close is currently
 * defined as it's difference being < 0.01.
 */
private static boolean isClose(float value, float targetValue) {
    return Math.abs(value - targetValue) < 0.01f;
}

/**
 * Interpolate between {@code startValue} and {@code endValue}, using {@code progress}.
 */
private static float interpolate(float startValue, float endValue, float progress) {
    return startValue + ((endValue - startValue) * progress);
}

}

我终于解决了这个问题。如果其他人卡在这里,这就是我修复它的方式。无需将您的布局更改为 Recyclerview 和所有!以下是步骤。

1) 将 CoordinatorLayout 的 fitsSystemWindows 设置为 false。

android:fitsSystemWindows="false"

2) 现在,我的状态栏变成了白色。要解决此问题,请将以下行添加到 MainActivity.java

中的 onCreate 方法
Window window = getWindow();

// clear FLAG_TRANSLUCENT_STATUS flag:
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

// add FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag to the window
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);

// finally change the color
window.setStatusBarColor(getResources().getColor(R.color.colorPrimaryDark));

瞧!现在一切正常。

这是我的 activity_main.xml 内容,以防遗漏。

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:ads="http://schemas.android.com/apk/res-auto"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="false"
    tools:context=".MainActivity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay"
        app:contentScrim="@color/colorPrimaryDark"
        app:layout_scrollFlags="scroll|enterAlways">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            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/tabs"
            android:layout_width="match_parent" android:layout_height="wrap_content"
            app:tabIndicatorColor="@android:color/white"
            app:tabIndicatorHeight="3dp"
            android:textStyle="bold"/>

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

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