bugfix: add api 19 support for QMUICollapsingTopBarLayout.#40

This commit is contained in:
cgspine
2017-09-20 16:16:54 +08:00
parent 8421af5028
commit 7a9f7be867
6 changed files with 177 additions and 39 deletions
@@ -4,6 +4,7 @@ import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.graphics.Rect;
import android.os.Build;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CoordinatorLayout;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.WindowInsetsCompat;
@@ -13,7 +14,6 @@ import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.qmuiteam.qmui.widget.IWindowInsetLayout;
import com.qmuiteam.qmui.widget.QMUIWindowInsetLayout;
import java.lang.ref.WeakReference;
@@ -68,8 +68,15 @@ public class QMUIWindowInsetHelper {
Rect childInsets = new Rect(insets);
computeInsetsWithGravity(child, childInsets);
child.setPadding(childInsets.left, childInsets.top, childInsets.right, childInsets.bottom);
if (!isHandleContainer(child)) {
child.setPadding(childInsets.left, childInsets.top, childInsets.right, childInsets.bottom);
} else {
if (child instanceof IWindowInsetLayout) {
((IWindowInsetLayout) child).applySystemWindowInsets19(childInsets);
} else {
defaultApplySystemWindowInsets19((ViewGroup) child, childInsets);
}
}
consumed = true;
}
@@ -114,10 +121,13 @@ public class QMUIWindowInsetHelper {
@SuppressWarnings("deprecation")
@TargetApi(19)
private boolean jumpDispatch(View child) {
return !child.getFitsSystemWindows() &&
!(child instanceof QMUIWindowInsetLayout) &&
!(child instanceof CoordinatorLayout);
public static boolean jumpDispatch(View child) {
return !child.getFitsSystemWindows() && !isHandleContainer(child);
}
public static boolean isHandleContainer(View child) {
return child instanceof IWindowInsetLayout ||
child instanceof CoordinatorLayout;
}
@SuppressLint("RtlHardcoded")
@@ -0,0 +1,76 @@
package com.qmuiteam.qmui.widget;
import android.content.Context;
import android.graphics.Rect;
import android.support.design.widget.AppBarLayout;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.WindowInsetsCompat;
import android.util.AttributeSet;
import android.view.View;
import com.qmuiteam.qmui.util.QMUIWindowInsetHelper;
import java.lang.reflect.Field;
/**
* add support for API 19 when use with {@link android.support.design.widget.CoordinatorLayout}
* and {@link QMUICollapsingTopBarLayout}
*
* notice: we use reflection to change the field value in AppBarLayout. use it only if you need to
* set fitSystemWindows for StatusBar
*
* @author cginechen
* @date 2017-09-20
*/
public class QMUIAppBarLayout extends AppBarLayout implements IWindowInsetLayout {
public QMUIAppBarLayout(Context context) {
super(context);
}
public QMUIAppBarLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean applySystemWindowInsets19(final Rect insets) {
if (ViewCompat.getFitsSystemWindows(this)) {
try {
Field field = AppBarLayout.class.getDeclaredField("mLastInsets");
field.setAccessible(true);
field.set(this, new WindowInsetsCompat(null) {
@Override
public int getSystemWindowInsetTop() {
return insets.top;
}
});
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (QMUIWindowInsetHelper.jumpDispatch(child)) {
continue;
}
if (!QMUIWindowInsetHelper.isHandleContainer(child)) {
child.setPadding(insets.left, insets.top, insets.right, insets.bottom);
} else {
if (child instanceof IWindowInsetLayout) {
((IWindowInsetLayout) child).applySystemWindowInsets19(insets);
}
}
}
return true;
}
return false;
}
@Override
public boolean applySystemWindowInsets21(WindowInsetsCompat insets) {
return true;
}
}
@@ -26,6 +26,7 @@ import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.ColorInt;
import android.support.annotation.DrawableRes;
import android.support.annotation.IntDef;
@@ -68,7 +69,7 @@ import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
* @date 2017-09-02
*/
public class QMUICollapsingTopBarLayout extends FrameLayout {
public class QMUICollapsingTopBarLayout extends FrameLayout implements IWindowInsetLayout {
private static final int DEFAULT_SCRIM_ANIMATION_DURATION = 600;
@@ -100,6 +101,7 @@ public class QMUICollapsingTopBarLayout extends FrameLayout {
int mCurrentOffset;
WindowInsetsCompat mLastInsets;
Rect mLastInsetRect;
public QMUICollapsingTopBarLayout(Context context) {
this(context, null);
@@ -184,13 +186,21 @@ public class QMUICollapsingTopBarLayout extends FrameLayout {
ViewCompat.setOnApplyWindowInsetsListener(this,
new android.support.v4.view.OnApplyWindowInsetsListener() {
@Override
public WindowInsetsCompat onApplyWindowInsets(View v,
WindowInsetsCompat insets) {
return onWindowInsetChanged(insets);
public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
return setWindowInsets(insets);
}
});
}
private WindowInsetsCompat setWindowInsets(WindowInsetsCompat insets) {
if (Build.VERSION.SDK_INT >= 21) {
if (applySystemWindowInsets21(insets)) {
return insets.consumeSystemWindowInsets();
}
}
return insets;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -222,24 +232,6 @@ public class QMUICollapsingTopBarLayout extends FrameLayout {
super.onDetachedFromWindow();
}
WindowInsetsCompat onWindowInsetChanged(final WindowInsetsCompat insets) {
WindowInsetsCompat newInsets = null;
if (ViewCompat.getFitsSystemWindows(this)) {
// If we're set to fit system windows, keep the insets
newInsets = insets;
}
// If our insets have changed, keep them and invalidate the scroll ranges...
if (!QMUILangHelper.objectEquals(mLastInsets, newInsets)) {
mLastInsets = newInsets;
requestLayout();
}
// Consume the insets. This is done so that child views with fitSystemWindows=true do not
// get the default padding functionality from View
return insets.consumeSystemWindowInsets();
}
@Override
public void draw(Canvas canvas) {
@@ -260,7 +252,7 @@ public class QMUICollapsingTopBarLayout extends FrameLayout {
// Now draw the status bar scrim
if (mStatusBarScrim != null && mScrimAlpha > 0) {
final int topInset = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0;
final int topInset = getWindowInsetTop();
if (topInset > 0) {
mStatusBarScrim.setBounds(0, -mCurrentOffset, getWidth(),
topInset - mCurrentOffset);
@@ -270,6 +262,16 @@ public class QMUICollapsingTopBarLayout extends FrameLayout {
}
}
private int getWindowInsetTop() {
if (mLastInsets != null) {
return mLastInsets.getSystemWindowInsetTop();
}
if (mLastInsetRect != null) {
return mLastInsetRect.top;
}
return 0;
}
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
// This is a little weird. Our scrim needs to be behind the Toolbar (if it is present),
@@ -354,9 +356,9 @@ public class QMUICollapsingTopBarLayout extends FrameLayout {
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (mLastInsets != null) {
if (mLastInsets != null || mLastInsetRect != null) {
// Shift down any views which are not set to fit system windows
final int insetTop = mLastInsets.getSystemWindowInsetTop();
final int insetTop = getWindowInsetTop();
for (int i = 0, z = getChildCount(); i < z; i++) {
final View child = getChildAt(i);
if (ViewCompat.getFitsSystemWindows(child)) {
@@ -511,7 +513,6 @@ public class QMUICollapsingTopBarLayout extends FrameLayout {
}
/**
*
* @param scrimUpdateListener 为 null 则是 removeUpdateListener
*/
public void setScrimUpdateListener(ValueAnimator.AnimatorUpdateListener scrimUpdateListener) {
@@ -523,7 +524,7 @@ public class QMUICollapsingTopBarLayout extends FrameLayout {
mScrimAnimator.removeUpdateListener(mScrimUpdateListener);
}
mScrimUpdateListener = scrimUpdateListener;
if(mScrimUpdateListener != null){
if (mScrimUpdateListener != null) {
mScrimAnimator.addUpdateListener(mScrimUpdateListener);
}
}
@@ -971,7 +972,7 @@ public class QMUICollapsingTopBarLayout extends FrameLayout {
}
// Otherwise we'll use the default computed value
final int insetTop = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0;
final int insetTop = getWindowInsetTop();
final int minHeight = ViewCompat.getMinimumHeight(this);
if (minHeight > 0) {
@@ -1020,6 +1021,48 @@ public class QMUICollapsingTopBarLayout extends FrameLayout {
return new QMUICollapsingTopBarLayout.LayoutParams(p);
}
@Override
@SuppressWarnings("deprecation")
protected boolean fitSystemWindows(Rect insets) {
return applySystemWindowInsets19(insets);
}
@Override
public boolean applySystemWindowInsets19(Rect insets) {
Rect newInsets = null;
if (ViewCompat.getFitsSystemWindows(this)) {
// If we're set to fit system windows, keep the insets
newInsets = insets;
}
// If our insets have changed, keep them and invalidate the scroll ranges...
if (!QMUILangHelper.objectEquals(mLastInsets, newInsets)) {
mLastInsetRect = newInsets;
requestLayout();
}
// Consume the insets. This is done so that child views with fitSystemWindows=true do not
// get the default padding functionality from View
return true;
}
@Override
public boolean applySystemWindowInsets21(WindowInsetsCompat insets) {
WindowInsetsCompat newInsets = null;
if (ViewCompat.getFitsSystemWindows(this)) {
// If we're set to fit system windows, keep the insets
newInsets = insets;
}
// If our insets have changed, keep them and invalidate the scroll ranges...
if (!QMUILangHelper.objectEquals(mLastInsets, newInsets)) {
mLastInsets = newInsets;
requestLayout();
}
return true;
}
public static class LayoutParams extends FrameLayout.LayoutParams {
private static final float DEFAULT_PARALLAX_MULTIPLIER = 0.5f;
@@ -1161,7 +1204,7 @@ public class QMUICollapsingTopBarLayout extends FrameLayout {
public void onOffsetChanged(AppBarLayout layout, int verticalOffset) {
mCurrentOffset = verticalOffset;
final int insetTop = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0;
final int insetTop = getWindowInsetTop();
for (int i = 0, z = getChildCount(); i < z; i++) {
final View child = getChildAt(i);
@@ -3,6 +3,7 @@ package com.qmuiteam.qmui.widget;
import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.WindowInsetsCompat;
import android.util.AttributeSet;
import android.widget.FrameLayout;
@@ -56,4 +57,11 @@ public class QMUIWindowInsetLayout extends FrameLayout implements IWindowInsetLa
return mQMUIWindowInsetHelper.defaultApplySystemWindowInsets21(this, insets);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (ViewCompat.getFitsSystemWindows(this)) {
ViewCompat.requestApplyInsets(this);
}
}
}
@@ -5,7 +5,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
<com.qmuiteam.qmui.widget.QMUIAppBarLayout
android:layout_width="match_parent"
android:layout_height="256dp"
android:fitsSystemWindows="true">
@@ -40,7 +40,7 @@
app:qmui_topbar_bg_color="@color/qmui_config_color_transparent"
app:qmui_topbar_need_separator="false"/>
</com.qmuiteam.qmui.widget.QMUICollapsingTopBarLayout>
</android.support.design.widget.AppBarLayout>
</com.qmuiteam.qmui.widget.QMUIAppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
@@ -9,7 +9,8 @@
android:background="?attr/app_content_bg_color"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="@dimen/home_tab_height"/>
android:layout_marginBottom="@dimen/home_tab_height"
android:fitsSystemWindows="true"/>
<com.qmuiteam.qmui.widget.QMUITabSegment
android:id="@+id/tabs"
android:layout_gravity="bottom"