/* * Copyright (c) 2015 Zhang Hai <Dreaming.in.Code.ZH@Gmail.com> * All Rights Reserved. */ package me.zhanghai.android.douya.ui; import android.annotation.TargetApi; import android.content.Context; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; import android.support.annotation.NonNull; import android.support.v4.graphics.drawable.DrawableCompat; import android.support.v4.view.GravityCompat; import android.support.v4.view.ViewCompat; import android.support.v7.widget.TintTypedArray; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; import android.widget.FrameLayout; public class ForegroundHelper { //com.android.internal.R.attr.foregroundInsidePadding private static final int COM_ANDROID_INTERNAL_R_ATTR_FOREGROUND_INSIDE_PADDING = -300063; private static final int[] STYLEABLE = new int[] { android.R.attr.foreground, android.R.attr.foregroundGravity, COM_ANDROID_INTERNAL_R_ATTR_FOREGROUND_INSIDE_PADDING }; private static final int STYLEABLE_ANDROID_FOREGROUND = 0; private static final int STYLEABLE_ANDROID_FOREGROUND_GRAVITY = 1; private static final int STYLEABLE_ANDROID_FOREGROUND_INSIDE_PADDING = 2; private Delegate mDelegate; private boolean mHasFrameworkForeground; private Drawable mForeground; private final Rect mSelfBounds = new Rect(); private final Rect mOverlayBounds = new Rect(); private int mForegroundGravity = Gravity.FILL; private boolean mForegroundInPadding = true; private boolean mForegroundBoundsChanged = false; public ForegroundHelper(Delegate delegate) { mDelegate = delegate; } @SuppressWarnings("RestrictedApi") public void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { // @see View#View(android.content.Context, android.util.AttributeSet, int, int) mHasFrameworkForeground = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.M) || mDelegate.getOwner() instanceof FrameLayout; if (mHasFrameworkForeground) { return; } TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, STYLEABLE, defStyleAttr, defStyleRes); mForegroundGravity = a.getInt(STYLEABLE_ANDROID_FOREGROUND_GRAVITY, mForegroundGravity); Drawable foreground = a.getDrawable(STYLEABLE_ANDROID_FOREGROUND); if (foreground != null) { setForeground(foreground); } mForegroundInPadding = a.getBoolean(STYLEABLE_ANDROID_FOREGROUND_INSIDE_PADDING, true); a.recycle(); } public int getForegroundGravity() { if (mHasFrameworkForeground) { return mDelegate.superGetForegroundGravity(); } return mForegroundGravity; } public void setForegroundGravity(int foregroundGravity) { if (mHasFrameworkForeground) { mDelegate.superSetForegroundGravity(foregroundGravity); return; } if (mForegroundGravity != foregroundGravity) { if ((foregroundGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) { foregroundGravity |= Gravity.START; } if ((foregroundGravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) { foregroundGravity |= Gravity.TOP; } mForegroundGravity = foregroundGravity; mDelegate.getOwner().requestLayout(); } } public void setVisibility(int visibility) { mDelegate.superSetVisibility(visibility); if (mHasFrameworkForeground) { return; } if (mForeground != null) { mForeground.setVisible(visibility == View.VISIBLE, false); } } public boolean verifyDrawable(Drawable who) { if (mHasFrameworkForeground) { return mDelegate.superVerifyDrawable(who); } return mDelegate.superVerifyDrawable(who) || (who == mForeground); } public void jumpDrawablesToCurrentState() { mDelegate.superJumpDrawablesToCurrentState(); if (mHasFrameworkForeground) { return; } if (mForeground != null) { mForeground.jumpToCurrentState(); } } public void drawableStateChanged() { mDelegate.superDrawableStateChanged(); if (mHasFrameworkForeground) { return; } if (mForeground != null && mForeground.isStateful()) { mForeground.setState(mDelegate.getOwner().getDrawableState()); } } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public void drawableHotspotChanged(float x, float y) { mDelegate.superDrawableHotspotChanged(x, y); if (mHasFrameworkForeground) { return; } if (mForeground != null) { mForeground.setHotspot(x, y); } } public void setForeground(Drawable foreground) { if (mHasFrameworkForeground) { mDelegate.superSetForeground(foreground); return; } if (mForeground != foreground) { View owner = mDelegate.getOwner(); if (mForeground != null) { mForeground.setCallback(null); owner.unscheduleDrawable(mForeground); } mForeground = foreground; if (foreground != null) { owner.setWillNotDraw(false); foreground.setCallback(owner); DrawableCompat.setLayoutDirection(foreground, ViewCompat.getLayoutDirection(owner)); if (foreground.isStateful()) { foreground.setState(owner.getDrawableState()); } } else { owner.setWillNotDraw(true); } owner.requestLayout(); owner.invalidate(); } } public Drawable getForeground() { if (mHasFrameworkForeground) { return mDelegate.superGetForeground(); } return mForeground; } public void onLayout(boolean changed, int left, int top, int right, int bottom) { mDelegate.superOnLayout(changed, left, top, right, bottom); if (mHasFrameworkForeground) { return; } mForegroundBoundsChanged = true; } public void onSizeChanged(int w, int h, int oldw, int oldh) { mDelegate.superOnSizeChanged(w, h, oldw, oldh); if (mHasFrameworkForeground) { return; } mForegroundBoundsChanged = true; } public void draw(@NonNull Canvas canvas) { mDelegate.superDraw(canvas); if (mHasFrameworkForeground) { return; } if (mForeground != null) { final Drawable foreground = mForeground; if (mForegroundBoundsChanged) { View owner = mDelegate.getOwner(); mForegroundBoundsChanged = false; Rect selfBounds = mSelfBounds; Rect overlayBounds = mOverlayBounds; int w = owner.getRight() - owner.getLeft(); int h = owner.getBottom() - owner.getTop(); if (mForegroundInPadding) { selfBounds.set(0, 0, w, h); } else { selfBounds.set(owner.getPaddingLeft(), owner.getPaddingTop(), w - owner.getPaddingRight(), h - owner.getPaddingBottom()); } int layoutDirection = ViewCompat.getLayoutDirection(owner); GravityCompat.apply(mForegroundGravity, foreground.getIntrinsicWidth(), foreground.getIntrinsicHeight(), selfBounds, overlayBounds, layoutDirection); foreground.setBounds(overlayBounds); } foreground.draw(canvas); } } public interface Delegate { View getOwner(); int superGetForegroundGravity(); void superSetForegroundGravity(int foregroundGravity); void superSetVisibility(int visibility); boolean superVerifyDrawable(Drawable who); void superJumpDrawablesToCurrentState(); void superDrawableStateChanged(); void superDrawableHotspotChanged(float x, float y); void superSetForeground(Drawable foreground); Drawable superGetForeground(); void superOnLayout(boolean changed, int left, int top, int right, int bottom); void superOnSizeChanged(int w, int h, int oldw, int oldh); void superDraw(@NonNull Canvas canvas); } }