package com.example.administrator.searchpicturetool.widght.fitsystemwindowlayout; import android.content.Context; import android.content.res.Configuration; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.util.TypedValue; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.view.WindowInsets; import android.widget.FrameLayout; import com.example.administrator.searchpicturetool.R; import java.util.ArrayList; /** * Created by zhuchenxi on 15/11/7. */ public class FitSystemWindowsFrameLayout extends FrameLayout{ public static final int HORIZONTAL = 0; public static final int VERTICAL = 1; private static int STATUSBAR_HEIGHT; private static int NAVIGATIONBAR_HEIGHT; private final ArrayList<View> mMatchParentChildren = new ArrayList<View>(1); private boolean mPaddingStatusBar; private boolean mPaddingNavigationBar; private int mStatusBarColor = 0; private int mStatusBarHeight = 0; private int mNavigationBarHeight = 0; private int mScreenOrientation = VERTICAL; private boolean isInputMethod = false; private int mInputMethodHeight = 0; private Paint mStatusBarPaint; public FitSystemWindowsFrameLayout(Context context) { super(context); init(); } public FitSystemWindowsFrameLayout(Context context, AttributeSet attrs) { super(context, attrs); initAttrs(attrs); init(); } public FitSystemWindowsFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initAttrs(attrs); init(); } protected void initAttrs(AttributeSet attrs) { TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.fit_system_windows); try { int colorAttr; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { colorAttr = android.R.attr.colorPrimaryDark; } else { colorAttr = getContext().getResources().getIdentifier("colorPrimaryDark", "attr", getContext().getPackageName()); } TypedValue outValue = new TypedValue(); getContext().getTheme().resolveAttribute(colorAttr, outValue, true); if (outValue.resourceId!=0)mStatusBarColor = getResources().getColor(outValue.resourceId); mStatusBarColor = a.getColor(R.styleable.fit_system_windows_status_color,mStatusBarColor); mPaddingStatusBar = a.getBoolean(R.styleable.fit_system_windows_padding_status, true); mPaddingNavigationBar = a.getBoolean(R.styleable.fit_system_windows_padding_navigation, false); Utils.log("initAttrs"+" mStatusBarColor"+mStatusBarColor+" mPaddingStatusBar:"+mPaddingStatusBar+" mPaddingStatusBar:"+mPaddingStatusBar); } finally { a.recycle(); } } private void init(){ int statusBarHeight = 0; int navigationBarHeight = 0; setWillNotDraw(false); setFitsSystemWindows(false);//不然4.4就会绘制默认的statusBar遮罩 mScreenOrientation = (getResources().getConfiguration().orientation== Configuration.ORIENTATION_PORTRAIT)?VERTICAL:HORIZONTAL; STATUSBAR_HEIGHT = Utils.getStatusBarHeight(getContext()); NAVIGATIONBAR_HEIGHT = Utils.getNavigationBarHeight(getContext()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT ){ statusBarHeight = STATUSBAR_HEIGHT; if (Utils.hasSoftKeys(getContext())) navigationBarHeight = NAVIGATIONBAR_HEIGHT; } mStatusBarHeight = statusBarHeight; mNavigationBarHeight = navigationBarHeight; mStatusBarPaint = new Paint(); mStatusBarPaint.setColor(mStatusBarColor); Utils.log("init"+" mStatusBarHeight:"+mStatusBarHeight+" mNavigationBarHeight:"+mNavigationBarHeight); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mPaddingStatusBar){ canvas.drawRect(0,0,getRight(),mStatusBarHeight,mStatusBarPaint); } } public void setStatusBarColor(int color){ mStatusBarColor = color; mStatusBarPaint.setColor(mStatusBarColor); invalidate(); } @Override protected boolean fitSystemWindows(Rect insets) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { Utils.log("fitSystemWindows" +" Left:"+insets.left +" Top:"+insets.top +" Right:"+insets.right +" Bottom:"+insets.bottom); if(insets.bottom > NAVIGATIONBAR_HEIGHT){ mInputMethodHeight = insets.bottom; isInputMethod = true; }else { mInputMethodHeight = 0; isInputMethod = false; } insets.set(0,0,0,0); } return super.fitSystemWindows(insets); } @Override public final WindowInsets onApplyWindowInsets(WindowInsets insets) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { Utils.log("onApplyWindowInsets" +" Left:"+insets.getSystemWindowInsetLeft() +" Top:"+insets.getSystemWindowInsetTop() +" Right:"+insets.getSystemWindowInsetRight() +" Bottom:"+insets.getSystemWindowInsetBottom()); if(insets.getSystemWindowInsetBottom() > NAVIGATIONBAR_HEIGHT){ mInputMethodHeight = insets.getSystemWindowInsetBottom(); isInputMethod = true; }else { mInputMethodHeight = 0; isInputMethod = false; } insets.replaceSystemWindowInsets(0,0,0,0);//使默认的padding效果失效,因为我完全自己处理了。 return insets;//我重写了自己的Padding规则,所以我可以无视对insets的处理。 } else { return insets; } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int count = getChildCount(); final boolean measureMatchParentChildren = MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; mMatchParentChildren.clear(); int maxHeight = 0; int maxWidth = 0; int childState = 0; for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.getVisibility() != GONE) { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); //将父布局的属性转换到子View上。 if (lp.isPaddingNavigation()&&mScreenOrientation==VERTICAL) Utils.paddingToNavigationBar(child); if (!lp.hasSetMarginStatus())lp.setMarginStatus(mPaddingStatusBar); if (!lp.hasSetMarginNavigation())lp.setMarginNavigation(mPaddingNavigationBar); int usedHeight = (lp.isMarginStatus()?mStatusBarHeight:0)+(lp.isMarginNavigation()&&mScreenOrientation == VERTICAL?mNavigationBarHeight:0); int usedWidth = (lp.isMarginNavigation()&&mScreenOrientation == HORIZONTAL?mNavigationBarHeight:0); measureChildWithMargins(child, widthMeasureSpec, usedWidth, heightMeasureSpec, usedHeight); maxWidth = Math.max(maxWidth, child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); maxHeight = Math.max(maxHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); childState = combineMeasuredStates(childState, child.getMeasuredState()); if (measureMatchParentChildren) { if (lp.width == LayoutParams.MATCH_PARENT || lp.height == LayoutParams.MATCH_PARENT) { mMatchParentChildren.add(child); } } Utils.log("measure "+ child.getClass().getSimpleName()+" isMarginStatus:"+(lp.isMarginStatus()?"true":"false")+" isMarginNavigation:"+(lp.isMarginNavigation()?"true":"false")); } } // Account for padding too // maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground(); // maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground(); // Check against our minimum height and width maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); // Check against our foreground's minimum height and width final Drawable drawable = getForeground(); if (drawable != null) { maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); } setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT)); count = mMatchParentChildren.size(); for (int i = 0; i < count; i++) { final View child = mMatchParentChildren.get(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); final int childWidthMeasureSpec; if (lp.width == LayoutParams.MATCH_PARENT) { final int width = Math.max(0, getMeasuredWidth() // - getPaddingLeftWithForeground() - getPaddingRightWithForeground() - lp.leftMargin - lp.rightMargin - getNavigationHorizontalValue(lp)); childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( width, MeasureSpec.EXACTLY); } else { childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, // getPaddingLeftWithForeground() + getPaddingRightWithForeground() + lp.leftMargin + lp.rightMargin + getNavigationHorizontalValue(lp), lp.width); } final int childHeightMeasureSpec; if (lp.height == LayoutParams.MATCH_PARENT) { final int height = Math.max(0, getMeasuredHeight() // - getPaddingTopWithForeground() - getPaddingBottomWithForeground() - lp.topMargin - lp.bottomMargin - getStatusValue(lp) - getNavigationVerticalValue(lp)); childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( height, MeasureSpec.EXACTLY); } else { childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, // getPaddingTopWithForeground() + getPaddingBottomWithForeground() + lp.topMargin + lp.bottomMargin + getStatusValue(lp) + getNavigationVerticalValue(lp),//当设置时增加额外的Padding lp.height); } child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } } //返回底部应有padding private int getNavigationVerticalValue(LayoutParams lp){ if (isInputMethod)return mInputMethodHeight; return (mScreenOrientation == VERTICAL)?(lp.mMarginNavigation?mNavigationBarHeight:0):0; } //返回右边应有padding private int getNavigationHorizontalValue(LayoutParams lp){ return (mScreenOrientation == HORIZONTAL)?(lp.mMarginNavigation?mNavigationBarHeight:0):0; } //返回顶部应有padding private int getStatusValue(LayoutParams lp){ return (lp.mMarginStatus?mStatusBarHeight:0); } /** * {@inheritDoc} */ @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { layoutChildren(left, top, right, bottom, false /* no force left gravity */); } void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) { final int count = getChildCount(); final int parentLeft = 0; final int parentRight = right - left ; final int parentTop = 0; final int parentBottom = bottom - top ; for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.getVisibility() != GONE) { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); final int width = child.getMeasuredWidth(); final int height = child.getMeasuredHeight(); int childLeft; int childTop; int gravity = lp.gravity; if (gravity == -1) { gravity = Gravity.TOP|Gravity.START; } final int layoutDirection = ViewCompat.getLayoutDirection(this); final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity.CENTER_HORIZONTAL: childLeft = parentLeft + (parentRight - parentLeft - width) / 2 + lp.leftMargin - lp.rightMargin; break; case Gravity.RIGHT: if (!forceLeftGravity) { childLeft = parentRight - width - lp.rightMargin - getNavigationHorizontalValue(lp);//减去横屏时右侧的导航栏 break; } case Gravity.LEFT: default: childLeft = parentLeft + lp.leftMargin; } switch (verticalGravity) { case Gravity.TOP: childTop = parentTop + lp.topMargin + getStatusValue(lp); Utils.log(child.getClass().getSimpleName()+" topMargin:"+lp.topMargin+" getStatusValue:"+getStatusValue(lp)); break; case Gravity.CENTER_VERTICAL: childTop = parentTop + (parentBottom - parentTop - height) / 2 + lp.topMargin - lp.bottomMargin; break; case Gravity.BOTTOM: childTop = parentBottom - height - lp.bottomMargin - getNavigationVerticalValue(lp);//减去竖屏时的导航栏 Utils.log(child.getClass().getSimpleName()+" bottomMargin:"+lp.bottomMargin+" getNavigationVerticalValue:"+getNavigationVerticalValue(lp)); break; default: childTop = parentTop + lp.topMargin + getStatusValue(lp); } child.layout(childLeft, childTop, childLeft + width, childTop + height); } } } @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(getContext(), attrs); } @Override protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { if (p instanceof LayoutParams) { return new LayoutParams((LayoutParams) p); } else if (p instanceof MarginLayoutParams) { return new LayoutParams((MarginLayoutParams) p); } return new LayoutParams(p); } @Override protected FrameLayout.LayoutParams generateDefaultLayoutParams() { return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); } @Override protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { return p instanceof LayoutParams && super.checkLayoutParams(p); } public static class LayoutParams extends FrameLayout.LayoutParams { private boolean mPaddingNavigation = false; private boolean mMarginStatus = false; private boolean mHasSetMarginStatus = false; private boolean mMarginNavigation = false; private boolean mHasSetMarginNavigation = false; public LayoutParams(Context context, AttributeSet attrs) { super(context, attrs); final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.fit_system_windows); mHasSetMarginStatus = a.hasValue(R.styleable.fit_system_windows_margin_status); mHasSetMarginNavigation = a.hasValue(R.styleable.fit_system_windows_margin_navigation); this.mMarginStatus = a.getBoolean( R.styleable.fit_system_windows_margin_status, false); this.mMarginNavigation = a.getBoolean( R.styleable.fit_system_windows_margin_navigation, false); this.mPaddingNavigation = a.getBoolean( R.styleable.fit_system_windows_padding_navigation, false); a.recycle(); Utils.log("LayoutParams " +" mHasSetMarginStatus:"+mHasSetMarginStatus +" mMarginStatus:"+mMarginStatus +" mHasSetMarginNavigation:"+mHasSetMarginNavigation +" mMarginNavigation:"+mMarginNavigation); } public LayoutParams(int width, int height) { super(width, height); } public LayoutParams(int width, int height, int gravity) { super(width, height, gravity); } public LayoutParams(ViewGroup.LayoutParams source) { super(source); } public LayoutParams(MarginLayoutParams source) { super(source); } public boolean isMarginStatus() { return mMarginStatus; } public void setMarginStatus(boolean mMarginStatus) { this.mMarginStatus = mMarginStatus; } public boolean isMarginNavigation() { return mMarginNavigation; } public void setMarginNavigation(boolean mMarginNavigation) { this.mMarginNavigation = mMarginNavigation; } public boolean isPaddingNavigation() { return mPaddingNavigation; } public void setPaddingNavigation(boolean mPaddingNavigation) { this.mPaddingNavigation = mPaddingNavigation; } public boolean hasSetMarginStatus() { return mHasSetMarginStatus; } public boolean hasSetMarginNavigation() { return mHasSetMarginNavigation; } } }